Procházet zdrojové kódy

Merge branch 'ferrari' into iao_fixes

FabioFratini před 9 měsíci
rodič
revize
c1aa59a593

+ 0 - 1
app/Http/Livewire/Receipt.php

@@ -13,7 +13,6 @@ class Receipt extends Component
     public $filterStatus = '';
     public $hasFilter = false;
     public $filterFrom = '', $filterTo = '';
-
     public $filteredMemberId = '';
     public $members = [];
 

+ 19 - 5
app/Http/Livewire/RecordIN.php

@@ -13,7 +13,7 @@ class RecordIN extends Component
     use WithPagination;
     protected $paginationTheme = 'bootstrap';
 
-    protected $listeners = ['setCausal' => 'setCausal'];
+    protected $listeners = ['setCausal' => 'setCausal','refreshMembers' => 'refreshMembers'];
 
     public $sortField ='date';
     public $sortAsc = false;
@@ -167,6 +167,11 @@ class RecordIN extends Component
         $this->canSave = $this->checkCanSave();
     }
 
+    public function updatedCommercial($value)
+    {
+        $this->emitSelf('refreshMembers');
+    }
+
     public function checkCanSave()
     {
         $ret = true;
@@ -187,6 +192,15 @@ class RecordIN extends Component
         return $ret;
     }
 
+    public function refreshMembers()
+    {
+        if($this->commercial){
+            $this->members = \App\Models\Member::select(['id', 'first_name', 'last_name', 'fiscal_code'])->orderBy('last_name')->orderBy('first_name')->get();
+        } else {
+            $this->members = \App\Models\Member::select(['id', 'first_name', 'last_name', 'fiscal_code'])->where('current_status', 2)->orderBy('last_name')->orderBy('first_name')->get();
+        }
+    }
+
     public function setAmount()
     {
         $tot = 0;
@@ -315,7 +329,7 @@ class RecordIN extends Component
 
         //$this->buildTree(\App\Models\Causal::all(), null);
 
-        $this->members = \App\Models\Member::select(['id', 'first_name', 'last_name', 'fiscal_code'])->orderBy('last_name')->orderBy('first_name')->get();
+        $this->refreshMembers();
         $this->payments = \App\Models\PaymentMethod::select('id', 'name')->where('enabled', true)->whereIn('type', array('ALL', 'IN'))->orderBy('name')->get();
         $this->vats = \App\Models\Vat::select('id', 'name', 'value')->orderBy('value')->get();
 
@@ -388,7 +402,7 @@ class RecordIN extends Component
 
             }
 
-            
+
             if (isset($_GET["createSubscription"]) && $_GET["createSubscription"] == 1)
             {
 
@@ -407,7 +421,7 @@ class RecordIN extends Component
                     $this->rows[$count]["causal_id"] = $_GET["subCausalId"];
                 }
             }
-            
+
         }
         $this->first = false;
 
