feat: Frontend CSS refactoring and test improvements
Frontend: - Refactored CSS architecture with new utility classes - Updated dashboard components styling - Improved responsive grid system - Enhanced typography and variables - Updated E2E and integration tests Added: - Claude Code slash commands for validation - SSH tunnel and start test scripts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -42,12 +42,18 @@
|
||||
</div>
|
||||
|
||||
<!-- Breakdown section -->
|
||||
<div class="breakdown-section" v-if="casaItems.length > 0 || bancaItems.length > 0">
|
||||
<div
|
||||
class="breakdown-section"
|
||||
v-if="casaItems.length > 0 || bancaItems.length > 0"
|
||||
>
|
||||
<!-- Casa Breakdown -->
|
||||
<div class="breakdown-group" v-if="casaItems.length > 0">
|
||||
<div class="breakdown-header" @click="toggleCasaExpanded">
|
||||
<div class="breakdown-header-left">
|
||||
<i class="pi pi-chevron-right breakdown-toggle" :class="{ expanded: isCasaExpanded }"></i>
|
||||
<i
|
||||
class="pi pi-chevron-right breakdown-toggle"
|
||||
:class="{ expanded: isCasaExpanded }"
|
||||
></i>
|
||||
<span class="breakdown-label">Casa</span>
|
||||
</div>
|
||||
<span class="breakdown-value">{{ formatCurrency(casaTotal) }}</span>
|
||||
@@ -55,12 +61,20 @@
|
||||
|
||||
<!-- Casa Sub-items -->
|
||||
<div v-show="isCasaExpanded" class="breakdown-subitems slide-down">
|
||||
<div v-for="(item, idx) in casaItems" :key="idx" class="breakdown-subitem">
|
||||
<div
|
||||
v-for="(item, idx) in casaItems"
|
||||
:key="idx"
|
||||
class="breakdown-subitem"
|
||||
>
|
||||
<span class="breakdown-sublabel">
|
||||
{{ item.nume || `Cont ${item.cont}` }}
|
||||
<span v-if="item.cont" class="breakdown-cont">({{ item.cont }})</span>
|
||||
<span v-if="item.cont" class="breakdown-cont"
|
||||
>({{ item.cont }})</span
|
||||
>
|
||||
</span>
|
||||
<span class="breakdown-subvalue">{{ formatCurrency(item.sold) }}</span>
|
||||
<span class="breakdown-subvalue">{{
|
||||
formatCurrency(item.sold)
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,7 +83,10 @@
|
||||
<div class="breakdown-group" v-if="bancaItems.length > 0">
|
||||
<div class="breakdown-header" @click="toggleBancaExpanded">
|
||||
<div class="breakdown-header-left">
|
||||
<i class="pi pi-chevron-right breakdown-toggle" :class="{ expanded: isBancaExpanded }"></i>
|
||||
<i
|
||||
class="pi pi-chevron-right breakdown-toggle"
|
||||
:class="{ expanded: isBancaExpanded }"
|
||||
></i>
|
||||
<span class="breakdown-label">Bancă</span>
|
||||
</div>
|
||||
<span class="breakdown-value">{{ formatCurrency(bancaTotal) }}</span>
|
||||
@@ -77,12 +94,20 @@
|
||||
|
||||
<!-- Bancă Sub-items -->
|
||||
<div v-show="isBancaExpanded" class="breakdown-subitems slide-down">
|
||||
<div v-for="(item, idx) in bancaItems" :key="idx" class="breakdown-subitem">
|
||||
<div
|
||||
v-for="(item, idx) in bancaItems"
|
||||
:key="idx"
|
||||
class="breakdown-subitem"
|
||||
>
|
||||
<span class="breakdown-sublabel">
|
||||
{{ item.nume || `Cont ${item.cont}` }}
|
||||
<span v-if="item.cont" class="breakdown-cont">({{ item.cont }})</span>
|
||||
<span v-if="item.cont" class="breakdown-cont"
|
||||
>({{ item.cont }})</span
|
||||
>
|
||||
</span>
|
||||
<span class="breakdown-subvalue">{{ formatCurrency(item.sold) }}</span>
|
||||
<span class="breakdown-subvalue">{{
|
||||
formatCurrency(item.sold)
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,474 +116,499 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
|
||||
import { Chart, registerables } from 'chart.js'
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
watch,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
nextTick,
|
||||
} from "vue";
|
||||
import { Chart, registerables } from "chart.js";
|
||||
|
||||
Chart.register(...registerables)
|
||||
Chart.register(...registerables);
|
||||
|
||||
const props = defineProps({
|
||||
casaTotal: {
|
||||
type: Number,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
bancaTotal: {
|
||||
type: Number,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
casaItems: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: () => [],
|
||||
},
|
||||
bancaItems: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: () => [],
|
||||
},
|
||||
casaSparklineData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: () => [],
|
||||
},
|
||||
bancaSparklineData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: () => [],
|
||||
},
|
||||
casaPreviousSparklineData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: () => [],
|
||||
},
|
||||
bancaPreviousSparklineData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: () => [],
|
||||
},
|
||||
sparklineLabels: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: () => [],
|
||||
},
|
||||
previousSparklineLabels: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: () => [],
|
||||
},
|
||||
trend: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
// Refs pentru 2 canvas-uri separate
|
||||
const casaCanvas = ref(null)
|
||||
const bancaCanvas = ref(null)
|
||||
let casaChartInstance = null
|
||||
let bancaChartInstance = null
|
||||
const isCasaExpanded = ref(false)
|
||||
const isBancaExpanded = ref(false)
|
||||
const casaCanvas = ref(null);
|
||||
const bancaCanvas = ref(null);
|
||||
let casaChartInstance = null;
|
||||
let bancaChartInstance = null;
|
||||
const isCasaExpanded = ref(false);
|
||||
const isBancaExpanded = ref(false);
|
||||
|
||||
// Toggle functions
|
||||
const toggleCasaExpanded = () => {
|
||||
isCasaExpanded.value = !isCasaExpanded.value
|
||||
}
|
||||
isCasaExpanded.value = !isCasaExpanded.value;
|
||||
};
|
||||
|
||||
const toggleBancaExpanded = () => {
|
||||
isBancaExpanded.value = !isBancaExpanded.value
|
||||
}
|
||||
isBancaExpanded.value = !isBancaExpanded.value;
|
||||
};
|
||||
|
||||
// Format currency
|
||||
const formatCurrency = (amount) => {
|
||||
if (!amount && amount !== 0) return '0 RON'
|
||||
return new Intl.NumberFormat('ro-RO', {
|
||||
style: 'currency',
|
||||
currency: 'RON',
|
||||
if (!amount && amount !== 0) return "0 RON";
|
||||
return new Intl.NumberFormat("ro-RO", {
|
||||
style: "currency",
|
||||
currency: "RON",
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0
|
||||
}).format(Math.abs(amount))
|
||||
}
|
||||
maximumFractionDigits: 0,
|
||||
}).format(Math.abs(amount));
|
||||
};
|
||||
|
||||
// Check if sparkline data exists
|
||||
const hasSparklineData = computed(() => {
|
||||
return props.casaSparklineData.length > 0 && props.bancaSparklineData.length > 0
|
||||
})
|
||||
return (
|
||||
props.casaSparklineData.length > 0 && props.bancaSparklineData.length > 0
|
||||
);
|
||||
});
|
||||
|
||||
// Initialize Casa chart
|
||||
const initializeCasaChart = async () => {
|
||||
if (!casaCanvas.value || props.casaSparklineData.length === 0) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroy existing chart
|
||||
if (casaChartInstance) {
|
||||
casaChartInstance.destroy()
|
||||
casaChartInstance = null
|
||||
casaChartInstance.destroy();
|
||||
casaChartInstance = null;
|
||||
}
|
||||
|
||||
await nextTick()
|
||||
await nextTick();
|
||||
|
||||
const ctx = casaCanvas.value.getContext('2d')
|
||||
const ctx = casaCanvas.value.getContext("2d");
|
||||
|
||||
// Generate labels
|
||||
const labels = props.sparklineLabels.length > 0
|
||||
? props.sparklineLabels
|
||||
: props.casaSparklineData.map((_, i) => `L${i + 1}`)
|
||||
const labels =
|
||||
props.sparklineLabels.length > 0
|
||||
? props.sparklineLabels
|
||||
: props.casaSparklineData.map((_, i) => `L${i + 1}`);
|
||||
|
||||
// Prepare datasets
|
||||
const datasets = [{
|
||||
label: 'Casa (curent)',
|
||||
data: props.casaSparklineData,
|
||||
borderColor: '#10b981',
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: '#10b981',
|
||||
pointHoverBorderColor: '#ffffff',
|
||||
pointHoverBorderWidth: 2
|
||||
}]
|
||||
const datasets = [
|
||||
{
|
||||
label: "Casa (curent)",
|
||||
data: props.casaSparklineData,
|
||||
borderColor: "#10b981",
|
||||
backgroundColor: "rgba(16, 185, 129, 0.1)",
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: "#10b981",
|
||||
pointHoverBorderColor: "#ffffff",
|
||||
pointHoverBorderWidth: 2,
|
||||
},
|
||||
];
|
||||
|
||||
// Add previous year dataset if available
|
||||
if (props.casaPreviousSparklineData && props.casaPreviousSparklineData.length > 0) {
|
||||
if (
|
||||
props.casaPreviousSparklineData &&
|
||||
props.casaPreviousSparklineData.length > 0
|
||||
) {
|
||||
datasets.push({
|
||||
label: 'Casa (anul precedent)',
|
||||
label: "Casa (anul precedent)",
|
||||
data: props.casaPreviousSparklineData,
|
||||
borderColor: 'rgba(16, 185, 129, 0.4)',
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.05)',
|
||||
borderColor: "rgba(16, 185, 129, 0.4)",
|
||||
backgroundColor: "rgba(16, 185, 129, 0.05)",
|
||||
borderWidth: 2,
|
||||
borderDash: [5, 5],
|
||||
fill: false,
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: 'rgba(16, 185, 129, 0.4)',
|
||||
pointHoverBorderColor: '#ffffff',
|
||||
pointHoverBorderWidth: 2
|
||||
})
|
||||
pointHoverBackgroundColor: "rgba(16, 185, 129, 0.4)",
|
||||
pointHoverBorderColor: "#ffffff",
|
||||
pointHoverBorderWidth: 2,
|
||||
});
|
||||
}
|
||||
|
||||
// Calculate limits including both datasets
|
||||
const allDataPoints = [...props.casaSparklineData]
|
||||
if (props.casaPreviousSparklineData && props.casaPreviousSparklineData.length > 0) {
|
||||
allDataPoints.push(...props.casaPreviousSparklineData)
|
||||
const allDataPoints = [...props.casaSparklineData];
|
||||
if (
|
||||
props.casaPreviousSparklineData &&
|
||||
props.casaPreviousSparklineData.length > 0
|
||||
) {
|
||||
allDataPoints.push(...props.casaPreviousSparklineData);
|
||||
}
|
||||
const dataMin = Math.min(...allDataPoints)
|
||||
const dataMax = Math.max(...allDataPoints)
|
||||
const dataRange = dataMax - dataMin
|
||||
const dataPadding = dataRange * 0.05
|
||||
const dataMin = Math.min(...allDataPoints);
|
||||
const dataMax = Math.max(...allDataPoints);
|
||||
const dataRange = dataMax - dataMin;
|
||||
const dataPadding = dataRange * 0.05;
|
||||
|
||||
casaChartInstance = new Chart(ctx, {
|
||||
type: 'line',
|
||||
type: "line",
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: datasets
|
||||
datasets: datasets,
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: 'index'
|
||||
mode: "index",
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: datasets.length > 1,
|
||||
position: 'top',
|
||||
align: 'end',
|
||||
position: "top",
|
||||
align: "end",
|
||||
labels: {
|
||||
boxWidth: 12,
|
||||
boxHeight: 12,
|
||||
padding: 8,
|
||||
font: {
|
||||
size: 10,
|
||||
family: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif'
|
||||
family: "Inter, -apple-system, BlinkMacSystemFont, sans-serif",
|
||||
},
|
||||
color: 'rgba(107, 114, 128, 0.9)',
|
||||
color: "rgba(107, 114, 128, 0.9)",
|
||||
usePointStyle: true,
|
||||
pointStyle: 'line'
|
||||
}
|
||||
pointStyle: "line",
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||||
titleColor: '#ffffff',
|
||||
bodyColor: '#ffffff',
|
||||
borderColor: 'rgba(255, 255, 255, 0.2)',
|
||||
backgroundColor: "rgba(0, 0, 0, 0.8)",
|
||||
titleColor: "#ffffff",
|
||||
bodyColor: "#ffffff",
|
||||
borderColor: "rgba(255, 255, 255, 0.2)",
|
||||
borderWidth: 1,
|
||||
cornerRadius: 6,
|
||||
displayColors: true,
|
||||
callbacks: {
|
||||
title: (context) => context[0].label || '',
|
||||
title: (context) => context[0].label || "",
|
||||
label: (context) => {
|
||||
const value = context.parsed.y
|
||||
const label = context.dataset.label || ''
|
||||
const formattedValue = new Intl.NumberFormat('ro-RO', {
|
||||
style: 'currency',
|
||||
currency: 'RON',
|
||||
const value = context.parsed.y;
|
||||
const label = context.dataset.label || "";
|
||||
const formattedValue = new Intl.NumberFormat("ro-RO", {
|
||||
style: "currency",
|
||||
currency: "RON",
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0
|
||||
}).format(value)
|
||||
return `${label}: ${formattedValue}`
|
||||
}
|
||||
}
|
||||
}
|
||||
maximumFractionDigits: 0,
|
||||
}).format(value);
|
||||
return `${label}: ${formattedValue}`;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: true,
|
||||
grid: {
|
||||
display: false,
|
||||
drawBorder: false
|
||||
drawBorder: false,
|
||||
},
|
||||
ticks: {
|
||||
color: 'rgba(107, 114, 128, 0.7)',
|
||||
color: "rgba(107, 114, 128, 0.7)",
|
||||
font: {
|
||||
size: 10,
|
||||
family: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif'
|
||||
family: "Inter, -apple-system, BlinkMacSystemFont, sans-serif",
|
||||
},
|
||||
maxRotation: 45,
|
||||
minRotation: 45,
|
||||
maxTicksLimit: 6
|
||||
maxTicksLimit: 6,
|
||||
},
|
||||
border: {
|
||||
display: false
|
||||
}
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
display: true,
|
||||
min: dataMin - dataPadding,
|
||||
max: dataMax + dataPadding,
|
||||
grid: {
|
||||
color: 'rgba(107, 114, 128, 0.1)',
|
||||
drawBorder: false
|
||||
color: "rgba(107, 114, 128, 0.1)",
|
||||
drawBorder: false,
|
||||
},
|
||||
ticks: {
|
||||
color: '#10b981',
|
||||
color: "#10b981",
|
||||
font: {
|
||||
size: 11,
|
||||
family: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif'
|
||||
family: "Inter, -apple-system, BlinkMacSystemFont, sans-serif",
|
||||
},
|
||||
maxTicksLimit: 3,
|
||||
callback: function(value) {
|
||||
callback: function (value) {
|
||||
if (value >= 1000000) {
|
||||
return (value / 1000000).toFixed(1) + 'M'
|
||||
return (value / 1000000).toFixed(1) + "M";
|
||||
} else if (value >= 1000) {
|
||||
return (value / 1000).toFixed(0) + 'k'
|
||||
return (value / 1000).toFixed(0) + "k";
|
||||
}
|
||||
return value.toFixed(0)
|
||||
}
|
||||
return value.toFixed(0);
|
||||
},
|
||||
},
|
||||
border: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Initialize Bancă chart
|
||||
const initializeBancaChart = async () => {
|
||||
if (!bancaCanvas.value || props.bancaSparklineData.length === 0) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroy existing chart
|
||||
if (bancaChartInstance) {
|
||||
bancaChartInstance.destroy()
|
||||
bancaChartInstance = null
|
||||
bancaChartInstance.destroy();
|
||||
bancaChartInstance = null;
|
||||
}
|
||||
|
||||
await nextTick()
|
||||
await nextTick();
|
||||
|
||||
const ctx = bancaCanvas.value.getContext('2d')
|
||||
const ctx = bancaCanvas.value.getContext("2d");
|
||||
|
||||
// Generate labels
|
||||
const labels = props.sparklineLabels.length > 0
|
||||
? props.sparklineLabels
|
||||
: props.bancaSparklineData.map((_, i) => `L${i + 1}`)
|
||||
const labels =
|
||||
props.sparklineLabels.length > 0
|
||||
? props.sparklineLabels
|
||||
: props.bancaSparklineData.map((_, i) => `L${i + 1}`);
|
||||
|
||||
// Prepare datasets
|
||||
const datasets = [{
|
||||
label: 'Bancă (curent)',
|
||||
data: props.bancaSparklineData,
|
||||
borderColor: '#3b82f6',
|
||||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: '#3b82f6',
|
||||
pointHoverBorderColor: '#ffffff',
|
||||
pointHoverBorderWidth: 2
|
||||
}]
|
||||
const datasets = [
|
||||
{
|
||||
label: "Bancă (curent)",
|
||||
data: props.bancaSparklineData,
|
||||
borderColor: "#3b82f6",
|
||||
backgroundColor: "rgba(59, 130, 246, 0.1)",
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: "#3b82f6",
|
||||
pointHoverBorderColor: "#ffffff",
|
||||
pointHoverBorderWidth: 2,
|
||||
},
|
||||
];
|
||||
|
||||
// Add previous year dataset if available
|
||||
if (props.bancaPreviousSparklineData && props.bancaPreviousSparklineData.length > 0) {
|
||||
if (
|
||||
props.bancaPreviousSparklineData &&
|
||||
props.bancaPreviousSparklineData.length > 0
|
||||
) {
|
||||
datasets.push({
|
||||
label: 'Bancă (anul precedent)',
|
||||
label: "Bancă (anul precedent)",
|
||||
data: props.bancaPreviousSparklineData,
|
||||
borderColor: 'rgba(59, 130, 246, 0.4)',
|
||||
backgroundColor: 'rgba(59, 130, 246, 0.05)',
|
||||
borderColor: "rgba(59, 130, 246, 0.4)",
|
||||
backgroundColor: "rgba(59, 130, 246, 0.05)",
|
||||
borderWidth: 2,
|
||||
borderDash: [5, 5],
|
||||
fill: false,
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: 'rgba(59, 130, 246, 0.4)',
|
||||
pointHoverBorderColor: '#ffffff',
|
||||
pointHoverBorderWidth: 2
|
||||
})
|
||||
pointHoverBackgroundColor: "rgba(59, 130, 246, 0.4)",
|
||||
pointHoverBorderColor: "#ffffff",
|
||||
pointHoverBorderWidth: 2,
|
||||
});
|
||||
}
|
||||
|
||||
// Calculate limits including both datasets
|
||||
const allDataPoints = [...props.bancaSparklineData]
|
||||
if (props.bancaPreviousSparklineData && props.bancaPreviousSparklineData.length > 0) {
|
||||
allDataPoints.push(...props.bancaPreviousSparklineData)
|
||||
const allDataPoints = [...props.bancaSparklineData];
|
||||
if (
|
||||
props.bancaPreviousSparklineData &&
|
||||
props.bancaPreviousSparklineData.length > 0
|
||||
) {
|
||||
allDataPoints.push(...props.bancaPreviousSparklineData);
|
||||
}
|
||||
const dataMin = Math.min(...allDataPoints)
|
||||
const dataMax = Math.max(...allDataPoints)
|
||||
const dataRange = dataMax - dataMin
|
||||
const dataPadding = dataRange * 0.05
|
||||
const dataMin = Math.min(...allDataPoints);
|
||||
const dataMax = Math.max(...allDataPoints);
|
||||
const dataRange = dataMax - dataMin;
|
||||
const dataPadding = dataRange * 0.05;
|
||||
|
||||
bancaChartInstance = new Chart(ctx, {
|
||||
type: 'line',
|
||||
type: "line",
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: datasets
|
||||
datasets: datasets,
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: 'index'
|
||||
mode: "index",
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: datasets.length > 1,
|
||||
position: 'top',
|
||||
align: 'end',
|
||||
position: "top",
|
||||
align: "end",
|
||||
labels: {
|
||||
boxWidth: 12,
|
||||
boxHeight: 12,
|
||||
padding: 8,
|
||||
font: {
|
||||
size: 10,
|
||||
family: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif'
|
||||
family: "Inter, -apple-system, BlinkMacSystemFont, sans-serif",
|
||||
},
|
||||
color: 'rgba(107, 114, 128, 0.9)',
|
||||
color: "rgba(107, 114, 128, 0.9)",
|
||||
usePointStyle: true,
|
||||
pointStyle: 'line'
|
||||
}
|
||||
pointStyle: "line",
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||||
titleColor: '#ffffff',
|
||||
bodyColor: '#ffffff',
|
||||
borderColor: 'rgba(255, 255, 255, 0.2)',
|
||||
backgroundColor: "rgba(0, 0, 0, 0.8)",
|
||||
titleColor: "#ffffff",
|
||||
bodyColor: "#ffffff",
|
||||
borderColor: "rgba(255, 255, 255, 0.2)",
|
||||
borderWidth: 1,
|
||||
cornerRadius: 6,
|
||||
displayColors: true,
|
||||
callbacks: {
|
||||
title: (context) => context[0].label || '',
|
||||
title: (context) => context[0].label || "",
|
||||
label: (context) => {
|
||||
const value = context.parsed.y
|
||||
const label = context.dataset.label || ''
|
||||
const formattedValue = new Intl.NumberFormat('ro-RO', {
|
||||
style: 'currency',
|
||||
currency: 'RON',
|
||||
const value = context.parsed.y;
|
||||
const label = context.dataset.label || "";
|
||||
const formattedValue = new Intl.NumberFormat("ro-RO", {
|
||||
style: "currency",
|
||||
currency: "RON",
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0
|
||||
}).format(value)
|
||||
return `${label}: ${formattedValue}`
|
||||
}
|
||||
}
|
||||
}
|
||||
maximumFractionDigits: 0,
|
||||
}).format(value);
|
||||
return `${label}: ${formattedValue}`;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: true,
|
||||
grid: {
|
||||
display: false,
|
||||
drawBorder: false
|
||||
drawBorder: false,
|
||||
},
|
||||
ticks: {
|
||||
color: 'rgba(107, 114, 128, 0.7)',
|
||||
color: "rgba(107, 114, 128, 0.7)",
|
||||
font: {
|
||||
size: 10,
|
||||
family: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif'
|
||||
family: "Inter, -apple-system, BlinkMacSystemFont, sans-serif",
|
||||
},
|
||||
maxRotation: 45,
|
||||
minRotation: 45,
|
||||
maxTicksLimit: 6
|
||||
maxTicksLimit: 6,
|
||||
},
|
||||
border: {
|
||||
display: false
|
||||
}
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
display: true,
|
||||
min: dataMin - dataPadding,
|
||||
max: dataMax + dataPadding,
|
||||
grid: {
|
||||
color: 'rgba(107, 114, 128, 0.1)',
|
||||
drawBorder: false
|
||||
color: "rgba(107, 114, 128, 0.1)",
|
||||
drawBorder: false,
|
||||
},
|
||||
ticks: {
|
||||
color: '#3b82f6',
|
||||
color: "#3b82f6",
|
||||
font: {
|
||||
size: 11,
|
||||
family: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif'
|
||||
family: "Inter, -apple-system, BlinkMacSystemFont, sans-serif",
|
||||
},
|
||||
maxTicksLimit: 3,
|
||||
callback: function(value) {
|
||||
callback: function (value) {
|
||||
if (value >= 1000000) {
|
||||
return (value / 1000000).toFixed(1) + 'M'
|
||||
return (value / 1000000).toFixed(1) + "M";
|
||||
} else if (value >= 1000) {
|
||||
return (value / 1000).toFixed(0) + 'k'
|
||||
return (value / 1000).toFixed(0) + "k";
|
||||
}
|
||||
return value.toFixed(0)
|
||||
}
|
||||
return value.toFixed(0);
|
||||
},
|
||||
},
|
||||
border: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Watch for data changes
|
||||
watch(() => [
|
||||
props.casaSparklineData,
|
||||
props.bancaSparklineData,
|
||||
props.sparklineLabels,
|
||||
props.casaPreviousSparklineData,
|
||||
props.bancaPreviousSparklineData,
|
||||
props.previousSparklineLabels
|
||||
], async () => {
|
||||
await Promise.all([
|
||||
initializeCasaChart(),
|
||||
initializeBancaChart()
|
||||
])
|
||||
}, { deep: true })
|
||||
watch(
|
||||
() => [
|
||||
props.casaSparklineData,
|
||||
props.bancaSparklineData,
|
||||
props.sparklineLabels,
|
||||
props.casaPreviousSparklineData,
|
||||
props.bancaPreviousSparklineData,
|
||||
props.previousSparklineLabels,
|
||||
],
|
||||
async () => {
|
||||
await Promise.all([initializeCasaChart(), initializeBancaChart()]);
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
// Lifecycle hooks
|
||||
onMounted(async () => {
|
||||
await Promise.all([
|
||||
initializeCasaChart(),
|
||||
initializeBancaChart()
|
||||
])
|
||||
})
|
||||
await Promise.all([initializeCasaChart(), initializeBancaChart()]);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (casaChartInstance) {
|
||||
casaChartInstance.destroy()
|
||||
casaChartInstance = null
|
||||
casaChartInstance.destroy();
|
||||
casaChartInstance = null;
|
||||
}
|
||||
if (bancaChartInstance) {
|
||||
bancaChartInstance.destroy()
|
||||
bancaChartInstance = null
|
||||
bancaChartInstance.destroy();
|
||||
bancaChartInstance = null;
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user