|
|
@@ -5,7 +5,7 @@
|
|
|
|
|
|
<div class="dashboard-container">
|
|
|
|
|
|
- <div class="chart-row" style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; align-items: start;">
|
|
|
+ <div wire:ignore class="chart-row" style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; align-items: start;">
|
|
|
<div class="chart-card">
|
|
|
<div class="chart-header">
|
|
|
<h3 class="chart-title">Entrate/Uscite totali</h3>
|
|
|
@@ -933,19 +933,17 @@
|
|
|
|
|
|
const chartContainer = document.querySelector('.modern-chart-container');
|
|
|
if (chartContainer) {
|
|
|
- chartContainer.innerHTML = `
|
|
|
- <div class="chart-empty-state">
|
|
|
- <div style="text-align: center; padding: 4rem 2rem;">
|
|
|
- <div style="font-size: 4rem; margin-bottom: 1.5rem; opacity: 0.3;">📊</div>
|
|
|
- <h3 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: #374151;">
|
|
|
- Grafico non disponibile
|
|
|
- </h3>
|
|
|
- <p style="font-size: 1rem; opacity: 0.7; margin: 0; max-width: 400px; margin-left: auto; margin-right: auto; line-height: 1.5;">
|
|
|
- Il grafico per questo corso non può essere visualizzato nella stagione selezionata.
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- `;
|
|
|
+ chartContainer.innerHTML = `<div class="chart-empty-state">
|
|
|
+ <div style="text-align: center; padding: 4rem 2rem;">
|
|
|
+ <div style="font-size: 4rem; margin-bottom: 1.5rem; opacity: 0.3;">📊</div>
|
|
|
+ <h3 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: #374151;">
|
|
|
+ Grafico non disponibile
|
|
|
+ </h3>
|
|
|
+ <p style="font-size: 1rem; opacity: 0.7; margin: 0; max-width: 400px; margin-left: auto; margin-right: auto; line-height: 1.5;">
|
|
|
+ Il grafico per questo corso non può essere visualizzato nella stagione selezionata.
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </div>`;
|
|
|
}
|
|
|
|
|
|
if (tableContainer) {
|
|
|
@@ -968,19 +966,17 @@
|
|
|
}
|
|
|
|
|
|
const chartContainer = canvas.parentElement;
|
|
|
- chartContainer.innerHTML = `
|
|
|
- <div class="chart-empty-state">
|
|
|
- <div style="text-align: center; padding: 4rem 2rem;">
|
|
|
- <div style="font-size: 4rem; margin-bottom: 1.5rem; opacity: 0.3;">📊</div>
|
|
|
- <h3 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: #374151;">
|
|
|
- ${courseData.message}
|
|
|
- </h3>
|
|
|
- <p style="font-size: 1rem; opacity: 0.7; margin: 0; max-width: 400px; margin-left: auto; margin-right: auto; line-height: 1.5;">
|
|
|
- Questo corso non ha pagamenti registrati per la stagione selezionata.
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- `;
|
|
|
+ chartContainer.innerHTML = `<div class="chart-empty-state">
|
|
|
+ <div style="text-align: center; padding: 4rem 2rem;">
|
|
|
+ <div style="font-size: 4rem; margin-bottom: 1.5rem; opacity: 0.3;">📊</div>
|
|
|
+ <h3 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: #374151;">
|
|
|
+ ${courseData.message}
|
|
|
+ </h3>
|
|
|
+ <p style="font-size: 1rem; opacity: 0.7; margin: 0; max-width: 400px; margin-left: auto; margin-right: auto; line-height: 1.5;">
|
|
|
+ Questo corso non ha pagamenti registrati per la stagione selezionata.
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </div>`;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -999,6 +995,7 @@
|
|
|
this.updateCourseTable(tableContainer, courseData.tableData);
|
|
|
|
|
|
const participantData = courseData.datasets.find(d => d.participantData)?.participantData || [];
|
|
|
+ const suspendedData = courseData.datasets.find(d => d.suspendedData)?.suspendedData || [];
|
|
|
|
|
|
const ctx = canvasElement.getContext('2d');
|
|
|
|
|
|
@@ -1006,8 +1003,8 @@
|
|
|
earnedGradient.addColorStop(0, 'rgba(16, 185, 129, 1)');
|
|
|
earnedGradient.addColorStop(1, 'rgba(16, 185, 129, 1)');
|
|
|
|
|
|
- const totalData = courseData.datasets.find(d => d.label === 'Pagamenti Attesi')?.data || [];
|
|
|
- const earnedData = courseData.datasets.find(d => d.label === 'Pagamenti Effettuati')?.data || [];
|
|
|
+ const totalData = courseData.datasets.find(d => d.label === 'TOT. DA INCASSARE')?.data || [];
|
|
|
+ const earnedData = courseData.datasets.find(d => d.label === 'TOT. INCASSATO')?.data || [];
|
|
|
|
|
|
this.charts[chartId] = new Chart(ctx, {
|
|
|
type: 'bar',
|
|
|
@@ -1015,31 +1012,46 @@
|
|
|
labels: courseData.labels,
|
|
|
datasets: [
|
|
|
{
|
|
|
- label: 'Pagamenti Effettuati',
|
|
|
+ label: 'TOT. INCASSATO',
|
|
|
backgroundColor: earnedGradient,
|
|
|
borderColor: 'rgba(16, 185, 129, 1)',
|
|
|
borderWidth: 0,
|
|
|
- borderRadius: 8,
|
|
|
- borderSkipped: false,
|
|
|
+ borderRadius: {
|
|
|
+ topLeft: 8,
|
|
|
+ topRight: 8,
|
|
|
+ bottomLeft: 0,
|
|
|
+ bottomRight: 0,
|
|
|
+ },
|
|
|
+ borderSkipped: true,
|
|
|
data: earnedData,
|
|
|
type: 'bar',
|
|
|
- order: 2
|
|
|
+ barThickness: "flex",
|
|
|
+ barPercentage: 0.65,
|
|
|
+ categoryPercentage: 0.25,
|
|
|
+ order: 1,
|
|
|
+ participantData: participantData,
|
|
|
+ suspendedData: suspendedData,
|
|
|
},
|
|
|
{
|
|
|
- label: 'Pagamenti Attesi',
|
|
|
- backgroundColor: 'transparent',
|
|
|
- borderColor: 'rgba(59, 130, 246, 1)',
|
|
|
- borderWidth: 3,
|
|
|
- pointBackgroundColor: 'rgba(59, 130, 246, 1)',
|
|
|
- pointBorderColor: '#ffffff',
|
|
|
- pointBorderWidth: 3,
|
|
|
- pointRadius: 7,
|
|
|
- pointHoverRadius: 9,
|
|
|
+ label: 'TOT. DA INCASSARE',
|
|
|
+ backgroundColor: '#F28322',
|
|
|
+ borderColor: '#F28322',
|
|
|
+ borderWidth: 0,
|
|
|
+ borderRadius: {
|
|
|
+ topLeft: 8,
|
|
|
+ topRight: 8,
|
|
|
+ bottomLeft: 0,
|
|
|
+ bottomRight: 0,
|
|
|
+ },
|
|
|
+ borderSkipped: true,
|
|
|
data: totalData,
|
|
|
- type: 'line',
|
|
|
- tension: 0.3,
|
|
|
- order: 1,
|
|
|
- participantData: participantData
|
|
|
+ type: 'bar',
|
|
|
+ barThickness: "flex",
|
|
|
+ barPercentage: 0.65,
|
|
|
+ categoryPercentage: 0.25,
|
|
|
+ order: 2,
|
|
|
+ participantData: participantData,
|
|
|
+ suspendedData: suspendedData,
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
@@ -1060,6 +1072,7 @@
|
|
|
},
|
|
|
scales: {
|
|
|
x: {
|
|
|
+ stacked: true,
|
|
|
grid: {
|
|
|
display: false
|
|
|
},
|
|
|
@@ -1075,6 +1088,7 @@
|
|
|
}
|
|
|
},
|
|
|
y: {
|
|
|
+ stacked: true,
|
|
|
beginAtZero: true,
|
|
|
grid: {
|
|
|
color: 'rgba(156, 163, 175, 0.15)',
|
|
|
@@ -1110,50 +1124,54 @@
|
|
|
},
|
|
|
tooltip: {
|
|
|
backgroundColor: 'rgba(255, 255, 255, 1)',
|
|
|
- titleColor: '#111827',
|
|
|
- bodyColor: '#374151',
|
|
|
borderColor: 'rgba(229, 231, 235, 0.8)',
|
|
|
borderWidth: 2,
|
|
|
cornerRadius: 0,
|
|
|
titleFont: {
|
|
|
+ size: 15,
|
|
|
weight: 'bold',
|
|
|
- size: 15
|
|
|
},
|
|
|
+ titleColor: '#111827',
|
|
|
bodyFont: {
|
|
|
size: 14,
|
|
|
- weight: '500'
|
|
|
+ weight: 'bold',
|
|
|
+ },
|
|
|
+ bodyColor: '#111827',
|
|
|
+ footerFont: {
|
|
|
+ size: 14,
|
|
|
+ weight: 'bold',
|
|
|
},
|
|
|
+ footerColor: '#0C6197',
|
|
|
+ footerSpacing: 0,
|
|
|
+ footerMarginTop: 0,
|
|
|
padding: 16,
|
|
|
boxPadding: 8,
|
|
|
usePointStyle: true,
|
|
|
- displayColors: true,
|
|
|
+ displayColors: false,
|
|
|
callbacks: {
|
|
|
- title: function (context) {
|
|
|
- return context[0].label;
|
|
|
- },
|
|
|
- label: function (context) {
|
|
|
- let label = context.dataset.label + ': €' +
|
|
|
- new Intl.NumberFormat('it-IT').format(context.parsed.y);
|
|
|
-
|
|
|
- if (context.dataset.label === 'Pagamenti Effettuati') {
|
|
|
- const earnedValue = parseFloat(context.parsed.y) || 0;
|
|
|
- const totalValue = parseFloat(totalData[context.dataIndex]) || 0;
|
|
|
- const missingValue = Math.max(0, totalValue - earnedValue);
|
|
|
+ title: function (tooltipItems) {
|
|
|
+ let sum = 0;
|
|
|
|
|
|
- if (participantData[context.dataIndex]) {
|
|
|
- label += '\n👥 Partecipanti: ' + participantData[context.dataIndex];
|
|
|
- }
|
|
|
+ tooltipItems.forEach(function(tooltipItem) {
|
|
|
+ sum += tooltipItem.parsed.y;
|
|
|
+ });
|
|
|
|
|
|
- if (missingValue > 0) {
|
|
|
- label += '\n🔴 Mancanti: €' + new Intl.NumberFormat('it-IT').format(missingValue);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (context.dataset.label === 'Pagamenti Attesi' && participantData[context.dataIndex]) {
|
|
|
- label += '\n👥 Partecipanti: ' + participantData[context.dataIndex];
|
|
|
- }
|
|
|
+ return tooltipItems[0].label + '\n' + 'TOTALE ATTESO: €' + new Intl.NumberFormat('it-IT').format(sum);
|
|
|
+ },
|
|
|
+ labelTextColor: function(tooltipItems) {
|
|
|
+ return tooltipItems.dataset.backgroundColor;
|
|
|
+ },
|
|
|
+ label: function (tooltipItems) {
|
|
|
+ let label = tooltipItems.dataset.label + ': €' + new Intl.NumberFormat('it-IT').format(tooltipItems.parsed.y);
|
|
|
|
|
|
return label;
|
|
|
+ },
|
|
|
+ footer: function (tooltipItems) {
|
|
|
+ let item = tooltipItems[0];
|
|
|
+ let index = item.dataIndex;
|
|
|
+ let suspendedData = item.dataset["suspendedData"] ? item.dataset["suspendedData"][index] : 0;
|
|
|
+
|
|
|
+ return "TOTALE SOSPESI: " + suspendedData;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1176,8 +1194,8 @@
|
|
|
borderJoinStyle: 'round'
|
|
|
},
|
|
|
point: {
|
|
|
- hoverBorderWidth: 4,
|
|
|
- borderWidth: 3
|
|
|
+ hoverBorderWidth: 2,
|
|
|
+ borderWidth: 1
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1190,15 +1208,13 @@
|
|
|
updateCourseTable: function (container, tableData) {
|
|
|
if (!container || !tableData) return;
|
|
|
|
|
|
- let tableHtml = `
|
|
|
- <div class="course-table">
|
|
|
- <div class="table-header">
|
|
|
- <div class="table-cell month">Mese</div>
|
|
|
- <div class="table-cell participants">👥</div>
|
|
|
- <div class="table-cell delta">Mancanti</div>
|
|
|
- <div class="table-cell percentage">%</div>
|
|
|
- </div>
|
|
|
- `;
|
|
|
+ let tableHtml = `<div class="course-table">
|
|
|
+ <div class="table-header">
|
|
|
+ <div class="table-cell month">MESE</div>
|
|
|
+ <div class="table-cell percentage">%</div>
|
|
|
+ <div class="table-cell delta">TOT. DA INCASSARE</div>
|
|
|
+ <div class="table-cell suspended">SOSPESI</div>
|
|
|
+ </div>`;
|
|
|
|
|
|
tableData.forEach(row => {
|
|
|
const earned = parseFloat(row.earned) || 0;
|
|
|
@@ -1230,9 +1246,9 @@
|
|
|
tableHtml += `
|
|
|
<div class="table-row">
|
|
|
<div class="table-cell month">${row.month}</div>
|
|
|
- <div class="table-cell participants">${row.participants}</div>
|
|
|
- <div class="table-cell delta ${deltaClass}">€${new Intl.NumberFormat('it-IT').format(delta)}</div>
|
|
|
<div class="table-cell percentage ${percentageClass}">${percentageDisplay}</div>
|
|
|
+ <div class="table-cell delta ${deltaClass}">€${new Intl.NumberFormat('it-IT').format(delta)}</div>
|
|
|
+ <div class="table-cell suspended">${row.suspended}</div>
|
|
|
</div>
|
|
|
`;
|
|
|
});
|