@@ -795,7 +809,7 @@ class RecordIN extends Component
     public function rigenerate()
     {
         $this->emit('refresh');
-        
+
         try {
 
             $record = \App\Models\Record::findOrFail($this->dataId);

+ 1 - 0
app/Http/Livewire/RecordINOUT.php

@@ -618,6 +618,7 @@ class RecordINOUT extends Component
         }
 
 
+
         $count += 2;
         $activeWorksheet->setCellValue('A' . $count, "Uscite");
         foreach($columns as $idx => $column)

+ 444 - 0
app/Http/Livewire/Reports.php

@@ -0,0 +1,444 @@
+<?php
+
+namespace App\Http\Livewire;
+
+use Livewire\Component;
+use Illuminate\Support\Facades\Auth;
+use Carbon\Carbon;
+use App\Models\Receipt;
+use App\Models\ReceiptRow;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use App\Models\Course;
+use App\Models\MemberCard;
+
+class Reports extends Component
+{
+    public $type = 'anagrafica';
+    public $yearFilter;
+
+    public $courses = [];
+    public $selectedCourse = null;
+    protected $listeners = ['refreshData' => '$refresh'];
+
+    public function mount()
+    {
+        if (Auth::user()->level != env('LEVEL_ADMIN', 0))
+            return redirect()->to('/reports');
+
+        if (isset($_GET["type"]))
+            $this->type = $_GET["type"];
+
+        $this->yearFilter = Carbon::now()->year;
+        $this->courses = $this->getCoursesForSelect();
+        $this->emit('dataUpdated');
+    }
+
+    public function render()
+    {
+        return view('livewire.reports');
+    }
+    public function updateCourseChart()
+    {
+        $this->emit('courseDataUpdated');
+    }
+
+    public function updatedSelectedCourse($value)
+    {
+        Log::info('Selected course changed to: ' . $value);
+        $this->emit('courseDataUpdated', $value);
+    }
+
+    public function getTesseratiData()
+    {
+        $endYear = $this->yearFilter;
+        return self::getMemberCountChartData($endYear);
+    }
+
+    public function change($type)
+    {
+        $this->type = $type;
+    }
+
+    public function setYearFilter($year)
+    {
+        $this->yearFilter = $year;
+        $this->emit('dataUpdated');
+    }
+
+    public function getMonthlyTotals()
+    {
+        $year = $this->yearFilter;
+
+        $months = range(1, 12);
+        $monthNames = [];
+        $incomeData = array_fill(0, 12, 0);
+        $expenseData = array_fill(0, 12, 0);
+
+        foreach ($months as $month) {
+            $date = Carbon::createFromDate($year, $month, 1);
+            $monthNames[] = ucfirst($date->locale('it')->monthName);
+        }
+
+        $incomeReceipts = DB::table('receipts')
+            ->join('receipts_rows', 'receipts.id', '=', 'receipts_rows.receip_id')
+            ->where('receipts.year', $year)
+            ->where('receipts.type', 'IN')
+            ->select(DB::raw('MONTH(receipts.date) as month_num'), DB::raw('SUM(receipts_rows.amount) as total'))
+            ->groupBy('month_num')
+            ->get();
+
+        $expenseReceipts = DB::table('receipts')
+            ->join('receipts_rows', 'receipts.id', '=', 'receipts_rows.receip_id')
+            ->where('receipts.year', $year)
+            ->where('receipts.type', 'OUT')
+            ->select(DB::raw('MONTH(receipts.date) as month_num'), DB::raw('SUM(receipts_rows.amount) as total'))
+            ->groupBy('month_num')
+            ->get();
+
+        foreach ($incomeReceipts as $receipt) {
+            $index = $receipt->month_num - 1;
+            if (isset($incomeData[$index])) {
+                $incomeData[$index] = $receipt->total;
+            }
+        }
+
+        foreach ($expenseReceipts as $receipt) {
+            $index = $receipt->month_num - 1;
+            if (isset($expenseData[$index])) {
+                $expenseData[$index] = $receipt->total;
+            }
+        }
+
+
+        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()
+    {
+        $year = $this->yearFilter;
+
+        $totalIncome = DB::table('receipts')
+            ->join('receipts_rows', 'receipts.id', '=', 'receipts_rows.receip_id')
+            ->where('receipts.year', $year)
+            ->where('receipts.type', 'IN')
+            ->sum('receipts_rows.amount');
+
+        $totalExpenses = DB::table('receipts')
+            ->join('receipts_rows', 'receipts.id', '=', 'receipts_rows.receip_id')
+            ->where('receipts.year', $year)
+            ->where('receipts.type', 'OUT')
+            ->sum('receipts_rows.amount');
+
+        $delta = $totalIncome - $totalExpenses;
+
+        return [
+            'totalIncome' => $totalIncome,
+            'totalExpenses' => $totalExpenses,
+            'delta' => $delta
+        ];
+    }
+
+    public function getTopCausalsByAmount($limit = 10)
+    {
+        $year = $this->yearFilter;
+
+        $query = DB::table('receipts_rows')
+            ->join('receipts', 'receipts_rows.receip_id', '=', 'receipts.id')
+            ->join('causals', 'receipts_rows.causal_id', '=', 'causals.id')
+            ->where('receipts.year', $year);
+
+        $query->where('receipts.type', 'IN');
+
+        Log::info('Query: ' . $query->toSql());
+
+        $causals = $query->select(
+            'causals.id',
+            'causals.name',
+            'causals.parent_id',
+            DB::raw('SUM(receipts_rows.amount) as total_amount')
+        )
+            ->groupBy('causals.id', 'causals.name', 'causals.parent_id')
+            ->orderBy('total_amount', 'desc')
+            ->limit($limit)
+            ->get();
+
+        Log::info('Causals: ' . json_encode($causals));
+
+        $inData = [];
+
+        foreach ($causals as $causal) {
+            $tempCausal = new \App\Models\Causal();
+            $tempCausal->id = $causal->id;
+            $tempCausal->name = $causal->name;
+            $tempCausal->parent_id = $causal->parent_id;
+
+            $treeName = $tempCausal->getTree();
+
+            $displayName = strlen($treeName) > 30 ? substr($treeName, 0, 27) . '...' : $treeName;
+
+            $inData[] = [
+                'label' => $displayName,
+                'value' => $causal->total_amount,
+                'fullName' => $treeName
+            ];
+        }
+
+        usort($inData, function ($a, $b) {
+            return $b['value'] <=> $a['value'];
+        });
+
+        $inData = array_slice($inData, 0, $limit);
+
+        return [
+            'inLabels' => array_column($inData, 'label'),
+            'inData' => $inData,
+            'datasets' => [
+                [
+                    'label' => 'Entrate per Causale',
+                    'data' => array_column($inData, 'value'),
+                ]
+            ]
+        ];
+    }
+
+    public function getCoursesForSelect()
+    {
+        $currentYear = date('Y');
+
+        $courses = Course::with(['level', 'type', 'frequency'])
+            ->where('active', true)
+            ->where('year', 'like', '%' . $currentYear . '%')
+            ->orderBy('name')
+            ->get()
+            ->map(function ($course) {
+                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';
+                $typeName = is_object($type) ? $type->name : 'No Type';
+                $frequencyName = is_object($course->frequency) ? $course->frequency->name : 'No Frequency';
+                $year = $course->year ?? '';
+
+                return [
+                    'id' => $course->id,
+                    'name' => $course->name,
+                    'full_name' => "{$course->name} - {$levelName} - {$typeName} - {$frequencyName} ({$year})",
+                    'level_name' => $levelName,
+                    'type_name' => $typeName,
+                    'frequency_name' => $frequencyName,
+                    'year' => $year
+                ];
+            })->toArray();
+
+        return $courses;
+    }
+
+
+    public function getCourseMonthlyEarnings()
+    {
+        $courseId = $this->selectedCourse;
+        Log::info('Getting earnings for course ID: ' . $courseId);
+
+        if (empty($courseId)) {
+
+            return [
+                'labels' => ['Set', 'Ott', 'Nov', 'Dic', 'Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago'],
+                'datasets' => [
+                    [
+                        'label' => 'Pagamenti Effettuati',
+                        'backgroundColor' => 'rgba(0, 184, 148, 1)',
+                        'data' => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+                        'type' => 'bar',
+                        'order' => 3
+                    ],
+                    [
+                        'label' => 'Pagamenti Totali',
+                        'backgroundColor' => 'transparent',
+                        'borderColor' => 'rgba(48, 51, 107, 1)',
+                        'borderWidth' => 3,
+                        'pointBackgroundColor' => 'rgba(48, 51, 107, 1)',
+                        'pointRadius' => 5,
+                        'data' => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+                        'type' => 'line',
+                        'tension' => 0.2,
+                        'order' => 2
+                    ]
+                ]
+            ];
+        }
+
+        $monthOrder = [9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8];
+
+        $monthlyData = [];
+        foreach ($monthOrder as $i) {
+            $monthlyData[$i] = [
+                'earned' => 0,
+                'total' => 0,
+                'participants' => 0
+            ];
+        }
+
+        $memberCourses = \App\Models\MemberCourse::where('course_id', $courseId)
+            ->with('member')
+            ->get();
+
+        foreach ($memberCourses as $memberCourse) {
+            $price = (float)($memberCourse->price ?? 0);
+
+            if ($memberCourse->months) {
+                $monthsData = json_decode($memberCourse->months, true);
+
+                if (is_array($monthsData)) {
+                    foreach ($monthsData as $monthData) {
+                        $month = $monthData['m'] ?? null;
+                        $status = $monthData['status'] ?? '';
+
+                        if ($month !== null && isset($monthlyData[$month])) {
+                            $monthlyData[$month]['total'] += $price;
+
+                            if ($status === 1) {
+                                $monthlyData[$month]['earned'] += $price;
+                            }
+
+                            $monthlyData[$month]['participants']++;
+                        }
+                    }
+                }
+            }
+        }
+
+        $monthNames = [
+            9 => 'Set',
+            10 => 'Ott',
+            11 => 'Nov',
+            12 => 'Dic',
+            1 => 'Gen',
+            2 => 'Feb',
+            3 => 'Mar',
+            4 => 'Apr',
+            5 => 'Mag',
+            6 => 'Giu',
+            7 => 'Lug',
+            8 => 'Ago',
+        ];
+
+        $labels = [];
+        $earnedData = [];
+        $totalData = [];
+        $participantData = [];
+        $missingData = [];
+
+        foreach ($monthOrder as $month) {
+            $labels[] = $monthNames[$month];
+            $earnedData[] = round($monthlyData[$month]['earned'], 2);
+            $totalData[] = round($monthlyData[$month]['total'], 2);
+            $participantData[] = $monthlyData[$month]['participants'];
+            $missingData[] = round($monthlyData[$month]['total'] - $monthlyData[$month]['earned'], 2);
+        }
+
+        return [
+            'labels' => $labels,
+            'datasets' => [
+                [
+                    'label' => 'Pagamenti Effettuati',
+                    'backgroundColor' => 'rgba(0, 184, 148, 1)',
+                    'data' => $earnedData,
+                    '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' => $totalData,
+                    'type' => 'line',
+                    'tension' => 0.2,
+                    'order' => 2,
+                    'participants' => $participantData,
+                    'missing' => $missingData
+                ]
+            ]
+        ];
+    }
+
+    public static function getMemberCountChartData($endYear = null, $span = 5)
+    {
+        if ($endYear === null) {
+            $endYear = date('Y');
+        }
+
+        $startYear = $endYear - $span + 1;
+
+        $memberCards = MemberCard::select('member_id', 'expire_date')
+            ->whereNotNull('expire_date')
+            ->whereNotNull('member_id')
+            ->where('status', '!=', 'cancelled')
+            ->whereRaw('YEAR(expire_date) >= ?', [$startYear])
+            ->whereRaw('YEAR(expire_date) <= ?', [$endYear])
+            ->get();
+
+        $yearCounts = [];
+        for ($year = $startYear; $year <= $endYear; $year++) {
+            $yearPeriod = ($year - 1) . '-' . $year;
+            $yearCounts[$yearPeriod] = [];
+        }
+
+        foreach ($memberCards as $card) {
+            $expireYear = date('Y', strtotime($card->expire_date));
+
+            $previousYear = $expireYear - 1;
+            $yearPeriod = $previousYear . '-' . $expireYear;
+
+            if (isset($yearCounts[$yearPeriod])) {
+                $yearCounts[$yearPeriod][$card->member_id] = true;
+            }
+        }
+
+        $yearLabels = [];
+        $memberCountData = [];
+
+        foreach ($yearCounts as $yearPeriod => $members) {
+            $yearLabels[] = $yearPeriod;
+            $memberCountData[] = count($members);
+        }
+
+        return [
+            'labels' => $yearLabels,
+            'datasets' => [
+                [
+                    'label' => 'Membri Tesserati',
+                    'data' => $memberCountData,
+                    'backgroundColor' => 'rgba(54, 162, 235, 0.2)',
+                    'borderColor' => 'rgba(54, 162, 235, 1)',
+                    'borderWidth' => 2,
+                    'pointBackgroundColor' => 'rgba(54, 162, 235, 1)',
+                    'pointRadius' => 4,
+                    'tension' => 0.3,
+                    'fill' => true
+                ]
+            ]
+        ];
+    }
+}

+ 2 - 2
public/css/extra.css

@@ -3,7 +3,7 @@
     border-color: #FF0000 !important;
 }
 .numericCol{
-    text-align: right !important;
+    text-align: left !important;
 }
 .paddingLeftSelect0
 {
@@ -27,4 +27,4 @@ table.tableHead thead {
 ::-webkit-scrollbar-thumb {
     background: #e4e4e4 !important;
     border-radius: 10px;
-}
+}

+ 95 - 4
public/css/style.css

@@ -16277,10 +16277,6 @@ table.tablesaw tbody tr td .primary {
   text-align: center;
   padding-right: 30px;
 }
-.table--lista_ricevute tbody tr td:nth-child(7) {
-  text-align: right !important;
-  padding-right: 30px;
-}
 
 .tabella--corsi {
   min-width: unset !important;
@@ -16678,3 +16674,98 @@ div.dt-container div.dt-length label {
     background-color: #e9ecef;
     color: #495057;
 }
+
+
+/* CSS Ferrari - Modifiche UI */
+#home_logo {
+  height: 70px;
+}
+
+#sidebar--wrapper {
+  height: calc(100dvh - 86px);
+}
+
+#sidebar--wrapper #filter--section {
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+#card--dashboard {
+  padding-block: 10px;
+  margin: 0;
+  overflow: auto;
+  height: calc(100dvh - 86px);
+  max-width: 100vw;
+}
+
+body:has(#filter--section.filterWrapper_open) #card--dashboard {
+  max-width: calc(100vw - 250px);
+}
+
+.row > [wire\:id] {
+  padding-inline: 0;
+}
+
+#resume-table {
+  width: 100% !important;
+  max-width: 100% !important;
+  padding: 0;
+  border: none;
+  max-height: calc(100dvh - 86px) !important;
+  /* overflow: auto; */
+}
+
+#resume-table.course_list-table {
+  height: calc(100dvh - (86px + 80px)) !important;
+}
+
+#resume-table.records-table {
+  height: calc(100dvh - (86px + 195px)) !important;
+}
+
+#card--dashboard:has(.showFilter.filter_shown) #resume-table.course_list-table {
+  height: calc(100dvh - (86px + 80px + 212px)) !important;
+}
+
+#resume-table.course_list-table > .row {
+  margin: 0;
+  position: sticky;
+  bottom: 0;
+  left: 0;
+  padding: 10px 0;
+  border-top: 1px solid;
+  z-index: 100;
+  background-color: white;
+}
+
+table.tablesaw thead:has(tr+tr) tr:first-child th {
+  padding-bottom: 0;
+}
+
+table.tablesaw thead:has(tr+tr) tr:last-child th {
+  padding-top: 0;
+}
+
+table.tableHead thead {
+  top: -10px !important;
+}
+
+.course_list-table table.tableHead thead,
+.records-table table.tableHead thead {
+  top: 0 !important;
+}
+
+#btn-back-to-top,
+#btn-back-to-bottom {
+  bottom: 10px;
+  right: 10px;
+  padding: 7px;
+  width: 40px;
+  height: 40px;
+}
+
+#btn-back-to-bottom {
+  top: unset;
+  right: 60px;
+}
+/* END CSS Ferrari - Modifiche UI */

+ 19 - 6
resources/views/layouts/app.blade.php

@@ -103,9 +103,7 @@
     {
         z-index:9999 !important;
     }
-    .numericCol{
-        text-align: right !important;
-    }
+
   </style>
 
     <link rel="stylesheet" href="/css/style.css?v={{date('YmdHis')}}">
@@ -138,7 +136,7 @@
     <div class="row header--gestionale">
         <div class="header--gestionale_logo">
             <a href="/dashboard" class="d-flex align-items-center pb-2 pt-2">
-                <img src="{{env('LOGO2', env('LOGO', ''))}}" class="img-fluid" alt="logo madonnella"/>
+                <img src="{{env('LOGO2', env('LOGO', ''))}}" id="home_logo" class="img-fluid" alt="logo madonnella"/>
             </a>
         </div>
         <div class="header--gestionale_pageTitle d-flex align-items-center justify-content-between">
@@ -221,7 +219,8 @@
                     <i class="ico--ui hamburger--menu"></i>
             </a>
 
