ferrari 4 месяцев назад
Родитель
Сommit
00c14d0bbc
3 измененных файлов с 140 добавлено и 117 удалено
  1. 24 23
      app/Http/Livewire/Reports.php
  2. 14 8
      public/css/chart-reports.css
  3. 102 86
      resources/views/livewire/reports.blade.php

+ 24 - 23
app/Http/Livewire/Reports.php

@@ -514,6 +514,7 @@ class Reports extends Component
             $monthlyData[$i] = [
                 'earned' => 0,
                 'total' => 0,
+                'suspended' => 0,
                 'participants' => 0
             ];
         }
@@ -558,11 +559,18 @@ class Reports extends Component
                             $monthlyData[$monthNumber]['total'] += $pricePerMonth;
                             $monthlyData[$monthNumber]['participants']++;
                             $hasData = true;
-
+                            
                             //if (!is_null($rate->record_id) && $rate->record_id !== '') {
                             if ($month["status"] == 1) {
+                                $monthlyData[$monthNumber]['participants']--;
                                 $monthlyData[$monthNumber]['earned'] += $pricePerMonth;
                             }
+                            // pagamenti sospesi
+                            if ($month["status"] == 2) {
+                                $monthlyData[$monthNumber]['participants']--;
+                                $monthlyData[$monthNumber]['total'] -= $pricePerMonth;
+                                $monthlyData[$monthNumber]['suspended']++;
+                            }
                         }
                     }
                 }
@@ -590,17 +598,20 @@ class Reports extends Component
             $total = round($monthlyData[$month]['total'], 2);
             $delta = max(0, $total - $earned);
             $participants = $monthlyData[$month]['participants'];
+            $suspended = $monthlyData[$month]['suspended'];
 
             $labels[] = $monthNames[$month];
             $earnedData[] = $earned;
             $totalData[] = $total;
             $participantData[] = $participants;
+            $suspendedData[] = $suspended;
 
             $percentage = $total > 0 ? round(($earned / $total) * 100, 1) : 0;
 
             $tableData[] = [
                 'month' => $monthNames[$month],
                 'participants' => $participants,
+                'suspended' => $suspended,
                 'earned' => $earned,
                 'total' => $total,
                 'delta' => $delta,
@@ -608,35 +619,25 @@ class Reports extends Component
             ];
         }
 
+        $daIncassareData = array_map(function($tot, $inc) {
+            return $tot - $inc;
+        }, $totalData, $earnedData);
+
+
         return [
             'labels' => $labels,
             'datasets' => [
                 [
-                    'label' => 'Pagamenti Effettuati',
-                    'backgroundColor' => 'rgba(16, 185, 129, 0.8)',
-                    'borderColor' => 'rgba(16, 185, 129, 1)',
-                    'borderWidth' => 0,
-                    'borderRadius' => 8,
-                    'borderSkipped' => false,
+                    'label' => 'TOT. INCASSATO',
                     'data' => $earnedData,
-                    'type' => 'bar',
-                    'order' => 2
+                    '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,
-                    'data' => $totalData,
-                    'type' => 'line',
-                    'tension' => 0.3,
-                    'order' => 1,
-                    'participantData' => $participantData
+                    'label' => 'TOT. DA INCASSARE',
+                    'data' => $daIncassareData,
+                    'participantData' => $participantData,
+                    'suspendedData' => $suspendedData
                 ]
             ],
             'tableData' => $tableData,

+ 14 - 8
public/css/chart-reports.css

@@ -561,7 +561,7 @@
 
 .course-table .table-header {
     display: grid;
-    grid-template-columns: 1fr 40px 80px 50px;
+    grid-template-columns: 1fr 40px 110px 75px;
     background: white;
     padding: 12px 16px;
     font-weight: 600;
@@ -572,7 +572,7 @@
 
 .course-table .table-row {
     display: grid;
-    grid-template-columns: 1fr 40px 80px 50px;
+    grid-template-columns: 1fr 40px 110px 75px;
     padding: 12px 16px;
     border-bottom: 1px solid #f3f4f6;
     transition: background-color 0.2s;
@@ -600,7 +600,13 @@
 .course-table .table-cell.participants {
     justify-content: center;
     color: #6b7280;
-    font-weight: 500;
+    font-weight: 600;
+}
+
+.course-table .table-cell.suspended {
+    justify-content: center;
+    color: #0C6197;
+    font-weight: 600;
 }
 
 .course-table .table-cell.delta {
@@ -609,11 +615,11 @@
 }
 
 .course-table .table-cell.delta.positive {
-    color: #059669;
+    color: #10b981;
 }
 
 .course-table .table-cell.delta.negative {
-    color: #dc2626;
+    color: #f28322;
 }
 
 .course-table .table-cell.delta.neutral {
@@ -625,7 +631,7 @@
     font-weight: 600;
     font-size: 0.8rem;
 }
-
+/*
 .course-table .table-cell.percentage.good {
     color: #059669;
 }
@@ -636,7 +642,7 @@
 
 .course-table .table-cell.percentage.bad {
     color: #dc2626;
-}
+} */
 
 .modern-chart-container {
     background: white;
@@ -958,7 +964,7 @@ canvas[id^="courses-chart-"] {
 
 .modern-chart-layout {
     display: grid;
-    grid-template-columns: minmax(280px, 280px) minmax(0, 1fr);
+    grid-template-columns: minmax(350px, 350px) minmax(0, 1fr);
     gap: 24px;
     align-items: start;
     margin-top: 20px;

+ 102 - 86
resources/views/livewire/reports.blade.php

@@ -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>
         `;
                 });