Procházet zdrojové kódy

modifiche grafiche report + fix rate

ferrari před 4 měsíci
rodič
revize
a818b424ea

+ 5 - 5
app/Http/Livewire/Member.php

@@ -48,7 +48,7 @@ class Member extends Component
             if ($type === 'corsi' && $this->dataId > 0) {
             $this->loadMemberCards();
             $this->loadMemberCertificates();
-            $this->checkCourseAvailability();
+            // TODO $this->checkCourseAvailability();
         }
 
         $this->dispatchBrowserEvent('scroll-to-top');
@@ -1463,7 +1463,7 @@ class Member extends Component
             $this->resetCardFields();
             $this->addCard = false;
             $this->loadMemberCards();
-            $this->checkCourseAvailability();
+            // TODO $this->checkCourseAvailability();
         } catch (\Exception $ex) {
             session()->flash('error', 'Errore (' . $ex->getMessage() . ')');
         }
@@ -1584,7 +1584,7 @@ class Member extends Component
             $this->resetCardFields();
             $this->updateCard = false;
             $this->loadMemberCards();
-            $this->checkCourseAvailability();
+            // TODO $this->checkCourseAvailability();
         } catch (\Exception $ex) {
             Log::error('Error updating card', [
                 'card_id' => $this->cardDataId,
@@ -1869,7 +1869,7 @@ class Member extends Component
             $this->resetCertificateFields();
             $this->addCertificate = false;
             $this->loadMemberCertificates();
-            $this->checkCourseAvailability();
+            // TODO $this->checkCourseAvailability();
         } catch (\Exception $ex) {
             session()->flash('error', 'Errore (' . $ex->getMessage() . ')');
         }
@@ -1918,7 +1918,7 @@ class Member extends Component
             $this->resetCertificateFields();
             $this->updateCertificate = false;
             $this->loadMemberCertificates();
-            $this->checkCourseAvailability();
+            // TODO $this->checkCourseAvailability();
         } catch (\Exception $ex) {
             session()->flash('error', 'Errore (' . $ex->getMessage() . ')');
         }

+ 29 - 4
app/Http/Livewire/Rate.php

@@ -5,6 +5,8 @@ namespace App\Http\Livewire;
 use Livewire\Component;
 use Barryvdh\DomPDF\Facade\Pdf;
 use App\Models\Member;
+use App\Http\Middleware\TenantMiddleware;
+use Illuminate\Support\Facades\DB;
 
 
 class Rate extends Component
@@ -29,12 +31,35 @@ class Rate extends Component
     public $month = '';
     public $months = [];
     public $disabled = [];
-    public $couse_subscriptions = [];
+    public $course_subscriptions = [];
     public $price_list = [];
     public $type = '';
 
     public $errorMsg = '';
 
+    public function boot()
+    {
+        app(TenantMiddleware::class)->setupTenantConnection();
+    }
+
+    protected function setupTenantConnection()
+    {
+        $user = auth()->user();
+
+        config(['database.connections.tenant' => [
+            'driver' => 'mysql',
+            'host' => '127.0.0.1',
+            'port' => '3306',
+            'database' => $user->tenant_database,
+            'username' => $user->tenant_username,
+            'password' => $user->tenant_password,
+        ]]);
+
+        config(['database.default' => 'tenant']);
+        DB::purge('tenant');
+        DB::reconnect('tenant');
+    }
+
     public function mount()
     {
         // Load members for the dropdown
@@ -201,7 +226,7 @@ class Rate extends Component
             //$this->emit('load-data-table');
             session()->flash('success',"Rata eliminata");
         }catch(\Exception $e){
-            session()->flash('error','Errore (' . $ex->getMessage() . ')');
+            session()->flash('error','Errore (' . $e->getMessage() . ')');
         }
     }
 
@@ -215,7 +240,7 @@ class Rate extends Component
             //$this->emit('load-data-table');
             session()->flash('success',"Rata eliminata");
         }catch(\Exception $e){
-            session()->flash('error','Errore (' . $ex->getMessage() . ')');
+            session()->flash('error','Errore (' . $e->getMessage() . ')');
         }
     }
 
@@ -232,7 +257,7 @@ class Rate extends Component
             //$this->emit('load-data-table');
             session()->flash('success',"Rata eliminata");
         }catch(\Exception $e){
-            session()->flash('error','Errore (' . $ex->getMessage() . ')');
+            session()->flash('error','Errore (' . $e->getMessage() . ')');
         }
     }
 

