Przeglądaj źródła

ultimo report semi funzionante

FabioFratini 8 miesięcy temu
rodzic
commit
1f6d4ebd00

+ 84 - 160
app/Http/Livewire/Reports.php

@@ -43,9 +43,6 @@ class Reports extends Component
     {
         $now = Carbon::now();
         $currentYear = $now->year;
-
-        // If we're in September-December, season is current year to next year
-        // If we're in January-August, season is previous year to current year
         if ($now->month >= 9) {
             return $currentYear . '-' . ($currentYear + 1);
         } else {
@@ -60,16 +57,15 @@ class Reports extends Component
     {
         $seasons = [];
         $currentYear = Carbon::now()->year;
-        $startYear = 2020; // Adjust based on your data
+        $startYear = 2023;
 
-        // If current month is September or later, include next season
         $endYear = Carbon::now()->month >= 9 ? $currentYear + 1 : $currentYear;
 
-        for ($year = $startYear; $year <= $endYear; $year++) {
+        for ($year = $startYear; $year < $endYear; $year++) {
             $seasons[] = $year . '-' . ($year + 1);
         }
 
-        return array_reverse($seasons); // Most recent first
+        return array_reverse($seasons);
     }
 
     /**
@@ -140,133 +136,67 @@ class Reports extends Component
         $this->seasonFilter = $season;
     }
 
-public function getMonthlyTotals()
-{
-    $dateRange = $this->getSeasonDateRange($this->seasonFilter);
-    Log::info('Getting monthly totals for season: ' . $this->seasonFilter);
-
-    $monthOrder = [9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8];
-    $monthNames = ['Set', 'Ott', 'Nov', 'Dic', 'Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago'];
-
-    $incomeData = array_fill(0, 12, 0);
-    $expenseData = array_fill(0, 12, 0);
-    Log::info('Date range: ' . $dateRange['start'] . ' to ' . $dateRange['end']);
-
-    // Get income records with detailed logging
-    $incomeRecords = DB::table('records')
-        ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
-        ->whereBetween('records.date', [$dateRange['start'], $dateRange['end']])
-        ->where('records.type', 'IN')
-        ->select(DB::raw('MONTH(records.date) as month_num'), DB::raw('SUM(records_rows.amount) as total'))
-        ->groupBy('month_num')
-        ->get();
-
-    Log::info('Income records found: ' . $incomeRecords->count());
-    foreach ($incomeRecords as $record) {
-        Log::info("Income - Month: {$record->month_num}, Total: {$record->total}");
-    }
+    public function getMonthlyTotals()
+    {
+        $dateRange = $this->getSeasonDateRange($this->seasonFilter);
 
-    // Get expense records with detailed logging
-    $expenseRecords = DB::table('records')
-        ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
-        ->whereBetween('records.date', [$dateRange['start'], $dateRange['end']])
-        ->where('records.type', 'OUT')
-        ->select(DB::raw('MONTH(records.date) as month_num'), DB::raw('SUM(records_rows.amount) as total'))
-        ->groupBy('month_num')
-        ->get();
-
-    Log::info('Expense records found: ' . $expenseRecords->count());
-    foreach ($expenseRecords as $record) {
-        Log::info("Expense - Month: {$record->month_num}, Total: {$record->total}");
-    }
+        // September to August order
+        $monthOrder = [9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8];
+        $monthNames = ['Set', 'Ott', 'Nov', 'Dic', 'Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago'];
 
-    // Process income records
-    foreach ($incomeRecords as $record) {
-        $monthIndex = array_search($record->month_num, $monthOrder);
-        if ($monthIndex !== false) {
-            $incomeData[$monthIndex] = $record->total;
-
-            // Special logging for May (month 5)
-            if ($record->month_num == 5) {
-                Log::info("MAY INCOME DETECTED:");
-                Log::info("- Month number: {$record->month_num}");
-                Log::info("- Total amount: {$record->total}");
-                Log::info("- Array index: {$monthIndex}");
-                Log::info("- Month name: Mag");
-
-                // Get detailed May records for debugging
-                $mayIncomeDetails = DB::table('records')
-                    ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
-                    ->whereBetween('records.date', [$dateRange['start'], $dateRange['end']])
-                    ->where('records.type', 'IN')
-                    ->whereRaw('MONTH(records.date) = 5')
-                    ->select('records.date', 'records_rows.amount')
-                    ->get();
-
-                Log::info("May income detail records count: " . $mayIncomeDetails->count());
-                foreach ($mayIncomeDetails as $detail) {
-                    Log::info("May record - Date: {$detail->date}, Amount: {$detail->amount}");
-                }
+        $incomeData = array_fill(0, 12, 0);
+        $expenseData = array_fill(0, 12, 0);
+
+        // Get income records within season date range
+        $incomeRecords = DB::table('records')
+            ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
+            ->whereBetween('records.date', [$dateRange['start'], $dateRange['end']])
+            ->where('records.type', 'IN')
+            ->select(DB::raw('MONTH(records.date) as month_num'), DB::raw('SUM(records_rows.amount) as total'))
+            ->groupBy('month_num')
+            ->get();
+
+        // Get expense records within season date range
+        $expenseRecords = DB::table('records')
+            ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
+            ->whereBetween('records.date', [$dateRange['start'], $dateRange['end']])
+            ->where('records.type', 'OUT')
+            ->select(DB::raw('MONTH(records.date) as month_num'), DB::raw('SUM(records_rows.amount) as total'))
+            ->groupBy('month_num')
+            ->get();
+
+        // Map income data to correct position in season array
+        foreach ($incomeRecords as $record) {
+            $monthIndex = array_search($record->month_num, $monthOrder);
+            if ($monthIndex !== false) {
+                $incomeData[$monthIndex] = $record->total;
             }
         }
-    }
 
-    // Process expense records
-    foreach ($expenseRecords as $record) {
-        $monthIndex = array_search($record->month_num, $monthOrder);
-        if ($monthIndex !== false) {
-            $expenseData[$monthIndex] = $record->total;
-
-            // Special logging for May (month 5)
-            if ($record->month_num == 5) {
-                Log::info("MAY EXPENSE DETECTED:");
-                Log::info("- Month number: {$record->month_num}");
-                Log::info("- Total amount: {$record->total}");
-                Log::info("- Array index: {$monthIndex}");
-                Log::info("- Month name: Mag");
-
-                // Get detailed May records for debugging
-                $mayExpenseDetails = DB::table('records')
-                    ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
-                    ->whereBetween('records.date', [$dateRange['start'], $dateRange['end']])
-                    ->where('records.type', 'OUT')
-                    ->whereRaw('MONTH(records.date) = 5')
-                    ->select('records.date', 'records_rows.amount')
-                    ->get();
-
-                Log::info("May expense detail records count: " . $mayExpenseDetails->count());
-                foreach ($mayExpenseDetails as $detail) {
-                    Log::info("May record - Date: {$detail->date}, Amount: {$detail->amount}");
-                }
+        // Map expense data to correct position in season array
+        foreach ($expenseRecords as $record) {
+            $monthIndex = array_search($record->month_num, $monthOrder);
+            if ($monthIndex !== false) {
+                $expenseData[$monthIndex] = $record->total;
             }
         }
-    }
 
-    // Log final arrays
-    Log::info('Final income data array: ' . json_encode($incomeData));
-    Log::info('Final expense data array: ' . json_encode($expenseData));
-
-    // Specifically log May position (index 8 in our academic year order)
-    Log::info("May position in chart:");
-    Log::info("- Income for May (index 8): " . $incomeData[8]);
-    Log::info("- Expense for May (index 8): " . $expenseData[8]);
-
-    return [
-        'labels' => $monthNames,
-        'datasets' => [
-            [
-                'label' => 'Entrate',
-                'data' => $incomeData,
-                'backgroundColor' => 'rgba(54, 162, 235, 0.5)'
-            ],
-            [
-                'label' => 'Uscite',
-                'data' => $expenseData,
-                'backgroundColor' => 'rgba(255, 99, 132, 0.5)'
-            ],
-        ]
-    ];
-}
+        return [
+            'labels' => $monthNames,
+            'datasets' => [
+                [
+                    'label' => 'Entrate',
+                    'data' => $incomeData,
+                    'backgroundColor' => 'rgba(54, 162, 235, 0.5)'
+                ],
+                [
+                    'label' => 'Uscite',
+                    'data' => $expenseData,
+                    'backgroundColor' => 'rgba(255, 99, 132, 0.5)'
+                ],
+            ]
+        ];
+    }
 
     public function getYearlySummary()
     {
@@ -317,7 +247,7 @@ public function getMonthlyTotals()
             ->limit($limit)
             ->get();
 
-        Log::info('Causals: ' . json_encode(value: $causals));
+        Log::info('Causals: ' . json_encode($causals));
 
         $inData = [];
 
@@ -360,9 +290,13 @@ public function getMonthlyTotals()
     {
         $seasonYears = $this->parseSeason($this->seasonFilter);
 
+        Log::info('Getting courses for season: ' . $this->seasonFilter);
+        Log::info('Season years: ' . json_encode($seasonYears));
+
         $courses = Course::with(['level', 'type', 'frequency'])
             ->where('active', true)
             ->where(function($query) use ($seasonYears) {
+                // Match courses that contain either year of the season
                 $query->where('year', 'like', '%' . $seasonYears['start_year'] . '%')
                       ->orWhere('year', 'like', '%' . $seasonYears['end_year'] . '%')
                       ->orWhere('year', 'like', '%' . $this->seasonFilter . '%');
@@ -373,9 +307,6 @@ public function getMonthlyTotals()
                 $type = null;
                 if (!empty($course->course_type_id)) {
                     $type = \App\Models\CourseType::find($course->course_type_id);
-                    if ($type) {
-                        $typeName = $type->name;
-                    }
                 }
 
                 $levelName = is_object($course->level) ? $course->level->name : 'No Level';
@@ -394,15 +325,35 @@ public function getMonthlyTotals()
                 ];
             })->toArray();
 
+        Log::info('Found ' . count($courses) . ' courses for season ' . $this->seasonFilter);
+
         return $courses;
     }
 
-    public function getCourseMonthlyEarnings()
+public function updatedSelectedCourse()
+{
+    Log::info('updatedSelectedCourse called with: ' . $this->selectedCourse);
+    if ($this->selectedCourse) {
+        $this->emit('courseSelected', $this->selectedCourse);
+        Log::info('Event emitted with course ID: ' . $this->selectedCourse);
+    }
+}
+    public function getCourseData($courseId)
+    {
+        $this->selectedCourse = $courseId;
+        return $this->getCourseMonthlyEarnings($courseId);
+    }
+    public function getCourseMonthlyEarnings($courseId = null)
     {
-        $courseId = $this->selectedCourse;
+        $courseId = $courseId ?? $this->selectedCourse;
         Log::info('Getting earnings for course ID: ' . $courseId);
 
-        // September to August order for academic year
+        if (!$courseId) {
+            return [
+                'labels' => [],
+                'datasets' => []
+            ];
+        }
         $monthOrder = [9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8];
         $monthNames = [
             9 => 'Set', 10 => 'Ott', 11 => 'Nov', 12 => 'Dic',
@@ -410,33 +361,6 @@ public function getMonthlyTotals()
             5 => 'Mag', 6 => 'Giu', 7 => 'Lug', 8 => 'Ago'
         ];
 
-        if (empty($courseId)) {
-            return [
-                'labels' => array_values($monthNames),
-                'datasets' => [
-                    [
-                        'label' => 'Pagamenti Effettuati',
-                        'backgroundColor' => 'rgba(0, 184, 148, 1)',
-                        'data' => array_fill(0, 12, 0),
-                        'type' => 'bar',
-                        'order' => 3
-                    ],
-                    [
-                        'label' => 'Pagamenti Attesi',
-                        'backgroundColor' => 'transparent',
-                        'borderColor' => 'rgba(48, 51, 107, 1)',
-                        'borderWidth' => 3,
-                        'pointBackgroundColor' => 'rgba(48, 51, 107, 1)',
-                        'pointRadius' => 5,
-                        'data' => array_fill(0, 12, 0),
-                        'type' => 'line',
-                        'tension' => 0.2,
-                        'order' => 2
-                    ]
-                ]
-            ];
-        }
-
         $monthlyData = [];
         foreach ($monthOrder as $i) {
             $monthlyData[$i] = [

+ 500 - 0
public/css/chart-reports.css

@@ -0,0 +1,500 @@
+@charset "UTF-8";
+
+:root {
+    --primary-color: #6366f1;
+    --primary-light: #818cf8;
+    --primary-dark: #4f46e5;
+    --secondary-color: #64748b;
+    --success-color: #10b981;
+    --success-light: #34d399;
+    --info-color: #06b6d4;
+    --warning-color: #f59e0b;
+    --danger-color: #ef4444;
+    --danger-light: #f87171;
+    --dark-color: #1e293b;
+    --light-color: #f8fafc;
+    --border-color: #e2e8f0;
+    --border-light: #f1f5f9;
+    --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+    --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+    --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+    --gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    --gradient-success: linear-gradient(135deg, #10b981 0%, #059669 100%);
+    --gradient-danger: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
+}
+
+.dashboard-container {
+    padding: 2rem;
+    max-width: 1600px;
+    margin: 0 auto;
+    background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
+    min-height: 100vh;
+}
+
+.dashboard-header {
+    text-align: center;
+    margin-bottom: 3rem;
+    padding: 3rem 2rem;
+    background: var(--gradient-primary);
+    border-radius: 24px;
+    color: white;
+    position: relative;
+    overflow: hidden;
+}
+
+.dashboard-header::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="white" opacity="0.1"/><circle cx="75" cy="75" r="1" fill="white" opacity="0.1"/><circle cx="50" cy="10" r="0.5" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
+    pointer-events: none;
+}
+
+.dashboard-header h1 {
+    font-size: 3rem;
+    font-weight: 800;
+    margin-bottom: 0.75rem;
+    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+    position: relative;
+    z-index: 1;
+}
+
+.dashboard-header p {
+    font-size: 1.25rem;
+    opacity: 0.9;
+    position: relative;
+    z-index: 1;
+}
+
+.controls-section {
+    background: rgba(255, 255, 255, 0.8);
+    backdrop-filter: blur(10px);
+    border-radius: 20px;
+    padding: 2rem;
+    box-shadow: var(--shadow);
+    border: 1px solid rgba(255, 255, 255, 0.2);
+    margin-bottom: 3rem;
+    display: flex;
+    gap: 2rem;
+    align-items: center;
+    flex-wrap: wrap;
+}
+
+.control-group {
+    display: flex;
+    flex-direction: column;
+    gap: 0.75rem;
+}
+
+.control-group label {
+    font-weight: 700;
+    font-size: 0.875rem;
+    color: var(--dark-color);
+    text-transform: uppercase;
+    letter-spacing: 0.5px;
+}
+
+.form-select {
+    border-radius: 16px;
+    border: 2px solid var(--border-color);
+    padding: 1rem 1.25rem;
+    font-size: 1rem;
+    font-weight: 500;
+    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+    min-width: 250px;
+    background: white;
+    box-shadow: var(--shadow-sm);
+}
+
+.form-select:focus {
+    border-color: var(--primary-color);
+    box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
+    outline: none;
+    transform: translateY(-1px);
+}
+
+.summary-cards {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+    gap: 2rem;
+    margin-bottom: 3rem;
+}
+
+.summary-card {
+    background: rgba(255, 255, 255, 0.9);
+    backdrop-filter: blur(10px);
+    border-radius: 24px;
+    padding: 2.5rem;
+    box-shadow: var(--shadow);
+    border: 1px solid rgba(255, 255, 255, 0.2);
+    text-align: center;
+    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+    position: relative;
+    overflow: hidden;
+}
+
+.summary-card::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 4px;
+    transition: all 0.3s ease;
+}
+
+.summary-card.income::before {
+    background: var(--gradient-success);
+}
+
+.summary-card.expense::before {
+    background: var(--gradient-danger);
+}
+
+.summary-card.delta::before {
+    background: var(--gradient-primary);
+}
+
+.summary-card.delta.negative::before {
+    background: var(--gradient-danger);
+}
+
+.summary-card:hover {
+    transform: translateY(-8px);
+    box-shadow: var(--shadow-lg);
+}
+
+.summary-card h3 {
+    font-size: 0.875rem;
+    font-weight: 700;
+    color: var(--secondary-color);
+    text-transform: uppercase;
+    letter-spacing: 1px;
+    margin-bottom: 1rem;
+}
+
+.summary-card .value {
+    font-size: 2.5rem;
+    font-weight: 900;
+    margin-bottom: 0.5rem;
+    line-height: 1;
+}
+
+.summary-card.income .value {
+    background: var(--gradient-success);
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    background-clip: text;
+}
+
+.summary-card.expense .value {
+    background: var(--gradient-danger);
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    background-clip: text;
+}
+
+.summary-card.delta .value {
+    background: var(--gradient-primary);
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    background-clip: text;
+}
+
+.summary-card.delta.negative .value {
+    background: var(--gradient-danger);
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    background-clip: text;
+}
+
+.chart-row {
+    width: 100%;
+    margin-bottom: 3rem;
+}
+
+.chart-card {
+    background: rgba(255, 255, 255, 0.9);
+    backdrop-filter: blur(10px);
+    border-radius: 24px;
+    box-shadow: var(--shadow);
+    border: 1px solid rgba(255, 255, 255, 0.2);
+    overflow: hidden;
+    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.chart-card:hover {
+    box-shadow: var(--shadow-lg);
+    transform: translateY(-2px);
+}
+
+.chart-header {
+    padding: 2rem 2.5rem 1.5rem;
+    border-bottom: 1px solid var(--border-light);
+    background: linear-gradient(135deg, rgba(248, 250, 252, 0.8) 0%, rgba(255, 255, 255, 0.9) 100%);
+}
+
+.chart-title {
+    font-size: 1.5rem;
+    font-weight: 700;
+    color: var(--dark-color);
+    margin: 0;
+}
+
+.chart-body {
+    padding: 2.5rem;
+}
+
+.chart-container {
+    position: relative;
+    height: 450px !important;
+    width: 90%;
+    border-radius: 16px;
+    overflow: hidden;
+}
+
+.chart-container canvas {
+    max-height: 450px !important;
+    height: 450px !important;
+}
+
+.course-controls {
+    background: linear-gradient(135deg, rgba(248, 250, 252, 0.8) 0%, rgba(255, 255, 255, 0.9) 100%);
+    border-radius: 20px;
+    padding: 2rem;
+    margin-bottom: 2rem;
+    border: 1px solid var(--border-light);
+}
+
+.legend-container {
+    display: flex;
+    gap: 2rem;
+    margin-top: 1.5rem;
+    flex-wrap: wrap;
+}
+
+.legend-item {
+    display: flex;
+    align-items: center;
+    gap: 0.75rem;
+    font-size: 0.9rem;
+    font-weight: 600;
+    color: var(--dark-color);
+}
+
+.legend-color {
+    width: 16px;
+    height: 16px;
+    border-radius: 50%;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.monthly-table-container {
+    background: #f8f9fa;
+    border-radius: 12px;
+    box-shadow: var(--shadow-sm);
+}
+
+.members-table-container {
+    background: #f8f9fa;
+    border-radius: 12px;
+    box-shadow: var(--shadow-sm);
+}
+
+.monthly-table,
+.members-table {
+    font-size: 0.775rem;
+}
+
+.members-table .table-header {
+    display: grid;
+    grid-template-columns: 1.2fr 0.8fr 1fr;
+    gap: 0.5rem;
+    margin-bottom: 0.5rem;
+    padding-bottom: 0.5rem;
+    border-bottom: 2px solid var(--border-color);
+}
+
+.members-table .table-row {
+    display: grid;
+    grid-template-columns: 1.2fr 0.8fr 1fr;
+    border-bottom: 1px solid #e9ecef;
+    transition: background-color 0.2s ease;
+}
+
+.table-cell.season-name {
+    text-align: left;
+    font-weight: 500;
+    font-size: 0.7rem;
+}
+
+.table-cell.members-count {
+    text-align: center;
+    font-weight: 600;
+    color: var(--primary-color);
+}
+
+.table-cell.variation {
+    text-align: right;
+    font-size: 0.7rem;
+}
+
+.variation-positive {
+    color: var(--success-color);
+    font-weight: 600;
+}
+
+.variation-negative {
+    color: var(--danger-color);
+    font-weight: 600;
+}
+
+.variation-neutral {
+    color: var(--secondary-color);
+    font-weight: 500;
+}
+
+.monthly-table {
+    font-size: 0.775rem;
+}
+
+.table-header {
+    display: grid;
+    grid-template-columns: 1fr 1fr 1fr 1fr;
+    gap: 0.5rem;
+    margin-bottom: 0.5rem;
+    padding-bottom: 0.5rem;
+    border-bottom: 2px solid var(--border-color);
+}
+
+.table-row {
+    display: grid;
+    grid-template-columns: 1fr 1fr 1fr 1fr;
+    gap: 0.5rem;
+    padding: 0.2rem 0;
+    border-bottom: 1px solid #e9ecef;
+    transition: background-color 0.2s ease;
+}
+
+.table-row:hover {
+    background-color: rgba(0, 0, 0, 0.02);
+}
+
+.table-row.positive {
+    background-color: rgba(0, 184, 148, 0.05);
+}
+
+.table-row.negative {
+    background-color: rgba(255, 107, 107, 0.05);
+}
+
+.table-row.neutral {
+    background-color: rgba(73, 80, 87, 0.02);
+}
+
+.table-cell {
+    padding: 0.25rem 0.5rem;
+    text-align: right;
+}
+
+.table-header .table-cell {
+    font-weight: 600;
+    color: var(--secondary-color);
+    text-align: center;
+    text-transform: uppercase;
+    font-size: 0.75rem;
+    letter-spacing: 0.5px;
+}
+
+.table-cell.month-name {
+    text-align: left;
+    font-weight: 600;
+}
+
+.table-cell.income {
+    color: var(--success-color);
+    font-weight: 500;
+}
+
+.table-cell.expense {
+    color: var(--danger-color);
+    font-weight: 500;
+}
+
+.table-cell.net {
+    font-weight: 600;
+}
+
+.table-row.positive .table-cell.net {
+    color: var(--success-color);
+}
+
+.table-row.negative .table-cell.net {
+    color: var(--danger-color);
+}
+
+.table-row.neutral .table-cell.net {
+    color: var(--secondary-color);
+}
+
+@media (max-width: 1024px) {
+    .chart-body>div[style*="grid-template-columns"] {
+        grid-template-columns: 1fr !important;
+        gap: 1rem !important;
+    }
+
+    .monthly-table-container {
+        order: 2;
+    }
+}
+
+.loading {
+    text-align: center;
+    padding: 2rem;
+    color: var(--secondary-color);
+}
+
+.error-message {
+    background: #fff5f5;
+    border: 1px solid #fed7d7;
+    color: #c53030;
+    padding: 1rem;
+    border-radius: 8px;
+    margin: 1rem 0;
+}
+
+@media (max-width: 768px) {
+    .dashboard-container {
+        padding: 0.5rem;
+    }
+
+    .dashboard-header h1 {
+        font-size: 2rem;
+    }
+
+    .controls-section {
+        flex-direction: column;
+        align-items: stretch;
+        gap: 1rem;
+    }
+
+    .chart-body {
+        padding: 1rem;
+    }
+
+    .chart-container {
+        height: 300px !important;
+    }
+
+    .chart-container canvas {
+        max-height: 300px !important;
+        height: 300px !important;
+    }
+
+    .legend-container {
+        gap: 1rem;
+    }
+}

+ 557 - 808
resources/views/livewire/reports.blade.php

@@ -1,413 +1,9 @@
 {{-- resources/views/livewire/reports.blade.php --}}
 <div>
     <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
-    <style>
-        :root {
-            --primary-color: #3b5bdb;
-            --secondary-color: #495057;
-            --success-color: #00b894;
-            --info-color: #22b8cf;
-            --warning-color: #ffd43b;
-            --danger-color: #ff6b6b;
-            --dark-color: #212529;
-            --border-color: #e9ecef;
-            --shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
-            --shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
-        }
-
-        .dashboard-container {
-            padding: 1rem;
-            max-width: 1400px;
-            margin: 0 auto;
-        }
-
-        .dashboard-header {
-            text-align: center;
-            margin-bottom: 2rem;
-            padding: 2rem;
-            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-            border-radius: 16px;
-            color: white;
-        }
-
-        .dashboard-header h1 {
-            font-size: 2.5rem;
-            font-weight: 700;
-            margin-bottom: 0.5rem;
-        }
-
-        .dashboard-header p {
-            font-size: 1.1rem;
-            opacity: 0.9;
-        }
-
-        .controls-section {
-            background: white;
-            border-radius: 12px;
-            padding: 1.5rem;
-            box-shadow: var(--shadow-sm);
-            border: 1px solid var(--border-color);
-            margin-bottom: 2rem;
-            display: flex;
-            gap: 2rem;
-            align-items: center;
-            flex-wrap: wrap;
-        }
-
-        .control-group {
-            display: flex;
-            flex-direction: column;
-            gap: 0.5rem;
-        }
-
-        .control-group label {
-            font-weight: 600;
-            font-size: 0.875rem;
-            color: var(--secondary-color);
-        }
-
-        .form-select {
-            border-radius: 8px;
-            border: 2px solid var(--border-color);
-            padding: 0.75rem 1rem;
-            font-size: 1rem;
-            transition: all 0.3s ease;
-            min-width: 200px;
-        }
-
-        .form-select:focus {
-            border-color: var(--primary-color);
-            box-shadow: 0 0 0 0.2rem rgba(59, 91, 219, 0.25);
-            outline: none;
-        }
-
-        .summary-cards {
-            display: grid;
-            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
-            gap: 1rem;
-            margin-bottom: 2rem;
-        }
-
-        .summary-card {
-            background: white;
-            border-radius: 16px;
-            padding: 1.5rem;
-            box-shadow: var(--shadow-sm);
-            border: 1px solid var(--border-color);
-            text-align: center;
-            transition: transform 0.3s ease;
-        }
-
-        .summary-card:hover {
-            transform: translateY(-2px);
-        }
-
-        .summary-card h3 {
-            font-size: 0.875rem;
-            font-weight: 600;
-            color: var(--secondary-color);
-            text-transform: uppercase;
-            letter-spacing: 0.5px;
-            margin-bottom: 0.5rem;
-        }
-
-        .summary-card .value {
-            font-size: 2rem;
-            font-weight: 700;
-            margin-bottom: 0.25rem;
-        }
-
-        .summary-card.income .value { color: var(--success-color); }
-        .summary-card.expense .value { color: var(--danger-color); }
-        .summary-card.delta .value { color: var(--primary-color); }
-        .summary-card.delta.negative .value { color: var(--danger-color); }
-
-        .chart-row {
-            width: 100%;
-            margin-bottom: 2rem;
-        }
-
-        .chart-card {
-            background: white;
-            border-radius: 16px;
-            box-shadow: var(--shadow-sm);
-            border: 1px solid var(--border-color);
-            overflow: hidden;
-            transition: all 0.3s ease;
-        }
-
-        .chart-card:hover {
-            box-shadow: var(--shadow);
-        }
-
-        .chart-header {
-            padding: 1.5rem 2rem 1rem;
-            border-bottom: 1px solid var(--border-color);
-            background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
-        }
-
-        .chart-title {
-            font-size: 1.25rem;
-            font-weight: 600;
-            color: var(--dark-color);
-            margin: 0;
-        }
-
-        .chart-body {
-            padding: 2rem;
-        }
-
-        .chart-container {
-            position: relative;
-            height: 400px !important;
-            width: 100%;
-        }
-
-        .chart-container canvas {
-            max-height: 400px !important;
-            height: 400px !important;
-        }
-
-        .course-controls {
-            background: #f8f9fa;
-            border-radius: 12px;
-            padding: 1.5rem;
-            margin-bottom: 1.5rem;
-        }
-
-        .legend-container {
-            display: flex;
-            gap: 2rem;
-            margin-top: 1rem;
-            flex-wrap: wrap;
-        }
-
-        .legend-item {
-            display: flex;
-            align-items: center;
-            gap: 0.5rem;
-            font-size: 0.875rem;
-        }
-
-        .legend-color {
-            width: 12px;
-            height: 12px;
-            border-radius: 50%;
-        }
-
-        .monthly-table-container {
-            background: #f8f9fa;
-            border-radius: 12px;
-            padding: 1.5rem;
-            box-shadow: var(--shadow-sm);
-        }
-
-        .members-table-container {
-            background: #f8f9fa;
-            border-radius: 12px;
-            padding: 1.5rem;
-            box-shadow: var(--shadow-sm);
-        }
-
-        .monthly-table, .members-table {
-            font-size: 0.875rem;
-        }
-
-        .members-table .table-header {
-            display: grid;
-            grid-template-columns: 1.2fr 0.8fr 1fr;
-            gap: 0.5rem;
-            margin-bottom: 0.5rem;
-            padding-bottom: 0.5rem;
-            border-bottom: 2px solid var(--border-color);
-        }
-
-        .members-table .table-row {
-            display: grid;
-            grid-template-columns: 1.2fr 0.8fr 1fr;
-            gap: 0.5rem;
-            padding: 0.5rem 0;
-            border-bottom: 1px solid #e9ecef;
-            transition: background-color 0.2s ease;
-        }
-
-        .table-cell.season-name {
-            text-align: left;
-            font-weight: 600;
-            font-size: 0.8rem;
-        }
-
-        .table-cell.members-count {
-            text-align: center;
-            font-weight: 600;
-            color: var(--primary-color);
-        }
-
-        .table-cell.variation {
-            text-align: right;
-            font-size: 0.8rem;
-        }
-
-        .variation-positive {
-            color: var(--success-color);
-            font-weight: 600;
-        }
-
-        .variation-negative {
-            color: var(--danger-color);
-            font-weight: 600;
-        }
-
-        .variation-neutral {
-            color: var(--secondary-color);
-            font-weight: 500;
-        }
-
-        .monthly-table {
-            font-size: 0.875rem;
-        }
-
-        .table-header {
-            display: grid;
-            grid-template-columns: 1fr 1fr 1fr 1fr;
-            gap: 0.5rem;
-            margin-bottom: 0.5rem;
-            padding-bottom: 0.5rem;
-            border-bottom: 2px solid var(--border-color);
-        }
-
-        .table-row {
-            display: grid;
-            grid-template-columns: 1fr 1fr 1fr 1fr;
-            gap: 0.5rem;
-            padding: 0.5rem 0;
-            border-bottom: 1px solid #e9ecef;
-            transition: background-color 0.2s ease;
-        }
-
-        .table-row:hover {
-            background-color: rgba(0, 0, 0, 0.02);
-        }
-
-        .table-row.positive {
-            background-color: rgba(0, 184, 148, 0.05);
-        }
-
-        .table-row.negative {
-            background-color: rgba(255, 107, 107, 0.05);
-        }
-
-        .table-row.neutral {
-            background-color: rgba(73, 80, 87, 0.02);
-        }
-
-        .table-cell {
-            padding: 0.25rem 0.5rem;
-            text-align: right;
-        }
-
-        .table-header .table-cell {
-            font-weight: 600;
-            color: var(--secondary-color);
-            text-align: center;
-            text-transform: uppercase;
-            font-size: 0.75rem;
-            letter-spacing: 0.5px;
-        }
-
-        .table-cell.month-name {
-            text-align: left;
-            font-weight: 600;
-        }
-
-        .table-cell.income {
-            color: var(--success-color);
-            font-weight: 500;
-        }
-
-        .table-cell.expense {
-            color: var(--danger-color);
-            font-weight: 500;
-        }
-
-        .table-cell.net {
-            font-weight: 600;
-        }
-
-        .table-row.positive .table-cell.net {
-            color: var(--success-color);
-        }
-
-        .table-row.negative .table-cell.net {
-            color: var(--danger-color);
-        }
-
-        .table-row.neutral .table-cell.net {
-            color: var(--secondary-color);
-        }
-
-        @media (max-width: 1024px) {
-            .chart-body > div[style*="grid-template-columns"] {
-                grid-template-columns: 1fr !important;
-                gap: 1rem !important;
-            }
-
-            .monthly-table-container {
-                order: 2;
-            }
-        }
-
-        .loading {
-            text-align: center;
-            padding: 2rem;
-            color: var(--secondary-color);
-        }
-
-        .error-message {
-            background: #fff5f5;
-            border: 1px solid #fed7d7;
-            color: #c53030;
-            padding: 1rem;
-            border-radius: 8px;
-            margin: 1rem 0;
-        }
-
-        @media (max-width: 768px) {
-            .dashboard-container {
-                padding: 0.5rem;
-            }
-
-            .dashboard-header h1 {
-                font-size: 2rem;
-            }
-
-            .controls-section {
-                flex-direction: column;
-                align-items: stretch;
-                gap: 1rem;
-            }
-
-            .chart-body {
-                padding: 1rem;
-            }
-
-            .chart-container {
-                height: 300px !important;
-            }
-
-            .chart-container canvas {
-                max-height: 300px !important;
-                height: 300px !important;
-            }
-
-            .legend-container {
-                gap: 1rem;
-            }
-        }
-    </style>
+    <link rel="stylesheet" href="{{ asset('css/chart-reports.css') }}">
 
     <div class="dashboard-container">
-
         <div class="controls-section">
             <div class="control-group">
                 <label for="season-filter">Stagione di Riferimento:</label>
@@ -437,49 +33,20 @@
             </div>
         </div>
 
-        <!-- Charts Section - Force complete re-render with wire:key -->
-        <div wire:key="season-{{ $seasonFilter }}-course-{{ $selectedCourse ?? 'none' }}">
+        <!-- Main Charts Section - Protected with wire:ignore -->
+        <div wire:ignore>
             <div class="chart-row">
                 <div class="chart-card">
                     <div class="chart-header">
-                        <h3 class="chart-title">Entrate e Uscite Mensili - {{ $seasonFilter }}</h3>
+                        <h3 class="chart-title">Entrate e Uscite Mensili - <span
+                                id="monthly-season-title">{{ $seasonFilter }}</span></h3>
                     </div>
                     <div class="chart-body">
-                        <div style="display: grid; grid-template-columns: 1fr 300px; gap: 2rem; align-items: start;">
+                        <div style="display: grid; grid-template-columns: 1fr 300px; align-items: start;">
                             <div class="chart-container">
                                 <canvas id="monthly-chart-{{ str_replace('-', '', $seasonFilter) }}"></canvas>
                             </div>
-
-                            @php
-                                $monthlyTotals = $this->getMonthlyTotals();
-                                $incomeData = $monthlyTotals['datasets'][0]['data'];
-                                $expenseData = $monthlyTotals['datasets'][1]['data'];
-                                $monthNames = $monthlyTotals['labels'];
-                            @endphp
-
-                            <div class="monthly-table-container">
-                                <h4 style="margin-bottom: 1rem; font-size: 1rem; font-weight: 600; color: var(--dark-color);">Riepilogo Mensile</h4>
-                                <div class="monthly-table">
-                                    <div class="table-header">
-                                        <div class="table-cell">Mese</div>
-                                        <div class="table-cell">Entrate</div>
-                                        <div class="table-cell">Uscite</div>
-                                        <div class="table-cell">Netto</div>
-                                    </div>
-                                    @foreach($monthNames as $index => $month)
-                                        @php
-                                            $income = floatval($incomeData[$index] ?? 0);
-                                            $expense = floatval($expenseData[$index] ?? 0);
-                                            $net = $income - $expense;
-                                        @endphp
-                                        <div class="table-row {{ $net < 0 ? 'negative' : ($net > 0 ? 'positive' : 'neutral') }}">
-                                            <div class="table-cell month-name">{{ $month }}</div>
-                                            <div class="table-cell income">€{{ number_format($income, 0, ',', '.') }}</div>
-                                            <div class="table-cell expense">€{{ number_format($expense, 0, ',', '.') }}</div>
-                                            <div class="table-cell net">€{{ number_format($net, 0, ',', '.') }}</div>
-                                        </div>
-                                    @endforeach
-                                </div>
+                            <div class="monthly-table-container" id="monthly-table">
                             </div>
                         </div>
                     </div>
@@ -489,7 +56,8 @@
             <div class="chart-row">
                 <div class="chart-card">
                     <div class="chart-header">
-                        <h3 class="chart-title">Causali Performanti - {{ $seasonFilter }}</h3>
+                        <h3 class="chart-title">Causali Performanti - <span
+                                id="causals-season-title">{{ $seasonFilter }}</span></h3>
                     </div>
                     <div class="chart-body">
                         <div class="chart-container">
@@ -505,409 +73,590 @@
                         <h3 class="chart-title">Tesserati per Stagione</h3>
                     </div>
                     <div class="chart-body">
-                        <div style="display: grid; grid-template-columns: 1fr 300px; gap: 2rem; align-items: start;">
+                        <div style="display: grid; grid-template-columns: 1fr 300px; gap: 1rem; align-items: start;">
                             <div class="chart-container">
                                 <canvas id="members-chart-{{ str_replace('-', '', $seasonFilter) }}"></canvas>
                             </div>
-
-                            @php
-                                $membersData = $this->getTesseratiData();
-                                $seasonLabels = $membersData['labels'];
-                                $memberCounts = $membersData['datasets'][0]['data'];
-                            @endphp
-
-                            <div class="members-table-container">
-                                <h4 style="margin-bottom: 1rem; font-size: 1rem; font-weight: 600; color: var(--dark-color);">Riepilogo Tesserati</h4>
-                                <div class="members-table">
-                                    <div class="table-header">
-                                        <div class="table-cell">Stagione</div>
-                                        <div class="table-cell">Tesserati</div>
-                                        <div class="table-cell">Variazione</div>
-                                    </div>
-                                    @foreach($seasonLabels as $index => $season)
-                                        @php
-                                            $current = intval($memberCounts[$index] ?? 0);
-                                            $previous = $index > 0 ? intval($memberCounts[$index - 1] ?? 0) : 0;
-                                            $variation = $index > 0 ? $current - $previous : 0;
-                                            $variationPercent = $previous > 0 ? round(($variation / $previous) * 100, 1) : 0;
-                                        @endphp
-                                        <div class="table-row {{ $variation > 0 ? 'positive' : ($variation < 0 ? 'negative' : 'neutral') }}">
-                                            <div class="table-cell season-name">{{ $season }}</div>
-                                            <div class="table-cell members-count">{{ number_format($current, 0, ',', '.') }}</div>
-                                            <div class="table-cell variation">
-                                                @if($index > 0)
-                                                    @if($variation > 0)
-                                                        <span class="variation-positive">+{{ $variation }} (+{{ $variationPercent }}%)</span>
-                                                    @elseif($variation < 0)
-                                                        <span class="variation-negative">{{ $variation }} ({{ $variationPercent }}%)</span>
-                                                    @else
-                                                        <span class="variation-neutral">{{ $variation }}</span>
-                                                    @endif
-                                                @else
-                                                    <span class="variation-neutral">—</span>
-                                                @endif
-                                            </div>
-                                        </div>
-                                    @endforeach
-                                </div>
+                            <div class="members-table-container" id="members-table">
                             </div>
                         </div>
                     </div>
                 </div>
             </div>
+        </div>
 
-            <div class="chart-row">
-                <div class="chart-card">
-                    <div class="chart-header">
-                        <h3 class="chart-title">Analisi Corsi</h3>
-                    </div>
-                    <div class="chart-body">
-                        <div class="course-controls">
-                            <div class="control-group">
-                                <label>Seleziona Corso:</label>
-                                <select class="form-select" wire:model="selectedCourse" wire:change="updateCourseChart">
-                                    <option value="">Seleziona un Corso</option>
-                                    @foreach($courses as $course)
-                                        <option value="{{ $course['id'] }}">{{ $course['full_name'] }}</option>
-                                    @endforeach
-                                </select>
+        <div class="chart-row">
+            <div class="chart-card">
+                <div class="chart-header">
+                    <h3 class="chart-title">Analisi Corsi</h3>
+                </div>
+                <div class="chart-body">
+                    <div class="course-controls">
+                        <div class="control-group">
+                            <label>Seleziona Corso ({{ $seasonFilter }}):</label>
+                            <select class="form-select" wire:model.live="selectedCourse">
+                                <option value="">Seleziona un Corso</option>
+                                @foreach($this->getCoursesForSelect() as $course)
+                                    <option value="{{ $course['id'] }}">{{ $course['full_name'] }}</option>
+                                @endforeach
+                            </select>
+                        </div>
+                        <div class="legend-container">
+                            <div class="legend-item">
+                                <div class="legend-color" style="background: rgba(0, 184, 148, 1);"></div>
+                                <span>Pagamenti Effettuati</span>
                             </div>
-                            <div class="legend-container">
-                                <div class="legend-item">
-                                    <div class="legend-color" style="background: rgba(0, 184, 148, 1);"></div>
-                                    <span>Pagamenti Effettuati</span>
-                                </div>
-                                <div class="legend-item">
-                                    <div class="legend-color" style="background: rgba(48, 51, 107, 1);"></div>
-                                    <span>Pagamenti Attesi</span>
-                                </div>
+                            <div class="legend-item">
+                                <div class="legend-color" style="background: rgba(48, 51, 107, 1);"></div>
+                                <span>Pagamenti Attesi</span>
                             </div>
                         </div>
-                        <div class="chart-container">
-                            <canvas id="courses-chart-{{ str_replace('-', '', $seasonFilter) }}-{{ $selectedCourse ?? 'none' }}"></canvas>
-                        </div>
                     </div>
+
+                    @if($selectedCourse)
+                        <div wire:ignore wire:key="course-chart-{{ $seasonFilter }}-{{ $selectedCourse }}">
+                            <div class="chart-container">
+                                <canvas
+                                    id="courses-chart-{{ str_replace('-', '', $seasonFilter) }}-{{ $selectedCourse }}"></canvas>
+                            </div>
+                        </div>
+                    @else
+                        <div class="chart-container"
+                            style="display: flex; align-items: center; justify-content: center; min-height: 400px; color: var(--secondary-color);">
+                            <p style="font-size: 1.1rem;">Seleziona un corso per visualizzare il grafico</p>
+                        </div>
+                    @endif
                 </div>
             </div>
+        </div>
+    </div>
 
-            <script>
-                (function() {
-                    const seasonFilter = '{{ $seasonFilter }}';
-                    const selectedCourse = '{{ $selectedCourse ?? 'none' }}';
-                    const seasonKey = '{{ str_replace('-', '', $seasonFilter) }}';
-
-                    console.log('Creating charts for season:', seasonFilter);
-
-                    // Get the data fresh from PHP
-                    const monthlyData = @json($this->getMonthlyTotals());
-                    const causalsData = @json($this->getTopCausalsByAmount());
-                    const membersData = @json($this->getTesseratiData());
-                    const courseData = @json($this->getCourseMonthlyEarnings());
-
-                    console.log('Monthly data for', seasonFilter, ':', monthlyData);
-                    console.log('May value should be:', monthlyData.datasets[0].data[8]);
-
-                    // Wait for DOM to be ready
-                    if (document.readyState === 'loading') {
-                        document.addEventListener('DOMContentLoaded', initCharts);
-                    } else {
-                        setTimeout(initCharts, 100);
-                    }
-
-                    function initCharts() {
-                        createMonthlyChart();
-                        createCausalsChart();
-                        createMembersChart();
-                        createCoursesChart();
-                    }
-
-                    function createMonthlyChart() {
-                        const canvasId = `monthly-chart-${seasonKey}`;
-                        const canvas = document.getElementById(canvasId);
-                        if (!canvas) {
-                            console.log('Canvas not found:', canvasId);
-                            return;
-                        }
-
-                        const ctx = canvas.getContext('2d');
-
-                        const incomeGradient = ctx.createLinearGradient(0, 0, 0, 400);
-                        incomeGradient.addColorStop(0, 'rgba(0, 184, 148, 0.8)');
-                        incomeGradient.addColorStop(1, 'rgba(0, 184, 148, 0.2)');
-
-                        const expenseGradient = ctx.createLinearGradient(0, 0, 0, 400);
-                        expenseGradient.addColorStop(0, 'rgba(255, 107, 107, 0.8)');
-                        expenseGradient.addColorStop(1, 'rgba(255, 107, 107, 0.2)');
-
-                        new Chart(ctx, {
-                            type: 'bar',
-                            data: {
-                                labels: monthlyData.labels,
-                                datasets: [
-                                    {
-                                        label: 'Entrate',
-                                        data: monthlyData.datasets[0].data,
-                                        backgroundColor: incomeGradient,
-                                        borderColor: '#00b894',
-                                        borderWidth: 2,
-                                    },
-                                    {
-                                        label: 'Uscite',
-                                        data: monthlyData.datasets[1].data,
-                                        backgroundColor: expenseGradient,
-                                        borderColor: '#ff6b6b',
-                                        borderWidth: 2,
-                                    }
-                                ]
+    <!-- Single JavaScript section -->
+    <script>
+        // Global chart manager
+        window.ReportsChartManager = window.ReportsChartManager || {
+            charts: {},
+            currentSeason: null,
+
+            destroyChart: function (chartId) {
+                if (this.charts[chartId]) {
+                    this.charts[chartId].destroy();
+                    delete this.charts[chartId];
+                }
+            },
+
+            destroyAllCharts: function () {
+                Object.keys(this.charts).forEach(chartId => {
+                    this.destroyChart(chartId);
+                });
+            },
+
+            updateMainCharts: function () {
+                const seasonFilter = '{{ $seasonFilter }}';
+                const seasonKey = '{{ str_replace('-', '', $seasonFilter) }}';
+
+                // Only update if season changed
+                if (this.currentSeason === seasonFilter) {
+                    return;
+                }
+
+                this.currentSeason = seasonFilter;
+
+                // Get fresh data
+                const monthlyData = @json($this->getMonthlyTotals());
+                const causalsData = @json($this->getTopCausalsByAmount());
+                const membersData = @json($this->getTesseratiData());
+
+                // Update titles
+                document.getElementById('monthly-season-title').textContent = seasonFilter;
+                document.getElementById('causals-season-title').textContent = seasonFilter;
+
+                // Create/update charts
+                this.createMonthlyChart(seasonKey, monthlyData);
+                this.createCausalsChart(seasonKey, causalsData);
+                this.createMembersChart(seasonKey, membersData);
+
+                // Update tables
+                this.updateMonthlyTable(monthlyData);
+                this.updateMembersTable(membersData);
+            },
+
+            createMonthlyChart: function (seasonKey, monthlyData) {
+                const chartId = `monthly-chart-${seasonKey}`;
+                const canvas = document.getElementById(chartId);
+                if (!canvas) return;
+
+                this.destroyChart(chartId);
+
+                const ctx = canvas.getContext('2d');
+
+                const incomeGradient = ctx.createLinearGradient(0, 0, 0, 400);
+                incomeGradient.addColorStop(0, 'rgba(0, 184, 148, 0.8)');
+                incomeGradient.addColorStop(1, 'rgba(0, 184, 148, 0.2)');
+
+                const expenseGradient = ctx.createLinearGradient(0, 0, 0, 400);
+                expenseGradient.addColorStop(0, 'rgba(255, 107, 107, 0.8)');
+                expenseGradient.addColorStop(1, 'rgba(255, 107, 107, 0.2)');
+
+                this.charts[chartId] = new Chart(ctx, {
+                    type: 'bar',
+                    data: {
+                        labels: monthlyData.labels,
+                        datasets: [
+                            {
+                                label: 'Entrate',
+                                data: monthlyData.datasets[0].data,
+                                backgroundColor: incomeGradient,
+                                borderColor: '#00b894',
+                                borderWidth: 2,
                             },
-                            options: {
-                                responsive: true,
-                                maintainAspectRatio: false,
-                                plugins: {
-                                    legend: {
-                                        position: 'top',
-                                        labels: {
-                                            usePointStyle: true,
-                                            padding: 20,
-                                            font: { weight: '500' }
-                                        }
-                                    },
-                                    tooltip: {
-                                        backgroundColor: 'rgba(255, 255, 255, 0.95)',
-                                        titleColor: '#212529',
-                                        bodyColor: '#495057',
-                                        borderColor: '#e9ecef',
-                                        borderWidth: 1,
-                                        cornerRadius: 8,
-                                        callbacks: {
-                                            label: function(context) {
-                                                return context.dataset.label + ': €' +
-                                                       new Intl.NumberFormat('it-IT').format(context.parsed.y);
-                                            }
-                                        }
-                                    }
-                                },
-                                scales: {
-                                    x: {
-                                        grid: { display: false },
-                                        ticks: { font: { weight: '500' } }
-                                    },
-                                    y: {
-                                        beginAtZero: true,
-                                        grid: { color: 'rgba(0, 0, 0, 0.05)' },
-                                        ticks: {
-                                            callback: function(value) {
-                                                return '€' + new Intl.NumberFormat('it-IT').format(value);
-                                            }
-                                        }
+                            {
+                                label: 'Uscite',
+                                data: monthlyData.datasets[1].data,
+                                backgroundColor: expenseGradient,
+                                borderColor: '#ff6b6b',
+                                borderWidth: 2,
+                            }
+                        ]
+                    },
+                    options: {
+                        responsive: true,
+                        maintainAspectRatio: false,
+                        plugins: {
+                            legend: {
+                                position: 'top',
+                                labels: {
+                                    usePointStyle: true,
+                                    padding: 20,
+                                    font: { weight: '500' }
+                                }
+                            },
+                            tooltip: {
+                                backgroundColor: 'rgba(255, 255, 255, 0.95)',
+                                titleColor: '#212529',
+                                bodyColor: '#495057',
+                                borderColor: '#e9ecef',
+                                borderWidth: 1,
+                                cornerRadius: 8,
+                                callbacks: {
+                                    label: function (context) {
+                                        return context.dataset.label + ': €' +
+                                            new Intl.NumberFormat('it-IT').format(context.parsed.y);
                                     }
-                                },
-                                animation: {
-                                    duration: 1000,
-                                    easing: 'easeOutQuart'
                                 }
                             }
-                        });
-
-                        console.log('Monthly chart created for', seasonFilter);
-                    }
-
-                    function createCausalsChart() {
-                        const canvasId = `causals-chart-${seasonKey}`;
-                        const canvas = document.getElementById(canvasId);
-                        if (!canvas) return;
-
-                        const ctx = canvas.getContext('2d');
-
-                        const colors = [
-                            'rgba(59, 91, 219, 0.8)',
-                            'rgba(0, 184, 148, 0.8)',
-                            'rgba(34, 184, 207, 0.8)',
-                            'rgba(255, 212, 59, 0.8)',
-                            'rgba(255, 107, 107, 0.8)',
-                            'rgba(142, 68, 173, 0.8)',
-                            'rgba(230, 126, 34, 0.8)',
-                            'rgba(149, 165, 166, 0.8)',
-                            'rgba(241, 196, 15, 0.8)',
-                            'rgba(231, 76, 60, 0.8)'
-                        ];
-
-                        new Chart(ctx, {
-                            type: 'doughnut',
-                            data: {
-                                labels: causalsData.inLabels,
-                                datasets: [{
-                                    label: 'Importo',
-                                    data: causalsData.inData.map(item => item.value),
-                                    backgroundColor: colors,
-                                    borderColor: colors.map(color => color.replace('0.8', '1')),
-                                    borderWidth: 2,
-                                    hoverOffset: 8
-                                }]
+                        },
+                        scales: {
+                            x: {
+                                grid: { display: false },
+                                ticks: { font: { weight: '500' } }
                             },
-                            options: {
-                                responsive: true,
-                                maintainAspectRatio: false,
-                                cutout: '60%',
-                                plugins: {
-                                    legend: {
-                                        position: 'left',
-                                        labels: {
-                                            usePointStyle: true,
-                                            padding: 15,
-                                            font: { size: 11, weight: '500' }
-                                        }
-                                    },
-                                    tooltip: {
-                                        backgroundColor: 'rgba(255, 255, 255, 0.95)',
-                                        titleColor: '#212529',
-                                        bodyColor: '#495057',
-                                        borderColor: '#e9ecef',
-                                        borderWidth: 1,
-                                        cornerRadius: 8,
-                                        callbacks: {
-                                            label: function(context) {
-                                                const value = context.raw;
-                                                const total = context.dataset.data.reduce((a, b) => a + b, 0);
-                                                const percentage = Math.round((value / total) * 100);
-                                                return context.label + ': €' +
-                                                       new Intl.NumberFormat('it-IT').format(value) +
-                                                       ` (${percentage}%)`;
-                                            }
-                                        }
+                            y: {
+                                beginAtZero: true,
+                                grid: { color: 'rgba(0, 0, 0, 0.05)' },
+                                ticks: {
+                                    callback: function (value) {
+                                        return '€' + new Intl.NumberFormat('it-IT').format(value);
                                     }
-                                },
-                                animation: {
-                                    animateRotate: true,
-                                    duration: 1000
                                 }
                             }
-                        });
+                        },
+                        animation: {
+                            duration: 1000,
+                            easing: 'easeOutQuart'
+                        }
                     }
-
-                    function createMembersChart() {
-                        const canvasId = `members-chart-${seasonKey}`;
-                        const canvas = document.getElementById(canvasId);
-                        if (!canvas) return;
-
-                        const ctx = canvas.getContext('2d');
-
-                        const gradient = ctx.createLinearGradient(0, 0, 0, 400);
-                        gradient.addColorStop(0, 'rgba(59, 91, 219, 0.3)');
-                        gradient.addColorStop(1, 'rgba(59, 91, 219, 0.05)');
-
-                        new Chart(ctx, {
-                            type: 'line',
-                            data: {
-                                labels: membersData.labels,
-                                datasets: [{
-                                    label: 'Membri Tesserati',
-                                    data: membersData.datasets[0].data,
-                                    borderColor: '#3b5bdb',
-                                    backgroundColor: gradient,
-                                    borderWidth: 3,
-                                    fill: true,
-                                    tension: 0.4,
-                                    pointBackgroundColor: '#3b5bdb',
-                                    pointBorderColor: '#ffffff',
-                                    pointBorderWidth: 2,
-                                    pointRadius: 6,
-                                    pointHoverRadius: 8
-                                }]
+                });
+            },
+
+            createCausalsChart: function (seasonKey, causalsData) {
+                const chartId = `causals-chart-${seasonKey}`;
+                const canvas = document.getElementById(chartId);
+                if (!canvas) return;
+
+                this.destroyChart(chartId);
+
+                const ctx = canvas.getContext('2d');
+
+                const colors = [
+                    'rgba(59, 91, 219, 0.8)',
+                    'rgba(0, 184, 148, 0.8)',
+                    'rgba(34, 184, 207, 0.8)',
+                    'rgba(255, 212, 59, 0.8)',
+                    'rgba(255, 107, 107, 0.8)',
+                    'rgba(142, 68, 173, 0.8)',
+                    'rgba(230, 126, 34, 0.8)',
+                    'rgba(149, 165, 166, 0.8)',
+                    'rgba(241, 196, 15, 0.8)',
+                    'rgba(231, 76, 60, 0.8)'
+                ];
+
+                this.charts[chartId] = new Chart(ctx, {
+                    type: 'doughnut',
+                    data: {
+                        labels: causalsData.inLabels,
+                        datasets: [{
+                            label: 'Importo',
+                            data: causalsData.inData.map(item => item.value),
+                            backgroundColor: colors,
+                            borderColor: colors.map(color => color.replace('0.8', '1')),
+                            borderWidth: 2,
+                            hoverOffset: 8
+                        }]
+                    },
+                    options: {
+                        responsive: true,
+                        maintainAspectRatio: false,
+                        cutout: '60%',
+                        plugins: {
+                            legend: {
+                                position: 'left',
+                                labels: {
+                                    usePointStyle: true,
+                                    padding: 15,
+                                    font: { size: 11, weight: '500' }
+                                }
                             },
-                            options: {
-                                responsive: true,
-                                maintainAspectRatio: false,
-                                plugins: {
-                                    legend: { display: false },
-                                    tooltip: {
-                                        backgroundColor: 'rgba(255, 255, 255, 0.95)',
-                                        titleColor: '#212529',
-                                        bodyColor: '#495057',
-                                        borderColor: '#e9ecef',
-                                        borderWidth: 1,
-                                        cornerRadius: 8,
-                                        callbacks: {
-                                            label: function(context) {
-                                                return 'Tesserati: ' + context.parsed.y;
-                                            }
-                                        }
+                            tooltip: {
+                                backgroundColor: 'rgba(255, 255, 255, 0.95)',
+                                titleColor: '#212529',
+                                bodyColor: '#495057',
+                                borderColor: '#e9ecef',
+                                borderWidth: 1,
+                                cornerRadius: 8,
+                                callbacks: {
+                                    label: function (context) {
+                                        const value = context.raw;
+                                        const total = context.dataset.data.reduce((a, b) => a + b, 0);
+                                        const percentage = Math.round((value / total) * 100);
+                                        return context.label + ': €' +
+                                            new Intl.NumberFormat('it-IT').format(value) +
+                                            ` (${percentage}%)`;
                                     }
-                                },
-                                scales: {
-                                    x: { grid: { display: false } },
-                                    y: {
-                                        beginAtZero: true,
-                                        grid: { color: 'rgba(0, 0, 0, 0.05)' },
-                                        ticks: { precision: 0 }
+                                }
+                            }
+                        },
+                        animation: {
+                            animateRotate: true,
+                            duration: 1000
+                        }
+                    }
+                });
+            },
+
+            createMembersChart: function (seasonKey, membersData) {
+                const chartId = `members-chart-${seasonKey}`;
+                const canvas = document.getElementById(chartId);
+                if (!canvas) return;
+
+                this.destroyChart(chartId);
+
+                const ctx = canvas.getContext('2d');
+
+                const gradient = ctx.createLinearGradient(0, 0, 0, 400);
+                gradient.addColorStop(0, 'rgba(59, 91, 219, 0.3)');
+                gradient.addColorStop(1, 'rgba(59, 91, 219, 0.05)');
+
+                this.charts[chartId] = new Chart(ctx, {
+                    type: 'line',
+                    data: {
+                        labels: membersData.labels,
+                        datasets: [{
+                            label: 'Membri Tesserati',
+                            data: membersData.datasets[0].data,
+                            borderColor: '#3b5bdb',
+                            backgroundColor: gradient,
+                            borderWidth: 3,
+                            fill: true,
+                            tension: 0.4,
+                            pointBackgroundColor: '#3b5bdb',
+                            pointBorderColor: '#ffffff',
+                            pointBorderWidth: 2,
+                            pointRadius: 6,
+                            pointHoverRadius: 8
+                        }]
+                    },
+                    options: {
+                        responsive: true,
+                        maintainAspectRatio: false,
+                        plugins: {
+                            legend: { display: false },
+                            tooltip: {
+                                backgroundColor: 'rgba(255, 255, 255, 0.95)',
+                                titleColor: '#212529',
+                                bodyColor: '#495057',
+                                borderColor: '#e9ecef',
+                                borderWidth: 1,
+                                cornerRadius: 8,
+                                callbacks: {
+                                    label: function (context) {
+                                        return 'Tesserati: ' + context.parsed.y;
                                     }
-                                },
-                                animation: {
-                                    duration: 1000,
-                                    easing: 'easeOutQuart'
                                 }
                             }
-                        });
+                        },
+                        scales: {
+                            x: { grid: { display: false } },
+                            y: {
+                                beginAtZero: true,
+                                grid: { color: 'rgba(0, 0, 0, 0.05)' },
+                                ticks: { precision: 0 }
+                            }
+                        },
+                        animation: {
+                            duration: 1000,
+                            easing: 'easeOutQuart'
+                        }
+                    }
+                });
+            },
+
+            updateMonthlyTable: function (monthlyData) {
+                const container = document.getElementById('monthly-table');
+                if (!container) return;
+
+                const incomeData = monthlyData.datasets[0].data;
+                const expenseData = monthlyData.datasets[1].data;
+                const monthNames = monthlyData.labels;
+
+                let tableHtml = `
+                    <div class="monthly-table">
+                        <div class="table-header">
+                            <div class="table-cell">Mese</div>
+                            <div class="table-cell">Entrate</div>
+                            <div class="table-cell">Uscite</div>
+                            <div class="table-cell">Delta</div>
+                        </div>
+                `;
+
+                monthNames.forEach((month, index) => {
+                    const income = parseFloat(incomeData[index] || 0);
+                    const expense = parseFloat(expenseData[index] || 0);
+                    const net = income - expense;
+                    const rowClass = net < 0 ? 'negative' : (net > 0 ? 'positive' : 'neutral');
+
+                    tableHtml += `
+                        <div class="table-row ${rowClass}">
+                            <div class="table-cell month-name">${month}</div>
+                            <div class="table-cell income">€${new Intl.NumberFormat('it-IT').format(income)}</div>
+                            <div class="table-cell expense">€${new Intl.NumberFormat('it-IT').format(expense)}</div>
+                            <div class="table-cell net">€${new Intl.NumberFormat('it-IT').format(net)}</div>
+                        </div>
+                    `;
+                });
+
+                tableHtml += '</div>';
+                container.innerHTML = tableHtml;
+            },
+
+            updateMembersTable: function (membersData) {
+                const container = document.getElementById('members-table');
+                if (!container) return;
+
+                const seasonLabels = membersData.labels;
+                const memberCounts = membersData.datasets[0].data;
+
+                let tableHtml = `
+                    <h4 style="margin-bottom: 1rem; font-size: 1rem; font-weight: 600; color: var(--dark-color);">
+                        Riepilogo Tesserati
+                    </h4>
+                    <div class="members-table">
+                        <div class="table-header">
+                            <div class="table-cell">Stagione</div>
+                            <div class="table-cell">Tesserati</div>
+                            <div class="table-cell">Variazione</div>
+                        </div>
+                `;
+
+                seasonLabels.forEach((season, index) => {
+                    const current = parseInt(memberCounts[index] || 0);
+                    const previous = index > 0 ? parseInt(memberCounts[index - 1] || 0) : 0;
+                    const variation = index > 0 ? current - previous : 0;
+                    const variationPercent = previous > 0 ? Math.round((variation / previous) * 100 * 10) / 10 : 0;
+                    const rowClass = variation > 0 ? 'positive' : (variation < 0 ? 'negative' : 'neutral');
+
+                    let variationText = '—';
+                    if (index > 0) {
+                        if (variation > 0) {
+                            variationText = `<span class="variation-positive">+${variation} (+${variationPercent}%)</span>`;
+                        } else if (variation < 0) {
+                            variationText = `<span class="variation-negative">${variation} (${variationPercent}%)</span>`;
+                        } else {
+                            variationText = `<span class="variation-neutral">${variation}</span>`;
+                        }
                     }
 
-                    function createCoursesChart() {
-                        const canvasId = `courses-chart-${seasonKey}-${selectedCourse}`;
-                        const canvas = document.getElementById(canvasId);
-                        if (!canvas) return;
-
-                        const ctx = canvas.getContext('2d');
-
-                        new Chart(ctx, {
-                            type: 'bar',
-                            data: {
-                                labels: courseData.labels,
-                                datasets: courseData.datasets
+                    tableHtml += `
+                        <div class="table-row ${rowClass}">
+                            <div class="table-cell season-name">${season}</div>
+                            <div class="table-cell members-count">${new Intl.NumberFormat('it-IT').format(current)}</div>
+                            <div class="table-cell variation">${variationText}</div>
+                        </div>
+                    `;
+                });
+
+                tableHtml += '</div>';
+                container.innerHTML = tableHtml;
+            },
+
+            createCourseChart: function () {
+                console.log('Creating course chart...');
+                const seasonFilter = '{{ $seasonFilter }}';
+                const selectedCourse = '{{ $selectedCourse ?? '' }}';
+                const seasonKey = '{{ str_replace('-', '', $seasonFilter) }}';
+                console.log('Selected course:', selectedCourse, 'for season:', seasonFilter);
+
+                // Add this check at the beginning
+                if (!selectedCourse || selectedCourse.trim() === '') {
+                    console.log('No course selected, skipping chart creation');
+                    return;
+                }
+
+                const chartId = `courses-chart-${seasonKey}-${selectedCourse}`;
+                const canvas = document.getElementById(chartId);
+                if (!canvas) return;
+
+                this.destroyChart(chartId);
+
+                const courseData = @json($this->getCourseMonthlyEarnings());
+                const ctx = canvas.getContext('2d');
+                this.charts[chartId] = new Chart(ctx, {
+                    type: 'bar',
+                    data: {
+                        labels: courseData.labels,
+                        datasets: courseData.datasets.map(dataset => {
+                            if (dataset.type === 'line') {
+                                return {
+                                    ...dataset,
+                                    type: 'line',
+                                    fill: false,
+                                    backgroundColor: 'transparent'
+                                };
+                            }
+                            return dataset;
+                        })
+                    },
+                    options: {
+                        responsive: true,
+                        maintainAspectRatio: false,
+                        interaction: {
+                            mode: 'index',
+                            intersect: false,
+                        },
+                        scales: {
+                            x: {
+                                grid: { display: false },
+                                ticks: { font: { weight: '500' } }
                             },
-                            options: {
-                                responsive: true,
-                                maintainAspectRatio: false,
-                                scales: {
-                                    x: { grid: { display: false } },
-                                    y: {
-                                        beginAtZero: true,
-                                        grid: {
-                                            color: 'rgba(0, 0, 0, 0.1)',
-                                            borderDash: [5, 5]
-                                        },
-                                        ticks: {
-                                            callback: function(value) {
-                                                return '€' + new Intl.NumberFormat('it-IT').format(value);
-                                            }
-                                        }
-                                    }
+                            y: {
+                                beginAtZero: true,
+                                grid: {
+                                    color: 'rgba(0, 0, 0, 0.1)',
+                                    borderDash: [5, 5]
                                 },
-                                plugins: {
-                                    legend: { display: false },
-                                    tooltip: {
-                                        backgroundColor: 'rgba(255, 255, 255, 0.95)',
-                                        titleColor: '#212529',
-                                        bodyColor: '#495057',
-                                        borderColor: '#e9ecef',
-                                        borderWidth: 1,
-                                        cornerRadius: 8,
-                                        callbacks: {
-                                            label: function(context) {
-                                                return context.dataset.label + ': €' +
-                                                       new Intl.NumberFormat('it-IT').format(context.parsed.y);
-                                            }
-                                        }
+                                ticks: {
+                                    callback: function (value) {
+                                        return '€' + new Intl.NumberFormat('it-IT').format(value);
+                                    }
+                                }
+                            }
+                        },
+                        plugins: {
+                            legend: {
+                                display: true,
+                                position: 'top',
+                                labels: {
+                                    usePointStyle: true,
+                                    padding: 20,
+                                    font: { weight: '500' }
+                                }
+                            },
+                            tooltip: {
+                                backgroundColor: 'rgba(255, 255, 255, 0.95)',
+                                titleColor: '#212529',
+                                bodyColor: '#495057',
+                                borderColor: '#e9ecef',
+                                borderWidth: 1,
+                                cornerRadius: 8,
+                                callbacks: {
+                                    label: function (context) {
+                                        return context.dataset.label + ': €' +
+                                            new Intl.NumberFormat('it-IT').format(context.parsed.y);
                                     }
-                                },
-                                animation: {
-                                    duration: 1000,
-                                    easing: 'easeOutQuart'
                                 }
                             }
-                        });
+                        },
+                        animation: {
+                            duration: 1000,
+                            easing: 'easeOutQuart'
+                        }
                     }
-                })();
-            </script>
-        </div>
-
-    </div>
+                });
+            },
+           createCourseChartWithValue: function (selectedCourseValue) {
+    console.log('Creating course chart with value:', selectedCourseValue);
+    const seasonFilter = '{{ $seasonFilter }}';
+    const seasonKey = '{{ str_replace('-', '', $seasonFilter) }}';
+
+    const chartId = `courses-chart-${seasonKey}-${selectedCourseValue}`;
+    const canvas = document.getElementById(chartId);
+    if (!canvas) {
+        console.log('Canvas not found for chart ID:', chartId);
+        return;
+    }
+
+    this.destroyChart(chartId);
+
+    // Call Livewire method to get fresh data
+    @this.call('getCourseData', selectedCourseValue).then(courseData => {
+        console.log('Received course data:', courseData);
+
+        if (!courseData || !courseData.labels || courseData.labels.length === 0) {
+            console.log('No data available for chart');
+            return;
+        }
+
+        const ctx = canvas.getContext('2d');
+        this.charts[chartId] = new Chart(ctx, {
+            type: 'bar',
+            data: {
+                labels: courseData.labels,
+                datasets: courseData.datasets
+            },
+            options: {
+                responsive: true,
+                maintainAspectRatio: false,
+                scales: {
+                    y: {
+                        beginAtZero: true
+                    }
+                }
+            }
+        });
+    }).catch(error => {
+        console.error('Error calling getCourseData:', error);
+    });
+},
+        };
+
+        document.addEventListener('DOMContentLoaded', function () {
+            setTimeout(() => {
+                window.ReportsChartManager.updateMainCharts();
+            }, 100);
+        });
+
+        document.addEventListener('livewire:navigated', function () {
+            setTimeout(() => {
+                window.ReportsChartManager.updateMainCharts();
+            }, 100);
+        });
+        document.addEventListener('livewire:load', function () {
+            Livewire.on('courseSelected', (courseId) => {
+                console.log('Course selected event received:', courseId);
+                setTimeout(() => {
+                    window.ReportsChartManager.createCourseChartWithValue(courseId);
+                }, 200);
+            });
+        });
+    </script>
 </div>