fix(dashboard): Add missing CollapsibleCard shared component

This component was imported in DashboardView.vue but never committed.
CollapsibleCard provides an expandable/collapsible card UI pattern used
for Trezorerie, Cash Flow, Clienți, and Furnizori dashboard cards.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-20 20:47:14 +00:00
parent 3039138e66
commit 69683b2d65

View File

@@ -0,0 +1,242 @@
<template>
<div class="collapsible-card" :class="{ 'collapsible-card--expanded': isExpanded }">
<!-- Collapsed Header (always visible) -->
<div class="collapsible-card__header" @click="toggleExpanded" role="button" tabindex="0" @keydown.enter="toggleExpanded">
<div class="collapsible-card__content">
<i v-if="icon" :class="icon" class="collapsible-card__icon"></i>
<span class="collapsible-card__label">{{ label }}</span>
<span class="collapsible-card__value" :class="valueClass">{{ formattedValue }}</span>
</div>
<i class="pi pi-chevron-down collapsible-card__chevron"
:class="{ 'collapsible-card__chevron--expanded': isExpanded }"></i>
</div>
<!-- Expandable Content -->
<div v-show="isExpanded" class="collapsible-card__body">
<slot></slot>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const props = defineProps({
/**
* Label displayed in the collapsed header
*/
label: {
type: String,
required: true
},
/**
* Main value displayed in the collapsed header
* Can be a number or string
*/
value: {
type: [Number, String],
default: null
},
/**
* PrimeVue icon class (e.g., 'pi pi-wallet')
*/
icon: {
type: String,
default: ''
},
/**
* CSS class to apply to the value (e.g., 'positive', 'negative', 'neutral')
*/
valueClass: {
type: String,
default: ''
},
/**
* Whether the card is expanded by default
*/
defaultExpanded: {
type: Boolean,
default: false
},
/**
* Format the value as currency (RON)
*/
formatCurrency: {
type: Boolean,
default: true
}
})
// State
const isExpanded = ref(props.defaultExpanded)
// Computed: Format the value
const formattedValue = computed(() => {
if (props.value === null || props.value === undefined) return '-'
if (props.formatCurrency && typeof props.value === 'number') {
return new Intl.NumberFormat('ro-RO', {
style: 'currency',
currency: 'RON',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(props.value)
}
return String(props.value)
})
// Methods
const toggleExpanded = () => {
isExpanded.value = !isExpanded.value
}
</script>
<style scoped>
/* Collapsible Card Container */
.collapsible-card {
background: var(--surface-card);
border: 1px solid var(--surface-border);
border-radius: var(--radius-md);
overflow: hidden;
transition: all var(--transition-fast);
}
/* Header - clickable area */
.collapsible-card__header {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-md) var(--space-lg);
cursor: pointer;
user-select: none;
transition: background-color var(--transition-fast);
}
.collapsible-card__header:hover {
background: var(--surface-hover);
}
.collapsible-card__header:focus {
outline: 2px solid var(--color-primary);
outline-offset: -2px;
}
/* Header content: icon + label + value */
.collapsible-card__content {
display: flex;
align-items: center;
gap: var(--space-sm);
flex: 1;
min-width: 0;
}
/* Icon */
.collapsible-card__icon {
font-size: var(--text-lg);
color: var(--color-primary);
flex-shrink: 0;
}
/* Label */
.collapsible-card__label {
font-size: var(--text-sm);
color: var(--text-color-secondary);
font-weight: var(--font-medium);
white-space: nowrap;
}
/* Value */
.collapsible-card__value {
font-size: var(--text-lg);
font-weight: var(--font-bold);
color: var(--text-color);
font-family: var(--font-mono);
margin-left: auto;
white-space: nowrap;
}
/* Value variants */
.collapsible-card__value.positive {
color: var(--green-600);
}
.collapsible-card__value.negative {
color: var(--red-600);
}
.collapsible-card__value.neutral {
color: var(--text-color-secondary);
}
/* Dark mode value colors */
[data-theme="dark"] .collapsible-card__value.positive {
color: var(--green-400);
}
[data-theme="dark"] .collapsible-card__value.negative {
color: var(--red-400);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) .collapsible-card__value.positive {
color: var(--green-400);
}
:root:not([data-theme]) .collapsible-card__value.negative {
color: var(--red-400);
}
}
/* Chevron */
.collapsible-card__chevron {
font-size: var(--text-lg);
color: var(--text-color-secondary);
transition: transform var(--transition-fast);
flex-shrink: 0;
margin-left: var(--space-md);
}
.collapsible-card__chevron--expanded {
transform: rotate(180deg);
}
/* Body - expandable content */
.collapsible-card__body {
border-top: 1px solid var(--surface-border);
}
/* When expanded, remove internal card borders/shadows */
.collapsible-card--expanded .collapsible-card__body :deep(.metric-card),
.collapsible-card--expanded .collapsible-card__body :deep(.treasury-dual-card),
.collapsible-card--expanded .collapsible-card__body :deep(.cash-flow-metric-card),
.collapsible-card--expanded .collapsible-card__body :deep(.clienti-balance-card),
.collapsible-card--expanded .collapsible-card__body :deep(.furnizori-balance-card) {
border: none;
border-radius: 0;
box-shadow: none;
}
/* Responsive */
@media (max-width: 768px) {
.collapsible-card__header {
padding: var(--space-sm) var(--space-md);
}
.collapsible-card__label {
font-size: var(--text-xs);
}
.collapsible-card__value {
font-size: var(--text-base);
}
.collapsible-card__icon {
font-size: var(--text-base);
}
.collapsible-card__chevron {
font-size: var(--text-base);
}
}
</style>