-            <div class="d-flex flex-column align-items-center align-items-sm-start min-vh-100 offcanvas-lg offcanvas-start" tabindex="-1" id="offcanvasExample" aria-labelledby="offcanvasExampleLabel">
+            <!-- <div class="d-flex flex-column align-items-center align-items-sm-start min-vh-100 offcanvas-lg offcanvas-start" tabindex="-1" id="offcanvasExample" aria-labelledby="offcanvasExampleLabel"> -->
+            <div class="d-flex flex-column align-items-center align-items-sm-start offcanvas-lg offcanvas-start" tabindex="-1" id="offcanvasExample" aria-labelledby="offcanvasExampleLabel">
                 @if(false)
                     <a href="/dashboard" class="d-flex align-items-center pb-2 pt-2 mb-md-0 me-md-auto text-white text-decoration-none">
                         <img src="{{env('LOGO2', env('LOGO', ''))}}" class="fs-5 d-none d-sm-inline img-fluid" alt="logo madonnella"  style="max-width:200px"/>
@@ -353,13 +352,22 @@
                             </div>
                         @endif
                     @endif
+                    @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
+                        <div class="accordion-item " style="{{Request::is('reports') || Request::is('categories') || Request::is('disciplines') || Request::is('cards') || Request::is('course_subscriptions') || Request::is('courses') || Request::is('course_durations') || Request::is('course_frequencies') || Request::is('course_levels') || Request::is('course_types') || Request::is('banks') || Request::is('causals') || Request::is('vats') || Request::is('payment_methods') || Request::is('users') ? 'background-color: #c5d9e6;' : ''}}">
+                            <h2 class="accordion-header linkMenu">
+                                <a class="accordion-button collapsed" href="/reports">
+                                    Reports
+                                </a>
+                            </h2>
+                        </div>
+                    @endif
                 </div>
                 </div>
             </div>
         </div>
 
         <button id="open-filter" onclick="pcsh1()"></button>
-        
+
 
         <div class="col">
             <div class="row h-100">
@@ -377,6 +385,11 @@
         <li class="user--boxlist_item">
             <a href="/profile">Profilo</a>
         </li>
+        @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
+            <li class="user--boxlist_item">
+                <a href="/azienda">Azienda</a>
+            </li>
+        @endif
         <li class="user--boxlist_item">
             <a href="/logout">Logout</a>
         </li>

+ 220 - 0
resources/views/livewire/azienda.blade.php

@@ -0,0 +1,220 @@
+<div>
+
+    <div class="row mt-4">
+        <div class="col-md-12">
+            <div class="card">
+                <div class="card-body">
+                    @if (session()->has('message'))
+                        <div class="alert alert-success" role="alert">
+                            {{ session()->get('message') }}
+                        </div>
+                    @endif
+
+                    @if (session()->has('error'))
+                        <div class="alert alert-danger" role="alert">
+                            {{ session()->get('error') }}
+                        </div>
+                    @endif
+
+                    <div class="tab-content">
+                        <form wire:submit.prevent="save">
+                            <div class="row mb-4">
+                                <div class="col-md-4">
+                                    <div class="logo-container mb-3">
+                                        @if($azienda && $azienda->logo_url)
+                                            <img src="{{ $azienda->logo_url }}" alt="Logo" class="img-thumbnail" style="max-width: 200px;">
+                                        @else
+                                            <div class="mb-3">
+                                                <label class="form-label">Logo</label>
+                                                <input type="file" class="form-control" wire:model="temp_logo">
+                                            </div>
+                                        @endif
+                                    </div>
+
+                                </div>
+                            </div>
+
+                            <div class="mb-4">
+
+                                <div class="row">
+                                    <div class="col-md-6 mb-3">
+                                        <label class="form-label">Ragione sociale*</label>
+                                        <input type="text" class="form-control @error('ragione_sociale') is-invalid @enderror"
+                                                wire:model="ragione_sociale">
+                                        @error('ragione_sociale') <span class="text-danger">{{ $message }}</span> @enderror
+                                    </div>
+                                    <div class="col-md-6 mb-3">
+                                        <label class="form-label">Nome associazione/società</label>
+                                        <input type="text" class="form-control" wire:model="nome_associazione">
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="col-md-6 mb-3">
+                                        <label class="form-label">Tipologia* (ASD/SSD/Polisportiva ecc.)</label>
+                                        <input type="text" class="form-control" wire:model="tipologia">
+                                    </div>
+                                    <div class="col-md-6  mb-3">
+                                        <label class="form-label">Discipline</label>
+                                        <div wire:ignore>
+                                            <select class="form-select discipline-select" multiple wire:model="selectedDisciplines">
+                                                @forelse($disciplines as $discipline)
+                                                    <option value="{{ $discipline->id }}">
+                                                        {{ $discipline->name }}
+                                                    </option>
+                                                @empty
+                                                    <option disabled>Nessuna disciplina trovata</option>
+                                                @endforelse
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <h5 class="mb-3">Sede legale</h5>
+                            <div class="row">
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Nazione</label>
+                                    <input type="text" class="form-control" wire:model="sede_legale_nazione">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Provincia</label>
+                                    <input type="text" class="form-control" wire:model="sede_legale_provincia">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Comune</label>
+                                    <input type="text" class="form-control" wire:model="sede_legale_comune">
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-md-8 mb-3">
+                                    <label class="form-label">Indirizzo</label>
+                                    <input type="text" class="form-control" wire:model="sede_legale_indirizzo">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">CAP</label>
+                                    <input type="text" class="form-control" wire:model="sede_legale_cap">
+                                </div>
+                            </div>
+
+                            <div class="form-check mb-3">
+                                <input class="form-check-input" type="checkbox" wire:model="same_address" id="sameAddress">
+                                <label class="form-check-label" for="sameAddress">
+                                    Sede operativa uguale a sede legale
+                                </label>
+                            </div>
+
+                            @if(!$same_address)
+                                <h5 class="mb-3">Sede operativa</h5>
+                                <div class="row">
+                                    <div class="col-md-4 mb-3">
+                                        <label class="form-label">Nazione</label>
+                                        <input type="text" class="form-control" wire:model="sede_operativa_nazione">
+                                    </div>
+                                    <div class="col-md-4 mb-3">
+                                        <label class="form-label">Provincia</label>
+                                        <input type="text" class="form-control" wire:model="sede_operativa_provincia">
+                                    </div>
+                                    <div class="col-md-4 mb-3">
+                                        <label class="form-label">Comune</label>
+                                        <input type="text" class="form-control" wire:model="sede_operativa_comune">
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="col-md-8 mb-3">
+                                        <label class="form-label">Indirizzo</label>
+                                        <input type="text" class="form-control" wire:model="sede_operativa_indirizzo">
+                                    </div>
+                                    <div class="col-md-4 mb-3">
+                                        <label class="form-label">CAP</label>
+                                        <input type="text" class="form-control" wire:model="sede_operativa_cap">
+                                    </div>
+                                </div>
+                            @endif
+
+                            <h5 class="mb-3">Contatti</h5>
+                            <div class="row">
+                                <div class="col-md-6 mb-3">
+                                    <label class="form-label">Email*</label>
+                                    <input type="email" class="form-control @error('email') is-invalid @enderror"
+                                            wire:model="email">
+                                    @error('email') <span class="text-danger">{{ $message }}</span> @enderror
+                                </div>
+                                <div class="col-md-6 mb-3">
+                                    <label class="form-label">Pec*</label>
+                                    <input type="email" class="form-control @error('pec') is-invalid @enderror"
+                                            wire:model="pec">
+                                    @error('pec') <span class="text-danger">{{ $message }}</span> @enderror
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-md-6 mb-3">
+                                    <label class="form-label">Telefono</label>
+                                    <input type="text" class="form-control" wire:model="telefono">
+                                </div>
+                                <div class="col-md-6 mb-3">
+                                    <label class="form-label">Cellulare*</label>
+                                    <input type="text" class="form-control @error('cellulare') is-invalid @enderror"
+                                            wire:model="cellulare">
+                                    @error('cellulare') <span class="text-danger">{{ $message }}</span> @enderror
+                                </div>
+                            </div>
+
+                            <h5 class="mb-3">Dati fiscali</h5>
+                            <div class="row">
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Partita IVA</label>
+                                    <input type="text" class="form-control" wire:model="partita_iva">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Codice fiscale</label>
+                                    <input type="text" class="form-control" wire:model="codice_fiscale">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Codice SDI</label>
+                                    <input type="text" class="form-control" wire:model="codice_sdi">
+                                </div>
+                            </div>
+
+                            <h5 class="mb-3">Configurazione contabilità</h5>
+                            <div class="row">
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Chiusura anno fiscale</label>
+                                    <input type="date" class="form-control" wire:model="chiusura_anno_fiscale">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Scadenza abbonamenti</label>
+                                    <input type="date" class="form-control" wire:model="scadenza_abbonamenti">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Scadenza pagamenti in uscita</label>
+                                    <input type="date" class="form-control" wire:model="scadenza_pagamenti_uscita">
+                                </div>
+                            </div>
+
+                            <div class="mt-4">
+                                <button type="submit" class="btn btn-primary">Salva</button>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+@push('scripts')
+    <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
+    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
+    <script>
+        $(document).ready(function() {
+            $('.discipline-select').select2({
+                placeholder: 'Seleziona discipline',
+                allowClear: true
+            });
+
+            $('.discipline-select').on('change', function (e) {
+                var data = $(this).select2("val");
+                @this.set('selectedDisciplines', data);
+            });
+        });
+    </script>
+@endpush

+ 72 - 64
resources/views/livewire/course.blade.php

@@ -123,28 +123,30 @@
                                     @enderror
                                 </div>
                             </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Tipologia</label>
-                                    <select class="form-select form-select-lg me-1 @error('course_type_id') is-invalid @enderror" wire:model="course_type_id">
-                                        <option value="">
-                                        @foreach($course_types as $c)
-                                            <option value="{{$c["id"]}}">{{$c["name"]}}</option>
-                                        @endforeach
-                                    </select>
+                            @if(false)
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Tipologia</label>
+                                        <select class="form-select form-select-lg me-1 @error('course_type_id') is-invalid @enderror" wire:model="course_type_id">
+                                            <option value="">
+                                            @foreach($course_types as $c)
+                                                <option value="{{$c["id"]}}">{{$c["name"]}}</option>
+                                            @endforeach
+                                        </select>
+                                    </div>
                                 </div>