+ 45 - 20
app/Http/Livewire/Reports.php

@@ -447,12 +447,27 @@ class Reports extends Component
             7 => 'Lug',
             8 => 'Ago'
         ];
+        $monthNamesExtended = [
+            0 => 'Settembre',
+            1 => 'Ottobre',
+            2 => 'Novembre',
+            3 => 'Dicembre',
+            4 => 'Gennaio',
+            5 => 'Febbraio',
+            6 => 'Marzo',
+            7 => 'Aprile',
+            8 => 'Maggio',
+            9 => 'Giugno',
+            10 => 'Luglio',
+            11 => 'Agosto'
+        ];
 
         $monthlyData = [];
         foreach ($monthOrder as $i) {
             $monthlyData[$i] = [
                 'earned' => 0,
                 'total' => 0,
+                'suspended' => 0,
                 'participants' => 0
             ];
         }
@@ -490,9 +505,21 @@ class Reports extends Component
                             $monthlyData[$monthNumber]['participants']++;
                             $hasData = true;
 
-                            if (!is_null($rate->record_id) && $rate->record_id !== '') {
+                            // if (!is_null($rate->record_id) && $rate->record_id !== '') {
+                            //     $monthlyData[$monthNumber]['earned'] += $pricePerMonth;
+                            // }
+
+                            // pagamenti effettuati
+                            if (!is_null($rate->record_id) && $rate->record_id !== '' && $rate->status == 1) {
+                                $monthlyData[$monthNumber]['participants']--;
                                 $monthlyData[$monthNumber]['earned'] += $pricePerMonth;
                             }
+                            // pagamenti sospesi
+                            if ($rate->status == 2) {
+                                $monthlyData[$monthNumber]['participants']--;
+                                $monthlyData[$monthNumber]['total'] -= $pricePerMonth;
+                                $monthlyData[$monthNumber]['suspended']++;
+                            }
                         }
                     }
                 }
@@ -520,17 +547,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,
@@ -538,32 +568,27 @@ 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,
+                    'monthNamesExtended' => $monthNamesExtended,
                 ],
                 [
-                    'label' => 'Pagamenti Attesi',
-                    'backgroundColor' => 'transparent',
-                    'backgroundColor' => 'rgba(59, 130, 246, 0.8)',
-                    'borderColor' => 'rgba(59, 130, 246, 1)',
-                    'borderWidth' => 0,
-                    'borderRadius' => 8,
-                    'borderSkippet' => false,
-                    'data' => $totalData,
-                    'type' => 'bar',
-                    'order' => 1,
-                    'participantData' => $participantData
+                    'label' => 'TOT. DA INCASSARE',
+                    'data' => $daIncassareData,
+                    'participantData' => $participantData,
+                    'suspendedData' => $suspendedData,
+                    'monthNamesExtended' => $monthNamesExtended,
                 ]
             ],
             'tableData' => $tableData,

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

@@ -560,7 +560,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;
@@ -571,7 +571,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;
@@ -599,7 +599,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 {
@@ -608,11 +614,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-size: 0.8rem;
 }
 