-                            </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Durata</label>
-                                    <select class="form-select form-select-lg me-1 @error('course_duration_id') is-invalid @enderror" wire:model="course_duration_id">
-                                        <option value="">
-                                        @foreach($course_durations as $c)
-                                            <option value="{{$c["id"]}}">{{$c["name"]}}</option>
-                                        @endforeach
-                                    </select>
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Durata</label>
+                                        <select class="form-select form-select-lg me-1 @error('course_duration_id') is-invalid @enderror" wire:model="course_duration_id">
+                                            <option value="">
+                                            @foreach($course_durations as $c)
+                                                <option value="{{$c["id"]}}">{{$c["name"]}}</option>
+                                            @endforeach
+                                        </select>
+                                    </div>
                                 </div>
-                            </div>
+                            @endif
                             <div class="col-6 mt-2">
                                 <div class="form--item">
                                     <label for="inputName" class="form-label">Frequenza</label>
@@ -179,42 +181,45 @@
                                     <input class="form-control" type="date" placeholder="Data fine" wire:model="date_to">
                                 </div>
                             </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Prezzo mensile</label>
-                                    <input class="form-control js-keyupTitle @error('price') is-invalid @enderror" type="text" id="price" onkeyup="onlyNumberAmount(this)" placeholder="€ 0,00" wire:model="price">
-                                    @error('price')
-                                        <div class="invalid-feedback">{{ $message }}</div>
-                                    @enderror
+                            @if(false)
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Prezzo mensile</label>
+                                        <input class="form-control js-keyupTitle @error('price') is-invalid @enderror" type="text" id="price" onkeyup="onlyNumberAmount(this)" placeholder="€ 0,00" wire:model="price">
+                                        @error('price')
+                                            <div class="invalid-feedback">{{ $message }}</div>
+                                        @enderror
+                                    </div>
                                 </div>
-                            </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Prezzo iscrizione</label>
-                                    <input class="form-control js-keyupTitle @error('subscription_price') is-invalid @enderror" type="text" id="subscription_price" onkeyup="onlyNumberAmount(this)" placeholder="€ 0,00" wire:model="subscription_price">
-                                    @error('subscription_price')
-                                        <div class="invalid-feedback">{{ $message }}</div>
-                                    @enderror
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Prezzo iscrizione</label>
+                                        <input class="form-control js-keyupTitle @error('subscription_price') is-invalid @enderror" type="text" id="subscription_price" onkeyup="onlyNumberAmount(this)" placeholder="€ 0,00" wire:model="subscription_price">
+                                        @error('subscription_price')
+                                            <div class="invalid-feedback">{{ $message }}</div>
+                                        @enderror
+                                    </div>
                                 </div>
-                            </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Causale</label>
-                                    <livewire:causals :type="$typeIN" :idx="0" :causal_id="$causal_id" :wire:key="0" />
-                                    @error('causal_id')
-                                        <span style="argin-top: 0.25rem; font-size: 0.875em; color: var(--bs-form-invalid-color);">{{ $message }}</span>
-                                    @enderror
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Causale</label>
+                                        <livewire:causals :type="$typeIN" :idx="0" :causal_id="$causal_id" :wire:key="0" />
+                                        @error('causal_id')
+                                            <span style="argin-top: 0.25rem; font-size: 0.875em; color: var(--bs-form-invalid-color);">{{ $message }}</span>
+                                        @enderror
+                                    </div>
                                 </div>
-                            </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Causale iscrizione</label>
-                                    <livewire:causals :type="$typeIN" :idx="0" :causal_id="$sub_causal_id" :wire:key="0" :emit="$setSubscriptionCausal" />
-                                    @error('sub_causal_id')
-                                        <span style="argin-top: 0.25rem; font-size: 0.875em; color: var(--bs-form-invalid-color);">{{ $message }}</span>
-                                    @enderror
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Causale iscrizione</label>
+                                        <livewire:causals :type="$typeIN" :idx="0" :causal_id="$sub_causal_id" :wire:key="0" :emit="$setSubscriptionCausal" />
+                                        @error('sub_causal_id')
+                                            <span style="argin-top: 0.25rem; font-size: 0.875em; color: var(--bs-form-invalid-color);">{{ $message }}</span>
+                                        @enderror
+                                    </div>
                                 </div>
-                            </div>
+                            @endif
+
                             <div class="col-6 mt-2">
                                 <div class="form--item">
                                     <label for="inputName" class="form-label">N° partecipanti</label>
@@ -236,6 +241,19 @@
                                     </select>
                                 </div>
                             </div>
+
+                            <div class="col-6 mt-2">
+                                <div class="form--item">
+                                    <label for="inputName" class="form-label">Gruppo di appartenenza</label>
+                                    <select class="form-select form-select-lg me-1 @error('category_id') is-invalid @enderror" wire:model="category_id">
+                                        <option value="">
+                                        @foreach($categories as $category)
+                                            <option value="{{$category["id"]}}">{{str_repeat('  ', $category["indentation"])}}{{$category["name"]}}
+                                        @endforeach
+                                    </select>
+                                </div>
+                            </div>
+
                             <div class="col-6 mt-2">
                                 <div class="form--item">
                                     <label for="inputName" class="form-label">Anno</label>
@@ -250,17 +268,7 @@
                                 </div>
                             </div>
 
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Gruppo di appartenenza</label>
-                                    <select class="form-select form-select-lg me-1 @error('category_id') is-invalid @enderror" wire:model="category_id">
-                                        <option value="">
-                                        @foreach($categories as $category)
-                                            <option value="{{$category["id"]}}">{{str_repeat('  ', $category["indentation"])}}{{$category["name"]}}
-                                        @endforeach
-                                    </select>
-                                </div>
-                            </div>
+
 
                             <div class="col">
                                 <div class="form--item">
@@ -269,7 +277,7 @@
                                 </div>
                             </div>
 
-                            
+
                         </div>
 
                         <div class="form--item">

+ 59 - 46
resources/views/livewire/course_list.blade.php

@@ -9,33 +9,44 @@
     </header>
 
 
-    <div class="row" style="margin-top: 10px; margin-bottom:10px;">
-        <div class="col-md-4">
-            <div class="col-md ">
-                <div class="dt-buttons btn-group flex-wrap">
-                    <div class="btn-group">
-                        <button class="btn btn-secondary buttons-collection" wire:click="export()" style="color:black !important">ESPORTA</button>
+    <div class="dt-container">
+
+        <div class="row" style="margin-top: 10px; margin-bottom:10px;">
+            <div class="col-md-4">
+                <div class="col-md ">
+                    <div class="dt-buttons btn-group flex-wrap">
+                        <div class="btn-group">
+                            <button class="btn btn-secondary buttons-collection" wire:click="export()" style="color:black !important">ESPORTA</button>
+                        </div>
                     </div>
                 </div>
             </div>
-        </div>
-        <div class="col-md-4">
-            <div class="row">
-                <div class="col-md-4" style="text-align:right">Visualizza</div>
-                <div class="col-md-4">
-                    <select name="tablesaw-350_length" aria-controls="tablesaw-350" class="form-select form-select-sm" id="dt-length-0" wire:model="pageLength">
-                        <option value="10">10</option>
-                        <option value="25">25</option>
-                        <option value="50">50</option>
-                        <option value="100">100</option>
-                        <option value="100000">Tutti</option>
-                    </select>
-                </div>
-                <div class="col-md-3">elementi</div>
+            <div class="col-md-4">
+                <!-- <div class="row"> -->
+                    <div class="dt-length">
+                        <label class="align-items-center d-flex dt-length-0 flex-row gap-2 justify-content-center">
+                            Visualizza
+                            <!-- <div class="col-md-4"> -->
+                                <select name="tablesaw-350_length" aria-controls="tablesaw-350" class="form-select form-select-sm m-0" id="dt-length-0" wire:model="pageLength">
+                                    <option value="10">10</option>
+                                    <option value="25">25</option>
+                                    <option value="50">50</option>
+                                    <option value="100">100</option>
+                                    <option value="100000">Tutti</option>
+                                </select>
+                            <!-- </div> -->
+                            <!-- <div class="col-md-3">elementi</div> -->
+                            elementi
+                        </label>
+                    </div>
+                <!-- </div> -->
+            </div>
+            <div class="col-md-4" style="text-align:right">
+                <a href="#" class="showHideFilter btn--ui" ><i class="fa-solid fa-sliders"></i></a><br>
             </div>
         </div>
         <div class="col-md-4" style="text-align:right">
-            
+
             <a href="#" class="showHideFilter btn--ui" style="width:100px; float:right;"><i class="fa-solid fa-sliders"></i></a>
             <input type="text" wire:model="search" class="form-control" style="width:200px; float:right;margin-right:10px;">
         </div>
@@ -159,7 +170,8 @@
         </div>
     </div>
 
-    <section id="resume-table" class="scrollTable" style="margin-top:10px;">
+    <!-- <section id="resume-table" class="scrollTable" style="margin-top:10px;"> -->
+    <section id="resume-table" class="scrollTable course_list-table" style="margin-top:10px;">
         <div class="compare--chart_wrapper d-none"></div>
 
 
@@ -221,15 +233,15 @@
                 </tbody>
                 <tfoot id="checkall-target">
                     <tr>
-                        <th class="sticky-col-header first-zero"></th>
-                        <th class="sticky-col-header first-col"></th>
-                        <th class="sticky-col-header second-col"></th>
-                        <th class="sticky-col-header third-col"></th>
+                        <th class="sticky-col-header first-zero" style="border-bottom:none;"></th>
+                        <th class="sticky-col-header first-col" style="border-bottom:none;"></th>
+                        <th class="sticky-col-header second-col" style="border-bottom:none;"></th>
+                        <th class="sticky-col-header third-col" style="border-bottom:none;"></th>
                         @foreach($totS as $yyy)
                             @if (isset($yyy))
-                                <th style="padding-left:5px !important;padding-right:5px !important;">{!!$yyy!!}</th>
+                                <th style="padding-left:5px !important;padding-right:5px !important;border-bottom:none;">{!!$yyy!!}</th>
                             @else