-.course-table .table-cell.percentage.good {
+/* .course-table .table-cell.percentage.good {
     color: #059669;
 }
 
@@ -635,7 +641,7 @@
 
 .course-table .table-cell.percentage.bad {
     color: #dc2626;
-}
+} */
 
 .modern-chart-container {
     background: white;
@@ -957,7 +963,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;

+ 88 - 79
resources/views/livewire/reports.blade.php

@@ -114,7 +114,6 @@
                     <div class="modern-chart-layout">
                         <div class="course-delta-table" id="course-delta-table-{{ str_replace('-', '', $seasonFilter) }}-{{ $selectedCourse }}">
                         </div>
-
                         <div class="modern-chart-container">
                             <canvas id="courses-chart-{{ str_replace('-', '', $seasonFilter) }}-{{ $selectedCourse }}"></canvas>
                         </div>
@@ -691,9 +690,9 @@
 
             createCourseChart: function () {
                 console.log('Creating course chart...');
-                const seasonFilter = '{{ $seasonFilter }}';
-                const selectedCourse = '{{ $selectedCourse ?? '' }}';
-                const seasonKey = '{{ str_replace('-', '', $seasonFilter) }}';
+                const seasonFilter = @this.seasonFilter;
+                const selectedCourse = @this.selectedCourse ?? '';
+                const seasonKey = seasonFilter.replace('-', '');
                 console.log('Selected course:', selectedCourse, 'for season:', seasonFilter);
 
                 if (!selectedCourse || selectedCourse.trim() === '') {
@@ -784,8 +783,8 @@
             },
             createCourseChartWithValue: function (selectedCourseValue) {
                 console.log('Creating modern course chart with value:', selectedCourseValue);
-                const seasonFilter = '{{ $seasonFilter }}';
-                const seasonKey = '{{ str_replace('-', '', $seasonFilter) }}';
+                const seasonFilter = @this.seasonFilter;
+                const seasonKey = seasonFilter.replace('-', '');
 
                 const chartId = `courses-chart-${seasonKey}-${selectedCourseValue}`;
                 const tableId = `course-delta-table-${seasonKey}-${selectedCourseValue}`;
@@ -797,19 +796,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) {
@@ -832,19 +829,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;
                     }
 
@@ -863,6 +858,8 @@
                     this.updateCourseTable(tableContainer, courseData.tableData);
 
                     const participantData = courseData.datasets.find(d => d.participantData)?.participantData || [];
+                    const suspendedData = courseData.datasets.find(d => d.suspendedData)?.suspendedData || [];
+                    const monthNamesExtended = courseData.datasets.find(d => d.monthNamesExtended)?.monthNamesExtended || [];
 
                     const ctx = canvasElement.getContext('2d');
 
@@ -870,8 +867,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',
@@ -879,7 +876,7 @@
                             labels: courseData.labels,
                             datasets: [
                                 {
-                                    label: 'Pagamenti Effettuati',
+                                    label: 'TOT. INCASSATO',
                                     backgroundColor: earnedGradient,
                                     borderColor: 'rgba(16, 185, 129, 1)',
                                     borderWidth: 0,
@@ -894,13 +891,16 @@
                                     type: 'bar',
                                     barThickness: "flex",
                                     barPercentage: 0.65,
-                                    categoryPercentage: 0.4,
-                                    order: 2,
+                                    categoryPercentage: 0.25,
+                                    order: 1,
+                                    participantData: participantData,
+                                    suspendedData: suspendedData,
+                                    monthNamesExtended: monthNamesExtended,
                                 },
                                 {
-                                    label: 'Pagamenti Attesi',
-                                    backgroundColor: 'rgba(59, 130, 246, 1)',
-                                    borderColor: 'rgba(59, 130, 246, 1)',
+                                    label: 'TOT. DA INCASSARE',
+                                    backgroundColor: '#F28322',
+                                    borderColor: '#F28322',
                                     borderWidth: 0,
                                     borderRadius: {
                                         topLeft: 8,
@@ -913,9 +913,11 @@
                                     type: 'bar',
                                     barThickness: "flex",
                                     barPercentage: 0.65,
-                                    categoryPercentage: 0.4,
-                                    order: 1,
+                                    categoryPercentage: 0.25,
+                                    order: 2,
                                     participantData: participantData,
+                                    suspendedData: suspendedData,
+                                    monthNamesExtended: monthNamesExtended,
                                 }
                             ]
                         },
@@ -988,51 +990,60 @@
                                 },
                                 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: '400',
+                                    },
+                                    bodyColor: '#111827',
+                                    footerFont: {
+                                        size: 14,
+                                        weight: '400',
                                     },
+                                    // footerColor: '#0C6197',
+                                    footerSpacing: 0,
+                                    footerMarginTop: 0,
                                     padding: 16,
                                     boxPadding: 8,
                                     usePointStyle: true,
                                     displayColors: true,
                                     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);
+                                        title: function (tooltipItems) {
+                                            let sum = 0;
 
-                                            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);
+                                            tooltipItems.forEach(function(tooltipItem) {
+                                                sum += tooltipItem.parsed.y;
+                                            });
 
-                                                if (participantData[context.dataIndex]) {
-                                                    label += '\n👥 Partecipanti: ' + participantData[context.dataIndex];
-                                                }
+                                            let item = tooltipItems[0];
+                                            let index = item.dataIndex;
+                                            let monthNameExtended = item.dataset["monthNamesExtended"] ? item.dataset["monthNamesExtended"][index] : 0;
 
-                                                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 item.label + '\n' + 'TOTALE ATTESO: €' + new Intl.NumberFormat('it-IT').format(sum);
+                                            return monthNameExtended + '\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;
+                                        // }
                                     }
                                 }
                             },
@@ -1068,15 +1079,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">%<br/>INCASSATO</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;
@@ -1108,9 +1117,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>
         `;
                 });