-                                <th style="padding-left:5px !important;padding-right:5px !important;">aa</th>
+                                <th style="padding-left:5px !important;padding-right:5px !important;border-bottom:none;">aa</th>
                             @endif
                         @endforeach
 
@@ -568,11 +580,13 @@
                 {
                     isFilter = false;
                     $(".showFilter").hide();
+                    $(".showFilter").removeClass("filter_shown");
                 }
                 else
                 {
                     isFilter = true;
                     $(".showFilter").show();
+                    $(".showFilter").addClass("filter_shown");
                 }
             });
         } );
@@ -1228,15 +1242,6 @@
             @this.newPayment(selectedCourseId, months.toString(), selectedMemberId, selectedMemberCourseId, subscription);
         }
 
-        $( document ).ready( function(){
-
-            setMaxWidth();
-            setMaxHeight();
-            $( window ).bind( "resize", setMaxWidth );
-            $( window ).bind( "resize", setMaxHeight );
-
-        });
-
         function suspendPayment()
         {
             @this.suspendPayment(selectedCourseId, selectedMonth, selectedMemberId, selectedMemberCourseId, subscription);
@@ -1248,16 +1253,24 @@
         }
 
 
-        function setMaxWidth() {
-                 $("#resume-table").width( Math.round( $(window ).width() - size ) ) ;
-                 //$(".justify-content-between").css({"width": Math.round( $(window ).width() - size) + "px;"}); //.width( Math.round( $(window ).width() - size ) ) ;
+        // $( document ).ready( function(){
 
-            }
-            function setMaxHeight() {
-                console.log('height:' + $(window ).height() + 'px !important');
-                 //$("div.row.h-100").attr('style', 'height:' + ($(window ).height() + 50) + 'px !important');
-                 $("#resume-table").height( Math.round( $(window ).height() - 220 ) ) ;
-            }
+        //     setMaxWidth();
+        //     setMaxHeight();
+        //     $( window ).bind( "resize", setMaxWidth );
+        //     $( window ).bind( "resize", setMaxHeight );
+
+        // });
+
+        // function setMaxWidth() {
+        //     $("#resume-table").width( Math.round( $(window ).width() - size ) ) ;
+        //     //$(".justify-content-between").css({"width": Math.round( $(window ).width() - size) + "px;"}); //.width( Math.round( $(window ).width() - size ) ) ;
+        // }
+        // function setMaxHeight() {
+        //     console.log('height:' + $(window ).height() + 'px !important');
+        //     //$("div.row.h-100").attr('style', 'height:' + ($(window ).height() + 50) + 'px !important');
+        //     $("#resume-table").height( Math.round( $(window ).height() - 220 ) ) ;
+        // }
 
     </script>
 @endpush

+ 79 - 76
resources/views/livewire/receipt.blade.php

@@ -1,6 +1,7 @@
 <div class="col card--ui" id="card--dashboard">
 
-    <header id="title--section" style="display:none !important"  class="d-flex align-items-center justify-content-between">
+    <header id="title--section" style="display:none !important"
+        class="d-flex align-items-center justify-content-between">
         <div class="title--section_name d-flex align-items-center justify-content-between">
             <i class="ico--ui title_section utenti me-2"></i>
             <h2 class="primary">Ricevute</h2>
@@ -16,7 +17,7 @@
 
     </header>
 
-    <div class="showFilter" style="display:none"  wire:ignore.self>
+    <div class="showFilter" style="display:none" wire:ignore.self>
         <hr size="1">
         <div class="row g-3">
             <div class="col-md-2">
@@ -31,10 +32,12 @@
                         <input id="dateTo" type="date" class="form-control filterTo" name="txtTo">
                     </div>
                     <div class="col-6 mt-2">
-                        <button class="btn--ui lightGrey todayButton" style="width:100%" onclick="setToday('{{date("Y-m-d")}}')">OGGI</button>
+                        <button class="btn--ui lightGrey todayButton" style="width:100%"
+                            onclick="setToday('{{date("Y-m-d")}}')">OGGI</button>
                     </div>
                     <div class="col-6 mt-2">
-                        <button class="btn--ui lightGrey yesterdayButton" style="width:100%" onclick="setYesterday('{{date("Y-m-d",strtotime("-1 days"))}}')">IERI</button>
+                        <button class="btn--ui lightGrey yesterdayButton" style="width:100%"
+                            onclick="setYesterday('{{date("Y-m-d", strtotime("-1 days"))}}')">IERI</button>
                     </div>
                 </div>
             </div>
@@ -104,7 +107,8 @@
                             <td>{{$record->type == 'IN' ? ($record->member->first_name) : ''}}</td>
                             <td>
                                 <span class="tablesaw-cell-content">
-                                    <span class="badge tessera-badge {{$record->status == 1 ? 'active' : 'suspended'}}">{{$record->status == 1 ? 'Attivo' : 'Annullata'}}</span>
+                                    <span
+                                        class="badge tessera-badge {{$record->status == 1 ? 'active' : 'suspended'}}">{{$record->status == 1 ? 'Attivo' : 'Annullata'}}</span>
                                 </span>
                             </td>
                             <td>{{date("d/m/Y", strtotime($record->date))}}</td>
@@ -135,54 +139,60 @@
 @push('scripts')
     <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
     <style>
-        .select2-container--default .select2-selection--single{
+        .select2-container--default .select2-selection--single {
             background-color: #E9F0F5;
             border: 0.0625rem solid #DFE5EB;
             font-size: 0.75rem;
         }
-        .select2-selection
-        {
+
+        .select2-selection {
             height: 38px !important;
         }
-        .select2-selection__rendered
-        {
-            padding-top:3px;
+
+        .select2-selection__rendered {
+            padding-top: 3px;
         }
+
         .select2 {
-            width:100% !important;
+            width: 100% !important;
         }
     </style>
-    <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
+    <script src="https://code.jquery.com/jquery-2.2.4.min.js"
+        integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
     <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
 @endpush
 
 @push('scripts')
     <link href="/css/datatables.css" rel="stylesheet" />
-    <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
+    <script src="https://code.jquery.com/jquery-2.2.4.min.js"
+        integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
 
 
     <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
     <style>
-        .select2-container--default .select2-selection--single{
+        .select2-container--default .select2-selection--single {
             background-color: #E9F0F5;
             border: 0.0625rem solid #DFE5EB;
             font-size: 0.75rem;
         }
-        .select2-selection
-        {
+
+        .select2-selection {
             height: 38px !important;
         }
-        .select2-selection__rendered
-        {
-            padding-top:3px;
+
+        .select2-selection__rendered {
+            padding-top: 3px;
         }
+
         .select2 {
-            width:100% !important;
+            width: 100% !important;
         }
-        .select2-selection--multiple{
+
+        .select2-selection--multiple {
             overflow: hidden !important;
             height: auto !important;
         }
+
         .select2-container {
             box-sizing: border-box;
             display: inline-block;
@@ -190,6 +200,7 @@
             position: relative;
             vertical-align: middle;
         }
+
         .select2-container .select2-selection--single {
             box-sizing: border-box;
             cursor: pointer;
@@ -198,6 +209,7 @@
             user-select: none;
             -webkit-user-select: none;
         }
+
         .select2-container .select2-selection--single .select2-selection__rendered {
             display: block;
             padding-left: 8px;
@@ -206,11 +218,13 @@
             text-overflow: ellipsis;
             white-space: nowrap;
         }
-        .select2-selection__choice__display{
-            color:#000000 !important;
+
+        .select2-selection__choice__display {
+            color: #000000 !important;
         }
     </style>
-    <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
+    <script src="https://code.jquery.com/jquery-2.2.4.min.js"
+        integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
     <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
     <script src="/assets/js/datatables.js"></script>
     <script src="https://cdn.datatables.net/buttons/3.0.2/js/buttons.dataTables.js"></script>
@@ -222,7 +236,7 @@
 @push('scripts')
     <script>
 
-$(document).ready(function () {
+        $(document).ready(function () {
             loadDataTable();
         });
 
@@ -240,55 +254,48 @@ $(document).ready(function () {
         });
 
         Livewire.on('load-data-table', () => {
-            setTimeout(function() {loadDataTable()}, 100);
+            setTimeout(function () { loadDataTable() }, 100);
         });
 
         Livewire.on('destroy-data-table', () => {
             $('#tablesaw-350').DataTable().destroy();
         });
 
-        function destroyDataTable()
-        {
+        function destroyDataTable() {
             $('#tablesaw-350').DataTable().destroy();
         }
 
         var isFilter = false;
-        $(document).ready(function() {
-            $(document).on("click",".showHideFilter",function() {
-                if (isFilter)
-                {
+        $(document).ready(function () {
+            $(document).on("click", ".showHideFilter", function () {
+                if (isFilter) {
                     isFilter = false;
                     $(".showFilter").hide();
                 }
-                else
-                {
+                else {
                     isFilter = true;
                     $(".showFilter").show();
                 }
             });
-        } );
+        });
 
-        function sendMail(id)
-        {
-            $.get("/receipt/mail/" + id, function(data, status){
+        function sendMail(id) {
+            $.get("/receipt/mail/" + id, function (data, status) {
                 alert('Mail inviata');
             });
         }
 
         @if(isset($_GET["showFilters"]))
             var filterStatus = localStorage.getItem("filterStatusReceipt");
-            if (filterStatus)
-            {
+            if (filterStatus) {
                 $('.filterStatus').val(filterStatus).trigger('change');
             }
             var filterFrom = localStorage.getItem("filterFromReceipt");
-            if (filterFrom)
-            {
+            if (filterFrom) {
                 $('input[name="txtFrom"]').val(filterFrom);
             }
             var filterTo = localStorage.getItem("filterToReceipt");
-            if (filterTo)
-            {
+            if (filterTo) {
                 $('input[name="txtTo"]').val(filterFrom);
             }
             var filterMember = localStorage.getItem("filterMemberReceipt");
@@ -297,21 +304,20 @@ $(document).ready(function () {
             }
         @endif
 
-        function reset()
-        {
-            $(".todayButton").addClass("lightGrey");
-            $(".yesterdayButton").addClass("lightGrey");
-            $('.filterStatus').val(null).trigger("change");
-            $('.filterMember').val(null).trigger("change");
-            $('.filterFrom').val('');
-            $('.filterTo').val('');
-            destroyDataTable();
-            loadDataTable();
-        }
+            function reset() {
+                $(".todayButton").addClass("lightGrey");
+                $(".yesterdayButton").addClass("lightGrey");
+                $('.filterStatus').val(null).trigger("change");
+                $('.filterMember').val(null).trigger("change");
+                $('.filterFrom').val('');
+                $('.filterTo').val('');
+                destroyDataTable();
+                loadDataTable();
+            }
 
-        function loadDataTable(){
+        function loadDataTable() {
 
-            if ( $.fn.DataTable.isDataTable('#tablesaw-350') ) {
+            if ($.fn.DataTable.isDataTable('#tablesaw-350')) {
                 $('#tablesaw-350').DataTable().destroy();
             }
 
@@ -324,24 +330,22 @@ $(document).ready(function () {
             var filterTo = $('.filterTo').val();
             localStorage.setItem("filterToReeipt", filterTo);
 
-
             var filterMember = $('.filterMember').val();
             localStorage.setItem("filterMemberReceipt", filterMember);
 
-
             $('#tablesaw-350').DataTable({
                 serverSide: true,
                 ajax: {
                     url: '/get_receipts?filterStatus=' + filterStatus + '&filterFrom=' + filterFrom + '&filterTo=' + filterTo + '&filterMember=' + filterMember,
                     dataSrc: function (json) {
-                        if(json.totals){
+                        if (json.totals) {
                             totals = json.totals;
                         }
                         return json.data;
                     }
                 },
                 thead: {
-                'th': {'background-color': 'blue'}
+                    'th': { 'background-color': 'blue' }
                 },
                 columns: [
                     {
@@ -358,7 +362,7 @@ $(document).ready(function () {
                     },
                     {
                         data: "status",
-                        render: function (data){
+                        render: function (data) {
                             // Split class and text
                             var ret = '<span class="tablesaw-cell-content"><span class="badge tessera-badge ' + (data == 1 ? 'active' : 'suspended') + '">' + (data == 1 ? 'Attiva' : 'Annullata') + '</span></span>';
                             return ret;
@@ -372,7 +376,7 @@ $(document).ready(function () {
                     },
                     {
                         data: "action",
-                        render: function (data){
+                        render: function (data) {
                             var ids = data.split("|");
                             // Split class and text
                             var ret = '<button type="button" class="btn u" onclick="document.location.href=' + "'" + '/in?id=' + ids[1] + "&from=receipts'" + '" data-bs-toggle="popover"  data-bs-trigger="hover focus" data-bs-placement="bottom" data-bs-content="Visualizza ricevuta"><i class="fa-regular fa-eye"></i></button>';
@@ -383,8 +387,8 @@ $(document).ready(function () {
                     }
                 ],
                 layout: {
-                    topStart : null,
-                    topEnd : null,
+                    topStart: null,
+                    topEnd: null,
                     top1A: {
                         buttons: [
                             {
@@ -392,21 +396,21 @@ $(document).ready(function () {
                                 text: 'ESPORTA',
                                 buttons: [
                                     {
-                                    extend: 'excelHtml5',"action":newexportaction,
+                                        extend: 'excelHtml5', "action": newexportaction,
                                         title: 'Ricevute',
                                         exportOptions: {
                                             columns: ":not(':last')"
                                         }
                                     },
                                     {
-                                        extend: 'pdfHtml5',"action":newexportaction,
+                                        extend: 'pdfHtml5', "action": newexportaction,
                                         title: 'Ricevute',
                                         exportOptions: {
                                             columns: ":not(':last')"
                                         }
                                     },
                                     {
-                                        extend: 'print',"action":newexportaction,
+                                        extend: 'print', "action": newexportaction,
                                         text: 'Stampa',
                                         title: 'Ricevute',
                                         exportOptions: {
@@ -418,12 +422,12 @@ $(document).ready(function () {
                             }
                         ]
                     },
-                    top1B : {
+                    top1B: {
                         pageLength: {
                             menu: [[10, 25, 50, 100, 100000], [10, 25, 50, 100, "Tutti"]]
                         }
                     },
-                    top1C :'search',
+                    top1C: 'search',
                 },
                 order: [[0, 'desc'], [1, 'asc']],
                 pagingType: 'numbers',
@@ -437,18 +441,17 @@ $(document).ready(function () {
             });
             $('#tablesaw-350 thead tr th').addClass('col');
             $('#tablesaw-350 thead tr th').css("background-color", "#f6f8fa");
-            $('#tablesaw-350').on('draw.dt', function() {
+            $('#tablesaw-350').on('draw.dt', function () {
                 $('[data-bs-toggle="popover"]').popover()
             });
 
-            $(document).ready(function() {
-                $(document).on("click",".addData",function() {
+            $(document).ready(function () {
+                $(document).on("click", ".addData", function () {
                     $(".title--section_addButton").trigger("click")
                 });
-            } );
+            });
 
         }
 
     </script>
 @endpush
-

+ 11 - 11
resources/views/livewire/records.blade.php

@@ -65,7 +65,7 @@
               </div>
     </section>
 
-    <section id="resume-table"  class="scrollTable">
+    <section id="resume-table"  class="scrollTable records-table">
 
         <!--
         <canvas id="recordChart"></canvas>
@@ -90,11 +90,11 @@
                     <th scope="col" style="border-left:3px solid white;"></th>
                     @foreach($payments as $p)
                         @if($p->type == 'ALL')
-                            <th scope="col" style="text-align:right; border-left:3px solid white;">Entrate</th>
-                            <th scope="col" style="text-align:right">Uscite</th>
+                            <th scope="col" style="text-align:center; border-left:3px solid white;">Entrate</th>
+                            <th scope="col" style="text-align:center">Uscite</th>
                         @elseif($p->type == 'IN')
-                            <th scope="col" style="text-align:right; border-left:3px solid white;">Entrate</th>
-                            <th scope="col" style="text-align:right;"></th>
+                            <th scope="col" style="text-align:center; border-left:3px solid white;">Entrate</th>
+                            <th scope="col" style="text-align:center;"></th>
 
                         @elseif($p->type == 'OUT')
                             <th style="border-left:3px solid white;"></th>
@@ -251,8 +251,8 @@
             background-color: #0C6197;
             color: white;
             position: fixed;
-            bottom: 20px;
-            right: 20px;
+            /* bottom: 20px; */
+            /* right: 20px; */
             display: none;
         }
 
@@ -260,8 +260,8 @@
             background-color: #0C6197;
             color: white;
             position: fixed;
-            top: 120px;
-            right: 20px;
+            /* top: 120px; */
+            /* right: 20px; */
             z-index: 9999;
             display: none;
         }
@@ -473,11 +473,11 @@
         });
 
             function setMaxWidth() {
-                $("#resume-table").width( Math.round( $(window ).width() - size ) ) ;
+                // $("#resume-table").width( Math.round( $(window ).width() - size ) ) ;
             }
 
             function setMaxHeight() {
-                 $("#resume-table").height( Math.round( $(window ).height() - 300 ) ) ;
+                //  $("#resume-table").height( Math.round( $(window ).height() - 300 ) ) ;
             }
 
             let mybuttonBottom = document.getElementById("btn-back-to-bottom");

+ 3 - 1
resources/views/livewire/records_in.blade.php

@@ -1249,7 +1249,7 @@
                             return ret;
                         }
                     },
-                    { data: 'total', className: "numericCol" },
+                    { data: 'total'},
                     { data: 'last_name' },
                     { data: 'first_name' },
                     { data: 'commercial' },
@@ -1393,6 +1393,8 @@
         function setCommercialType(t)
         {
             @this.set('commercial', t);
+            Livewire.emit('refreshMembers');
+
         }
 
         Livewire.on('goToAnchor', () =>

+ 579 - 0
resources/views/livewire/reports.blade.php

@@ -0,0 +1,579 @@
+<!-- filepath: /Users/fabiofratini/Desktop/Projects/iao_team/resources/views/livewire/reports.blade.php -->
+<div class="col card--ui" id="card--dashboard">
+
+    <header id="title--section" style="display:none !important"
+        class="d-flex align-items-center justify-content-between">
+        <div class="title--section_name d-flex align-items-center justify-content-between">
+            <i class="ico--ui title_section utenti me-2"></i>
+            <h2 class="primary">Reports</h2>
+        </div>
+
+    </header>
+
+
+    <section id="subheader" class="d-flex align-items-center">
+    </section>
+
+    <section id="reports-section">
+
+        <div class="row">
+            <div class="col-md-12">
+                <canvas id="monthly-in-out-chart"></canvas>
+            </div>
+
+        </div>
+        <div class="col-md-12 chart-container">
+            <canvas id="causals-chart" style="height: 300px; max-height: 300px;"></canvas>
+        </div>
+        <div class="row mt-5">
+            <div class="col-md-12">
+                <canvas id="tesserati-chart"></canvas>
+            </div>
+        </div>
+        <div class="row mt-5">
+            <div class="col-md-6">
+                <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>
+            <div class="col-md-12 mt-3">
+                <canvas id="courses-chart" style="height: 250px; max-height: 250px;"></canvas>
+            </div>
+        </div>
+    </section>
+
+</div>
+
+@push('scripts')
+    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+    <script>
+        document.addEventListener('DOMContentLoaded', function () {
+            initializeChartSizes();
+
+            window.livewire.on('dataUpdated', () => {
+                updateCharts();
+                updateCausalsChart();
+            });
+
+            window.livewire.on('courseDataUpdated', async (courseId) => {
+                console.log('Course data update event received for course ID:', courseId);
+                await updateCoursesChart(courseId);
+                updateCausalsChart();
+                Object.keys(window.chartSizes).forEach(chartId => {
+                    restoreChartSize(chartId);
+                });
+            });
+
+            updateCharts();
+            updateCausalsChart();
+            updateTesseratiChart();
+
+
+            async function updateCharts() {
+                try {
+                    const monthlyData = await @this.getMonthlyTotals();
+
+                    if (window.monthlyInOutChart) {
+                        window.monthlyInOutChart.destroy();
+                    }
+
+                    const monthlyInOutCtx = document.getElementById('monthly-in-out-chart').getContext('2d');
+
+                    window.monthlyInOutChart = new Chart(monthlyInOutCtx, {
+                        type: 'bar',
+                        data: monthlyData,
+                        options: {
+                            responsive: true,
+                            scales: {
+                                x: {
+                                    title: {
+                                        display: false,
+                                        text: 'Mese'
+                                    },
+                                    grid: {
+                                        display: false
+                                    }
+                                },
+                                y: {
+                                    display: false,
+                                    title: {
+                                        display: false,
+                                        text: 'Importo (€)'
+                                    },
+                                    beginAtZero: true,
+                                    ticks: {
+                                        display: false
+                                    },
+                                    grid: {
+                                        display: false
+                                    }
+                                }
+                            },
+                            plugins: {
+                                legend: {
+                                    display: true
+                                },
+                                title: {
+                                    display: true,
+                                    text: 'Entrate/Uscite Mensili',
+                                    font: {
+                                        size: 16
+                                    }
+                                },
+                                tooltip: {
+                                    callbacks: {
+                                        label: function (context) {
+                                            let label = context.dataset.label || '';
+                                            if (label) {
+                                                label += ': ';
+                                            }
+                                            if (context.parsed.y !== null) {
+                                                label += new Intl.NumberFormat('it-IT', {
+                                                    style: 'currency',
+                                                    currency: 'EUR'
+                                                }).format(context.parsed.y);
+                                            }
+                                            return label;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    });
+
+                    const summaryData = await @this.getYearlySummary();
+
+                } catch (error) {
+                    console.error('Error updating charts:', error);
+                    document.getElementById('monthly-in-out-chart').insertAdjacentHTML(
+                        'afterend',
+                        '<div class="alert alert-danger">Errore nel caricamento dei dati finanziari</div>'
+                    );
+                }
+            }
+
+            async function updateCausalsChart() {
+                try {
+                    const causalsData = await @this.getTopCausalsByAmount(10, 'IN');
+
+                    if (window.causalsChart) {
+                        window.causalsChart.destroy();
+                    }
+
+                    const causalsCtx = document.getElementById('causals-chart').getContext('2d');
+
+                    const existingTabs = document.querySelector('.causals-tabs');
+                    if (existingTabs) {
+                        existingTabs.remove();
+                    }
+
+                    const existingTitle = document.querySelector('.causals-title');
+                    if (existingTitle) {
+                        existingTitle.remove();
+                    }
+
+                    const chartTitle = document.createElement('h4');
+                    chartTitle.className = 'text-center mt-2 mb-3 causals-title';
+
+                    const chartCanvas = document.getElementById('causals-chart');
+                    chartCanvas.parentNode.insertBefore(chartTitle, chartCanvas);
+
+                    const inData = causalsData.inData;
+
+                    const colors = [
+                        'rgba(54, 162, 235, 0.8)',   // Blue
+                        'rgba(75, 192, 192, 0.8)',   // Teal
+                        'rgba(153, 102, 255, 0.8)',  // Purple
+                        'rgba(255, 159, 64, 0.8)',   // Orange
+                        'rgba(39, 174, 96, 0.8)',    // Green
+                        'rgba(41, 128, 185, 0.8)',   // Dark blue
+                        'rgba(142, 68, 173, 0.8)',   // Dark purple
+                        'rgba(230, 126, 34, 0.8)',   // Dark orange
+                        'rgba(46, 204, 113, 0.8)',   // Light green
+                        'rgba(52, 152, 219, 0.8)'    // Light blue
+                    ];
+
+                    const commonOptions = {
+                        responsive: true,
+                        maintainAspectRatio: false,
+                        plugins: {
+                            title: {
+                                display: true,
+                                text: 'Causali performanti',
+                                font: {
+                                    size: 16
+                                }
+                            },
+                            tooltip: {
+                                callbacks: {
+                                    label: function (context) {
+                                        const fullName = inData[context.dataIndex]?.fullName || context.label;
+                                        const value = context.raw;
+
+                                        return fullName + ': ' + new Intl.NumberFormat('it-IT', {
+                                            style: 'currency',
+                                            currency: 'EUR'
+                                        }).format(value);
+                                    }
+                                }
+                            },
+                            legend: {
+                                display: true,
+                                position: 'right',
+                                labels: {
+                                    boxWidth: 15,
+                                    padding: 10,
+                                    generateLabels: function (chart) {
+                                        const data = chart.data;
+                                        if (data.labels.length && data.datasets.length) {
+                                            return data.labels.map(function (label, i) {
+                                                const meta = chart.getDatasetMeta(0);
+                                                const style = meta.controller.getStyle(i);
+
+                                                let shortenedLabel = label;
+                                                if (label.length > 20) {
+                                                    shortenedLabel = label.substring(0, 17) + '...';
+                                                }
+
+                                                return {
+                                                    text: shortenedLabel,
+                                                    fillStyle: style.backgroundColor,
+                                                    hidden: !chart.getDataVisibility(i),
+                                                    index: i,
+                                                    datasetIndex: 0
+                                                };
+                                            });
+                                        }
+                                        return [];
+                                    }
+                                }
+                            }
+                        }
+                    };
+
+                    let chartData = {
+                        labels: inData.map(item => item.label),
+                        datasets: [{
+                            label: 'Importo',
+                            data: inData.map(item => item.value),
+                            backgroundColor: inData.map((item, index) => colors[index % colors.length]),
+                            borderWidth: 1,
+                            borderColor: '#fff'
+                        }]
+                    };
+
+                    window.causalsChart = new Chart(causalsCtx, {
+                        type: 'doughnut',
+                        data: chartData,
+                        options: commonOptions
+                    });
+
+                } catch (error) {
+                    console.error('Error updating causals chart:', error);
+                    document.getElementById('causals-chart').insertAdjacentHTML(
+                        'afterend',
+                        '<div class="alert alert-danger">Errore nel caricamento dei dati delle causali</div>'
+                    );
+                }
+            }
+        });
+
+
+        async function updateCoursesChart() {
+            try {
+                const courseData = await @this.getCourseMonthlyEarnings();
+                console.log('Course data received:', courseData);
+
+                if (window.coursesChart) {
+                    window.coursesChart.destroy();
+                }
+
+                const coursesCtx = document.getElementById('courses-chart').getContext('2d');
+
+                const dashedLinesPlugin = {
+                    // Plugin definition unchanged
+                    id: 'dashedLines',
+                    beforeDatasetsDraw: (chart) => {
+                        const ctx = chart.ctx;
+                        const lineDataset = chart.data.datasets.find(d => d.type === 'line' && d.label === 'Pagamenti Attesi');
+                        const barDataset = chart.data.datasets.find(d => d.type === 'bar' && d.label === 'Pagamenti Effettuati');
+
+                        if (!lineDataset || !barDataset) return;
+
+                        const lineMeta = chart.getDatasetMeta(chart.data.datasets.indexOf(lineDataset));
+                        const barMeta = chart.getDatasetMeta(chart.data.datasets.indexOf(barDataset));
+
+                        if (!lineMeta.data.length || !barMeta.data.length) return;
+
+                        const missingData = lineDataset.missing || [];
+
+                        ctx.save();
+                        ctx.lineWidth = 2;
+                        ctx.setLineDash([8, 4]);
+                        ctx.strokeStyle = 'rgba(48, 51, 107, 0.3)';
+
+                        for (let i = 0; i < lineMeta.data.length; i++) {
+                            const linePoint = lineMeta.data[i];
+                            const barPoint = barMeta.data[i];
+
+                            if (!linePoint || !barPoint) continue;
+
+                            ctx.beginPath();
+                            ctx.moveTo(linePoint.x, linePoint.y);
+                            ctx.lineTo(linePoint.x, barPoint.y);
+                            ctx.stroke();
+
+                            if (missingData[i] && missingData[i] > 0) {
+                                const midY = (linePoint.y + barPoint.y) / 2;
+                                ctx.textAlign = 'center';
+                                ctx.font = '10px Arial';
+                                ctx.fillStyle = 'rgba(48, 51, 107, 0.8)';
+                            }
+                        }
+
+                        ctx.restore();
+                    }
+                };
+
+                window.coursesChart = new Chart(coursesCtx, {
+                    type: 'bar',
+                    plugins: [dashedLinesPlugin],
+                    data: courseData,
+                    options: {
+                        responsive: true,
+                        maintainAspectRatio: false,
+                        layout: {
+                            padding: {
+                                top: 20
+                            }
+                        },
+                        // Rest of options unchanged
+                        scales: {
+                            x: {
+                                grid: {
+                                    display: false
+                                }
+                            },
+                            y: {
+                                display: true,
+                                beginAtZero: true,
+                                grid: {
+                                    color: 'rgba(0, 0, 0, 0.1)',
+                                    borderDash: [5, 5]
+                                },
+                                ticks: {
+                                    callback: function (value) {
+                                        return new Intl.NumberFormat('it-IT', {
+                                            style: 'currency',
+                                            currency: 'EUR',
+                                            maximumFractionDigits: 0
+                                        }).format(value);
+                                    }
+                                }
+                            }
+                        },
+                        plugins: {
+                            legend: {
+                                display: true,
+                                position: 'top',
+                                align: 'center',
+                                labels: {
+                                    usePointStyle: true,
+                                    padding: 20
+                                }
+                            },
+                            title: {
+                                display: true,
+                                text: 'Pagamenti per corso',
+                                font: {
+                                    size: 16
+                                }
+                            },
+                            tooltip: {
+                                callbacks: {
+                                    label: function (context) {
+                                        if (context.dataset.label === 'Pagamenti Attesi') {
+                                            let parts = [];
+
+                                            const participants = context.dataset.participants ? context.dataset.participants[context.dataIndex] : 0;
+                                            parts.push('N° iscritti: ' + participants);
+
+                                            const expectedAmount = context.parsed.y;
+                                            parts.push('Pagamenti attesi: ' + new Intl.NumberFormat('it-IT', {
+                                                style: 'currency',
+                                                currency: 'EUR'
+                                            }).format(expectedAmount));
+
+                                            const missingAmount = context.dataset.missing ? context.dataset.missing[context.dataIndex] : 0;
+                                            parts.push('Ancora da pagare: ' + new Intl.NumberFormat('it-IT', {
+                                                style: 'currency',
+                                                currency: 'EUR'
+                                            }).format(missingAmount));
+
+                                            return parts.join(' | ');
+                                        }
+
+                                        let label = context.dataset.label || '';
+                                        if (label) {
+                                            label += ': ';
+                                        }
+
+                                        if (context.parsed.y !== null) {
+                                            label += new Intl.NumberFormat('it-IT', {
+                                                style: 'currency',
+                                                currency: 'EUR'
+                                            }).format(context.parsed.y);
+                                        }
+
+                                        return label;
+                                    }
+                                }
+                            }
+                        },
+                        elements: {
+                            point: {
+                                radius: 4,
+                                hoverRadius: 6
+                            },
+                            line: {
+                                tension: 0.4
+                            }
+                        }
+                    }
+                });
+
+                // Maintain chart dimensions
+                coursesCtx.canvas.style.height = '250px';
+                coursesCtx.canvas.style.maxHeight = '250px';
+
+            } catch (error) {
+                console.error('Error updating courses chart:', error);
+                document.getElementById('courses-chart').insertAdjacentHTML(
+                    'afterend',
+                    '<div class="alert alert-danger">Errore nel caricamento dei dati del corso</div>'
+                );
+            }
+        }
+
+        async function updateTesseratiChart() {
+            try {
+                const tesseratiData = await @this.getTesseratiData();
+
+                if (window.tesseratiChart) {
+                    window.tesseratiChart.destroy();
+                }
+
+                const tesseratiCtx = document.getElementById('tesserati-chart').getContext('2d');
+
+                window.tesseratiChart = new Chart(tesseratiCtx, {
+                    type: 'line',
+                    data: tesseratiData,
+                    options: {
+                        responsive: true,
+                        scales: {
+                            x: {
+                                title: {
+                                    display: true,
+                                    text: 'Anno Tesseramento'
+                                },
+                                grid: {
+                                    display: false
+                                }
+                            },
+                            y: {
+                                title: {
+                                    display: true,
+                                    text: 'Numero di Tesserati'
+                                },
+                                beginAtZero: true,
+                                ticks: {
+                                    precision: 0
+                                }
+                            }
+                        },
+                        plugins: {
+                            legend: {
+                                display: true,
+                                position: 'top'
+                            },
+                            title: {
+                                display: true,
+                                text: 'Andamento Tesserati per Anno',
+                                font: {
+                                    size: 16
+                                }
+                            },
+                            tooltip: {
+                                callbacks: {
+                                    label: function (context) {
+                                        let label = context.dataset.label || '';
+                                        if (label) {
+                                            label += ': ';
+                                        }
+                                        if (context.parsed.y !== null) {
+                                            label += context.parsed.y + ' tesserati';
+                                        }
+                                        return label;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                });
+            } catch (error) {
+                console.error('Error updating tesserati chart:', error);
+                document.getElementById('tesserati-chart').insertAdjacentHTML(
+                    'afterend',
+                    '<div class="alert alert-danger">Errore nel caricamento dei dati tesserati</div>'
+                );
+            }
+        }
+
+        document.addEventListener('DOMContentLoaded', function () {
+            const selectElement = document.querySelector('select[name="selectedCourse"]');
+            if (selectElement) {
+                selectElement.addEventListener('change', function () {
+                    const selectedValue = this.value;
+                    console.log('Selected course ID:', selectedValue);
+                    @this.set('selectedCourse', selectedValue);
+                });
+            }
+        });
+
+        function initializeChartSizes() {
+            window.chartSizes = {
+                'monthly-in-out-chart': {
+                    height: document.getElementById('monthly-in-out-chart').style.height || 'auto',
+                    maxHeight: document.getElementById('monthly-in-out-chart').style.maxHeight || 'none'
+                },
+                'causals-chart': {
+                    height: '300px',
+                    maxHeight: '300px'
+                },
+                'tesserati-chart': {
+                    height: document.getElementById('tesserati-chart').style.height || 'auto',
+                    maxHeight: document.getElementById('tesserati-chart').style.maxHeight || 'none'
+                },
+                'courses-chart': {
+                    height: '250px',
+                    maxHeight: '250px'
+                }
+            };
+        }
+
+        function restoreChartSize(chartId) {
+            if (!window.chartSizes || !window.chartSizes[chartId]) return;
+
+            const canvas = document.getElementById(chartId);
+            if (canvas) {
+                canvas.style.height = window.chartSizes[chartId].height;
+                canvas.style.maxHeight = window.chartSizes[chartId].maxHeight;
+            }
+        }
+
+
+    </script>
+@endpush

+ 26 - 14
routes/web.php

@@ -31,8 +31,7 @@ Route::get('/login', function () {
 
 Route::post('/login', function () {
 
-    if(Auth::attempt(array('email' => $_POST["email"], 'password' => $_POST["password"])))
-    {
+    if (Auth::attempt(array('email' => $_POST["email"], 'password' => $_POST["password"]))) {
         return Redirect::to('/dashboard');
     } else {
         return Redirect::to('/?error=1');
@@ -81,6 +80,7 @@ Route::group(['middleware' => 'auth'], function () {
     Route::get('/records_in_out', \App\Http\Livewire\RecordINOUT::class);
     Route::get('/users', \App\Http\Livewire\User::class);
     Route::get('/profile', \App\Http\Livewire\Profile::class);
+    Route::get('/reports', \App\Http\Livewire\Reports::class);
 });
 
 Route::get('/receipt/{id}', function ($id) {
@@ -453,13 +453,13 @@ Route::get('/get_record_in', function () {
         ->leftJoin('receipts', 'records.id', '=', 'receipts.record_id')
         ->where('records.type', 'IN');
 
-        $x = \App\Models\Record::select('records.*', DB::raw('members.first_name as first_name'), DB::raw('members.last_name as last_name'), DB::raw('payment_methods.name as payment'), DB::raw('receipts.created_at as receipt_date')) // , \DB::raw('SUM(records.id) As total'))
-                ->leftJoin('members', 'records.member_id', '=', 'members.id')
-                ->leftJoin('payment_methods', 'records.payment_method_id', '=', 'payment_methods.id')
-                ->leftJoin('receipts', 'records.id', '=', 'receipts.record_id')
-                ->where('records.type', 'IN');
+    $x = \App\Models\Record::select('records.*', DB::raw('members.first_name as first_name'), DB::raw('members.last_name as last_name'), DB::raw('payment_methods.name as payment'), DB::raw('receipts.created_at as receipt_date')) // , \DB::raw('SUM(records.id) As total'))
+        ->leftJoin('members', 'records.member_id', '=', 'members.id')
+        ->leftJoin('payment_methods', 'records.payment_method_id', '=', 'payment_methods.id')
+        ->leftJoin('receipts', 'records.id', '=', 'receipts.record_id')
+        ->where('records.type', 'IN');
 
-        $y = \App\Models\Record::select('records_rows.amount', 'records.member_id', 'records.corrispettivo_fiscale', 'records.deleted', 'records.financial_movement', 'records_rows.causal_id', DB::raw('members.first_name as first_name'), DB::raw('members.last_name as last_name')) // , \DB::raw('SUM(records.id) As total'))
+    $y = \App\Models\Record::select('records_rows.amount', 'records.member_id', 'records.corrispettivo_fiscale', 'records.deleted', 'records.financial_movement', 'records_rows.causal_id', DB::raw('members.first_name as first_name'), DB::raw('members.last_name as last_name')) // , \DB::raw('SUM(records.id) As total'))
         ->leftJoin('members', 'records.member_id', '=', 'members.id')
         ->leftJoin('records_rows', 'records.id', '=', 'records_rows.record_id')
         //->leftJoin('receipts', 'records.id', '=', 'receipts.record_id')
@@ -568,8 +568,8 @@ Route::get('/get_record_in', function () {
         if ($borsellino)
             $excludeCausals[] = $borsellino->id;*/
 
-        // Aggiungo
-        /*
+    // Aggiungo
+    /*
         $excludes = \App\Models\Causal::where('no_records', true)->get();
         foreach($excludes as $e)
         {
@@ -1165,8 +1165,7 @@ Route::get('/get_course_members', function () {
         $datas = $datas->whereIn('member_id', $member_ids);
     }
 
-    if (isset($_GET["filterFromPrevious"]) && $_GET["filterFromPrevious"] != "")
-    {
+    if (isset($_GET["filterFromPrevious"]) && $_GET["filterFromPrevious"] != "") {
         $datas = $datas->whereIn('course_id', [$_GET["filterFromPrevious"]]);
     }
 
@@ -1331,6 +1330,8 @@ Route::get('/get_course_members', function () {
         //$this->filter .= "Anno : " . $this->filterYear . " ";
     }
 
+
+
     $aRet = [];
 
     if (isset($_GET["order"])) {
@@ -1411,6 +1412,17 @@ Route::get('/get_course_members', function () {
             $genderDisplay = "N/A";
         }
 
+        $genderDisplay = "";
+        if ($r->gender == 'M' || $r->gender == 'U') {
+            $genderDisplay = "Uomo";
+        } elseif ($r->gender == 'F' || $r->gender == 'D') {
+            $genderDisplay = "Donna";
+        } elseif ($r->gender == 'O') {
+            $genderDisplay = "Altro";
+        }elseif ($r->gender == null || $r->gender == "") {
+            $genderDisplay = "N/A";
+        }
+
         $ret[] = array(
             "column_0" => $idx + 1,
             "column_8" => $r->course_name,
@@ -1421,7 +1433,7 @@ Route::get('/get_course_members', function () {
             "column_9" => $genderDisplay,
             "column_5" => $r->phone,
             "column_6" => $certificateInfo,
-            "column_7" => $r->member_id
+            "column_7" => $r->member_id,
         );
     }
 
@@ -1527,7 +1539,7 @@ function getColor($months, $m)
     return $class;
 }
 
-Route::get('/migrate', function(){
+Route::get('/migrate', function () {
     Artisan::call('migrate');
     dd('migrated!');
 });