Parcourir la source

Gestione presenze

Luca Parisio il y a 6 mois
Parent
commit
1667696528
35 fichiers modifiés avec 2906 ajouts et 130 suppressions
  1. 109 0
      app/Http/Livewire/Calendar.php
  2. 127 0
      app/Http/Livewire/Court.php
  3. 83 0
      app/Http/Livewire/Member.php
  4. 130 0
      app/Http/Livewire/Motivation.php
  5. 272 0
      app/Http/Livewire/Presence.php
  6. 19 3
      app/Http/Livewire/Record.php
  7. 33 0
      app/Models/Calendar.php
  8. 16 0
      app/Models/Court.php
  9. 16 0
      app/Models/Motivation.php
  10. 28 0
      app/Models/Presence.php
  11. 1 0
      composer.json
  12. 285 4
      composer.lock
  13. 34 0
      database/migrations/2025_06_14_153500_create_courts_table.php
  14. 41 0
      database/migrations/2025_06_14_153700_create_calendars_table.php
  15. 39 0
      database/migrations/2025_06_14_154000_create_presences_table.php
  16. 33 0
      database/migrations/2025_06_23_101500_add_field_user_id_to_presences_table.php
  17. 34 0
      database/migrations/2025_06_23_103500_create_motivations_table.php
  18. 35 0
      database/migrations/2025_06_23_104500_add_fields_to_calendars_table.php
  19. 32 0
      database/migrations/2025_06_23_164000_add_color_to_courses_table.php
  20. 32 0
      database/migrations/2025_06_25_141000_add_status_to_presences_table.php
  21. 45 0
      database/migrations/2025_06_25_144500_add_more_fields_to_calendars_table.php
  22. 33 0
      database/migrations/2025_07_20_205000_add_motivation_id_to_presences_table.php
  23. 32 0
      database/migrations/2025_07_21_114500_add_type_to_motivations_table.php
  24. 32 0
      database/migrations/2025_07_21_123700_add_manual_to_calendars_table.php
  25. 33 0
      database/migrations/2025_07_21_153800_add_motivation_id_to_calendars_table.php
  26. 8 0
      public/assets/js/fullcalendar.js
  27. 5 0
      public/assets/js/fullcalendar_locales.js
  28. 129 119
      resources/views/layouts/app.blade.php
  29. 302 0
      resources/views/livewire/calendar.blade.php
  30. 243 0
      resources/views/livewire/court.blade.php
  31. 212 0
      resources/views/livewire/motivation.blade.php
  32. 402 0
      resources/views/livewire/presence.blade.php
  33. 1 0
      resources/views/livewire/records_in.blade.php
  34. 14 0
      resources/views/livewire/settings.blade.php
  35. 16 4
      routes/web.php

+ 109 - 0
app/Http/Livewire/Calendar.php

@@ -0,0 +1,109 @@
+<?php
+
+namespace App\Http\Livewire;
+
+use Livewire\Component;
+
+class Calendar extends Component
+{
+    public $records;
+
+    public $names = [];
+    public $course_types = [];
+    public $course_durations = [];
+    public $course_frequencies = [];
+    public $course_levels = [];
+
+    public $courts = [];
+    public $instructors = [];
+    public $motivations = [];
+
+    public $name_filter = null;
+
+    public $name = null;
+    public $from = null;
+    public $to = null;
+    
+    public $court_id = null;
+    public $instructor_id = null;
+    public $note = null;
+    
+    public $course_type_id = null;
+    public $course_duration_id = null;
+    public $course_frequency_id = null;
+    public $course_level_id = null;
+
+    public $motivation_id = null;
+
+    public function mount()
+    {
+        $this->names = \App\Models\Calendar::orderBy('name')->groupBy('name')->pluck('name')->toArray();
+        $this->course_types = \App\Models\CourseType::select('*')->where('enabled', true)->get();
+        $this->course_durations = \App\Models\CourseDuration::select('*')->where('enabled', true)->get();
+        $this->course_levels = \App\Models\CourseLevel::select('*')->where('enabled', true)->get();
+        $this->course_frequencies = \App\Models\CourseFrequency::select('*')->where('enabled', true)->get();
+        $this->courts = \App\Models\Court::select('*')->where('enabled', true)->get();
+        $this->instructors = \App\Models\User::select('*')->where('level', 2)->where('enabled', true)->get();
+        $this->motivations = \App\Models\Motivation::select('*')->where('enabled', true)->get();
+
+        if (isset($_GET["name_filter"]))
+            $this->name_filter = $_GET["name_filter"];
+
+    }
+
+    public function render()
+    {
+        $reload = false;
+        $this->records = [];
+        if ($this->name_filter != null && $this->name_filter != "")
+        {
+            $calendars = \App\Models\Calendar::where('name', $this->name_filter)->get();
+            $reload = true;
+        }
+        else
+            $calendars = \App\Models\Calendar::get();
+        foreach($calendars as $c)
+        {
+            $data = array('id' => $c->id, 'title' => $c->course ? $c->course->name : $c->name . ($c->status == 99 ? ' (annullata)' : ''), 'start' => $c->from, 'end' => $c->to);
+            if ($c->course && $c->course->color != '')
+                $data['color'] = $c->course->color;
+            $this->records[] = $data;
+        }
+        if ($reload)
+            $this->emit('reload-calendar', ["'" . json_encode($this->records) . "'"]);
+        return view('livewire.calendar');
+    }
+
+    public function createCalendar()
+    {
+
+        $calendar = new \App\Models\Calendar();
+        $calendar->course_id = null;
+        $calendar->court_id = $this->court_id;
+        $calendar->name = $this->name;
+        $calendar->course_type_id = $this->course_type_id != '' ? $this->course_type_id : null;
+        $calendar->course_duration_id = $this->course_duration_id != '' ? $this->course_duration_id : null;
+        $calendar->course_frequency_id = $this->course_frequency_id != '' ? $this->course_frequency_id : null;
+        $calendar->course_level_id = $this->course_level_id != '' ? $this->course_level_id : null;
+        $calendar->instructor_id = $this->instructor_id;
+        $calendar->from = $this->from;
+        $calendar->to = $this->to;
+        $calendar->note = $this->note;
+        $calendar->status = 0;
+        $calendar->manual = 1;
+        $calendar->save();
+
+        return redirect()->to('/presences?calendarId=' . $calendar->id);
+
+    }
+
+    public function cancelCalendar($id, $motivation_id)
+    {
+        $calendar = \App\Models\Calendar::findOrFail($id);
+        $calendar->motivation_id = $motivation_id;
+        $calendar->status = 99;
+        $calendar->save();
+        return redirect()->to('/calendar');
+    }
+
+}

+ 127 - 0
app/Http/Livewire/Court.php

@@ -0,0 +1,127 @@
+<?php
+
+namespace App\Http\Livewire;
+
+use Livewire\Component;
+
+class Court extends Component
+{
+    public $records, $name, $enabled, $dataId, $update = false, $add = false;
+
+    protected $rules = [
+        'name' => 'required'
+    ];
+
+    protected $messages = [
+        'name.required' => 'Il nome è obbligatorio'
+    ];
+
+    public $sortField ='name';
+    public $sortAsc = true;
+
+    public function mount(){
+
+        if(\Auth::user()->level != env('LEVEL_ADMIN', 0))
+            return redirect()->to('/dashboard');
+
+    }
+
+    public function sortBy($field)
+    {
+        if($this->sortField === $field)
+        {
+            $this->sortAsc = ! $this->sortAsc;
+        } else {
+            $this->sortAsc = true;
+        }
+
+        $this->sortField = $field;
+    }
+
+    public function resetFields(){
+        $this->name = '';
+        $this->enabled = true;
+        $this->emit('load-data-table');
+    }
+
+    public function render()
+    {
+        //$this->records = \App\Models\Court::select('id', 'name', 'enabled')->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc')->get();
+        $this->records = \App\Models\Court::select('id', 'name', 'enabled')->get();
+        return view('livewire.court');
+    }
+
+    public function add()
+    {
+        $this->resetFields();
+        $this->add = true;
+        $this->update = false;
+    }
+
+    public function store()
+    {
+        $this->validate();
+        try {
+            \App\Models\Court::create([
+                'name' => $this->name,
+                'enabled' => $this->enabled
+            ]);
+            session()->flash('success','Campo creata');
+            $this->resetFields();
+            $this->add = false;
+        } catch (\Exception $ex) {
+            session()->flash('error','Errore (' . $ex->getMessage() . ')');
+        }
+    }
+
+    public function edit($id){
+        try {
+            $court = \App\Models\Court::findOrFail($id);
+            if( !$court) {
+                session()->flash('error','Campo non trovata');
+            } else {
+                $this->name = $court->name;
+                $this->enabled = $court->enabled;
+                $this->dataId = $court->id;
+                $this->update = true;
+                $this->add = false;
+            }
+        } catch (\Exception $ex) {
+            session()->flash('error','Errore (' . $ex->getMessage() . ')');
+        }
+    }
+
+    public function update()
+    {
+        $this->validate();
+        try {
+            \App\Models\Court::whereId($this->dataId)->update([
+                'name' => $this->name,
+                'enabled' => $this->enabled
+            ]);
+            session()->flash('success','Campo aggiornata');
+            $this->resetFields();
+            $this->update = false;
+        } catch (\Exception $ex) {
+            session()->flash('error','Errore (' . $ex->getMessage() . ')');
+        }
+    }
+
+    public function cancel()
+    {
+        $this->add = false;
+        $this->update = false;
+        $this->resetFields();
+    }
+
+    public function delete($id)
+    {
+        try{
+            \App\Models\Court::find($id)->delete();
+            session()->flash('success',"Campo eliminata");
+            return redirect(request()->header('Referer'));
+        }catch(\Exception $e){
+            session()->flash('error','Errore (' . $ex->getMessage() . ')');
+        }
+    }
+}

+ 83 - 0
app/Http/Livewire/Member.php

@@ -1453,6 +1453,8 @@ class Member extends Component
                 'months' => json_encode($this->course_months),
                 'when' => json_encode($this->course_when)
             ]);
+
+            $course_name = '';
             // Se il corso ha associato una categoria iscrivo anche al gruppo
             $c = \App\Models\Course::findOrFail($this->course_course_id);
             if ($c) {
@@ -1463,7 +1465,88 @@ class Member extends Component
                         'date' => \Carbon\Carbon::now()
                     ]);
                 }
+                $course_name = $c->name;
             }
+
+            $days = array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday');
+
+            //foreach($this->course_months as $m)
+            //{
+
+                $from = date("Y-m-d", strtotime($c->date_from));
+                $to = date("Y-m-d", strtotime($c->date_to));
+
+                $endDate = strtotime($to);
+
+                foreach($this->course_when as $d)
+                {
+
+                    foreach($d["day"] as $dd)
+                    {
+
+                        $day = '';
+                        switch ($dd) {
+                            case 'lun':
+                                $day = $days[0];
+                                break;
+                            case 'mar':
+                                $day = $days[1];
+                                break;
+                            case 'mer':
+                                $day = $days[2];
+                                break;
+                            case 'gio':
+                                $day = $days[3];
+                                break;
+                            case 'ven':
+                                $day = $days[4];
+                                break;
+                            case 'sab':
+                                $day = $days[5];
+                                break;
+                            case 'dom':
+                                $day = $days[6];
+                                break;
+                            default:
+                                $day = '';
+                                break;
+                        }
+                        
+                        if ($day != '')
+                        {
+                            for($i = strtotime($day, strtotime($from)); $i <= $endDate; $i = strtotime('+1 week', $i))
+                            {
+
+                                // Controllo che non esiste un corso così
+                                $exist = \App\Models\Calendar::where('from', date('Y-m-d ' . $d["from"] . ":00", $i))->where('to', date('Y-m-d ' . $d["to"] . ":00", $i))->where('name', $course_name)->first();
+
+                                if (!$exist)
+                                {
+
+                                    // Creo il calendario del corso
+                                    $calendar = new \App\Models\Calendar();
+                                    $calendar->course_id = $this->course_course_id;
+                                    $calendar->court_id = null;
+                                    $calendar->name = $course_name;
+                                    $calendar->course_type_id = null;
+                                    $calendar->course_duration_id = null;
+                                    $calendar->course_frequency_id = null;
+                                    $calendar->course_level_id = null;
+                                    $calendar->instructor_id = null;
+                                    $calendar->from = date('Y-m-d ' . $d["from"] . ":00", $i);
+                                    $calendar->to = date('Y-m-d ' . $d["to"] . ":00", $i);
+                                    $calendar->note = '';
+                                    $calendar->status = 0;
+                                    $calendar->save();
+
+                                }
+                                
+                            }
+                        }
+                    }
+                }
+            //}
+
             session()->flash('success, Corso creato');
             $this->resetCourseFields();
             $this->addCourse = false;

+ 130 - 0
app/Http/Livewire/Motivation.php

@@ -0,0 +1,130 @@
+<?php
+
+namespace App\Http\Livewire;
+
+use Livewire\Component;
+
+class Motivation extends Component
+{
+    public $records, $name, $type, $enabled, $dataId, $update = false, $add = false;
+
+    protected $rules = [
+        'name' => 'required'
+    ];
+
+    protected $messages = [
+        'name.required' => 'Il nome è obbligatorio'
+    ];
+
+    public $sortField ='name';
+    public $sortAsc = true;
+
+    public function mount(){
+
+        if(\Auth::user()->level != env('LEVEL_ADMIN', 0))
+            return redirect()->to('/dashboard');
+
+    }
+
+    public function sortBy($field)
+    {
+        if($this->sortField === $field)
+        {
+            $this->sortAsc = ! $this->sortAsc;
+        } else {
+            $this->sortAsc = true;
+        }
+
+        $this->sortField = $field;
+    }
+
+    public function resetFields(){
+        $this->name = '';
+        $this->type = '';
+        $this->enabled = true;
+        $this->emit('load-data-table');
+    }
+
+    public function render()
+    {
+        $this->records = \App\Models\Motivation::select('id', 'name', 'type', 'enabled')->get();
+        return view('livewire.motivation');
+    }
+
+    public function add()
+    {
+        $this->resetFields();
+        $this->add = true;
+        $this->update = false;
+    }
+
+    public function store()
+    {
+        $this->validate();
+        try {
+            \App\Models\Motivation::create([
+                'name' => $this->name,
+                'type' => $this->type,
+                'enabled' => $this->enabled
+            ]);
+            session()->flash('success','Campo creata');
+            $this->resetFields();
+            $this->add = false;
+        } catch (\Exception $ex) {
+            session()->flash('error','Errore (' . $ex->getMessage() . ')');
+        }
+    }
+
+    public function edit($id){
+        try {
+            $motivation = \App\Models\Motivation::findOrFail($id);
+            if( !$motivation) {
+                session()->flash('error','Campo non trovata');
+            } else {
+                $this->name = $motivation->name;
+                $this->type = $motivation->type;
+                $this->enabled = $motivation->enabled;
+                $this->dataId = $motivation->id;
+                $this->update = true;
+                $this->add = false;
+            }
+        } catch (\Exception $ex) {
+            session()->flash('error','Errore (' . $ex->getMessage() . ')');
+        }
+    }
+
+    public function update()
+    {
+        $this->validate();
+        try {
+            \App\Models\Motivation::whereId($this->dataId)->update([
+                'name' => $this->name,
+                'type' => $this->type,
+                'enabled' => $this->enabled
+            ]);
+            session()->flash('success','Campo aggiornata');
+            $this->resetFields();
+            $this->update = false;
+        } catch (\Exception $ex) {
+            session()->flash('error','Errore (' . $ex->getMessage() . ')');
+        }
+    }
+
+    public function cancel()
+    {
+        $this->add = false;
+        $this->update = false;
+        $this->resetFields();
+    }
+
+    public function delete($id)
+    {
+        try{
+            \App\Models\Motivation::find($id)->delete();
+            session()->flash('success',"Campo eliminata");
+            return redirect(request()->header('Referer'));
+        }catch(\Exception $e){
+            session()->flash('error','Errore (' . $ex->getMessage() . ')');
+        }
+    }
+}

+ 272 - 0
app/Http/Livewire/Presence.php

@@ -0,0 +1,272 @@
+<?php
+
+namespace App\Http\Livewire;
+
+use Livewire\Component;
+
+class Presence extends Component
+{
+
+    public $calendar;
+
+    public $records;
+
+    public $court_id, $instructor_id, $motivation_id, $motivation_manual_id, $note, $manual;
+
+    public $newMemberFirstName, $newMemberLastName, $newMemberEmail, $newMemberToComplete, $newMemberFiscalCode, $newMemberFiscalCodeExist, $newMemberMotivationId;
+
+    public $userName, $userEmail;
+
+    public $added = false;
+
+    public $courts = [];
+    public $instructors = [];
+    public $motivations = [];
+    public $motivations_add = [];
+
+    public $members = [];
+
+    public $newMembers = [];
+
+    public $ids = [];
+
+    public function mount()
+    {
+        $this->calendar = \App\Models\Calendar::findOrFail($_GET["calendarId"]);
+        $this->court_id = $this->calendar->court_id;
+        $this->instructor_id = $this->calendar->instructor_id;
+        $this->motivation_manual_id = $this->calendar->motivation_manual_id;
+        $this->manual = $this->calendar->manual;
+        $this->note = $this->calendar->note;
+        $this->courts = \App\Models\Court::select('*')->where('enabled', true)->get();
+        $this->instructors = \App\Models\User::select('*')->where('level', 2)->where('enabled', true)->get();
+        $this->motivations = \App\Models\Motivation::select('*')->where('enabled', true)->where('type', 'del')->get();
+        $this->motivations_add = \App\Models\Motivation::select('*')->where('enabled', true)->where('type', 'add')->get();
+        $this->members = \App\Models\Member::select(['id', 'first_name', 'last_name', 'fiscal_code'])->orderBy('last_name')->orderBy('first_name')->get();
+    }
+
+    public function render()
+    {
+        
+        $this->records = [];
+
+        $presenceMembers = [];
+
+        if (!$this->manual)
+        {
+        
+            // Carco tutti gli iscritti a un corso padre in quel giorno con un range orario simile
+            $days = ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'];
+            $dow = date('w', strtotime($this->calendar->from));
+            $d = $days[$dow];
+
+            // Elenco corsi per tipologia in base al calendario
+            $courses = \App\Models\Course::where('name', $this->calendar->name)->where('date_from', '<=', $this->calendar->from)->where('date_to', '>=', $this->calendar->to)->pluck('id')->toArray();
+
+            // Elenco utenti iscritti al corso "padre"
+            $members_courses = \App\Models\MemberCourse::where('when', 'like', "%" . $d . "%")->whereIn('course_id', $courses)->pluck('member_id')->toArray();
+
+            $members = \App\Models\Member::whereIn('id', $members_courses)->orderBy('last_name')->orderBy('first_name')->get();
+
+            // $presences = \App\Models\Presence::where('calendar_id', $this->calendar->id)->pluck('member_id')->toArray();
+            // $my_presences = \App\Models\Presence::where('calendar_id', $this->calendar->id)->where('user_id', \Auth::user()->id)->pluck('member_id')->toArray();
+
+            foreach($members as $member)
+            {
+
+                $presenceMembers[] = $member->id;
+                $this->records[] = $this->getMember($member);
+            }
+
+        }
+
+        // Aggiungo i membri iscritti
+        $members_presences = \App\Models\Presence::where('calendar_id', $this->calendar->id)->whereNotIn('member_id', $presenceMembers)->pluck('member_id')->toArray();
+        $members = \App\Models\Member::whereIn('id', $members_presences)->get();
+        foreach($members as $member)
+        {
+            $this->records[] = $this->getMember($member);
+        }
+
+        foreach($this->newMembers as $m)
+        {
+            $member = \App\Models\Member::findOrFail($m);
+            $this->records[] = $this->getMember($member);
+        }
+
+        /*$calendars = \App\Models\Calendar::get();
+        foreach($calendars as $c)
+        {
+            $this->records[] = array('id' => $c->id, 'title' => $c->course->name, 'start' => $c->from, 'end' => $c->to);
+        }*/
+        return view('livewire.presence');
+    }
+
+    public function getMember($member)
+    {
+        $latestCert = \App\Models\MemberCertificate::where('member_id', $member->id)
+            ->orderBy('expire_date', 'desc')
+            ->first();
+
+        $certificate = '';
+        $y = "|";
+        if ($latestCert) 
+        {
+            $latest_date = $latestCert->expire_date;
+            $status = '';
+            if ($latest_date < date("Y-m-d")) {
+                $status = "0"; // Expired
+            } else if ($latest_date <= date("Y-m-d", strtotime("+1 month"))) {
+                $status = "1"; // Expiring soon
+            } else {
+                $status = "2"; // Valid
+            }
+            $y = $status . "|" . date("d/m/Y", strtotime($latest_date));
+        }
+
+        $presence = false;
+        $my_presence = false;
+        $status = 0;
+
+        $has_presence = \App\Models\Presence::where('calendar_id', $this->calendar->id)->where('member_id', $member->id)->first();
+        if ($has_presence)            
+        {
+            $presence = true;
+            $my_presence = $has_presence->user_id == \Auth::user()->id;
+            $status = $has_presence->status;
+        }
+
+        return array('id' => $member->id, 'first_name' => $member->first_name, 'last_name' => $member->last_name, 'certificate' => $y, 'presence' => $presence, 'my_presence' => $my_presence, 'status' => $status);
+
+    }
+
+    public function save($ids)
+    {
+
+        $this->calendar->court_id = $this->court_id;
+        $this->calendar->instructor_id = $this->instructor_id;
+        $this->calendar->note = $this->note;
+        if ($this->motivation_id != "" && $this->motivation_id != null)
+            $this->calendar->motivation_id = $this->motivation_id;
+        if ($this->motivation_manual_id != "" && $this->motivation_manual_id != null)
+            $this->calendar->motivation_manual_id = $this->motivation_manual_id;
+        $this->calendar->save();
+
+        \App\Models\Presence::where('calendar_id', $this->calendar->id)->where('user_id', \Auth::user()->id)->where('status', '<>', 99)->delete();
+        foreach($ids as $id)
+        {
+            $p = new \App\Models\Presence();
+            $p->member_id = $id;
+            $p->calendar_id = $this->calendar->id;
+            $p->user_id = \Auth::user()->id;
+            $p->status = 0;
+            $p->save();
+        }
+        return redirect()->to('/calendar');
+
+    }
+
+    public function cancel($ids, $motivation_id)
+    {
+
+        $presences = \App\Models\Presence::where('calendar_id', $this->calendar->id)->whereIn('member_id', $ids)->get();
+        foreach($presences as $presence)
+        {
+            $presence->motivation_id = $motivation_id;
+            $presence->status = 99;
+            $presence->save();
+        }
+        return redirect()->to('/presences?calendarId=' . $this->calendar->id);
+
+    }
+
+    public function addMember($id)
+    {
+
+        $this->added = true;
+        //if (!in_array($id, $this->newMembers))
+        //    $this->newMembers[] = $id;
+        $this->member_id = $id;
+        $this->emit('reload');
+
+    }
+
+    public function cancelCalendar()
+    {
+        $this->calendar->motivation_id = $this->motivation_id;
+        $this->calendar->status = 99;
+        $this->calendar->save();
+        return redirect()->to('/calendar');
+    }
+
+    public function createMember()
+    {
+
+        if (!$this->added)
+        {
+            $this->newMemberFiscalCodeExist = false;
+            $this->validate([
+                // 'newMemberFiscalCode'=>'required|max:16',
+                'newMemberFirstName'=>'required',
+                'newMemberLastName'=>'required',
+                //'newMemberEmail'=>'required',
+            ]);
+
+            // Check fiscal code exist
+            $exist = false;
+            if ($this->newMemberFiscalCode != '')
+            {
+                $check = \App\Models\Member::where('fiscal_code', $this->newMemberFiscalCode)->get();
+                $exist = $check->count() > 0;
+            }
+            if (!$exist)
+            {
+                $member = \App\Models\Member::create([
+                    'first_name' => strtoupper($this->newMemberFirstName),
+                    'last_name' => strtoupper($this->newMemberLastName),
+                    'email' => strtoupper($this->newMemberEmail),
+                    'to_complete' => $this->newMemberToComplete,
+                    'fiscal_code' => $this->newMemberFiscalCode,
+                    'status' => true
+                ]);
+
+                if (!in_array($member->id, $this->newMembers))
+                    $this->newMembers[] = $member->id;
+                $this->emit('reload');
+                $this->emit('saved');
+            }
+            else
+            {
+                $this->newMemberFiscalCodeExist = true;
+            }
+        }
+        else
+        {
+            if (!in_array($this->member_id, $this->newMembers))
+                $this->newMembers[] = $this->member_id;
+            $this->member_id = 0;
+            $this->added = false;
+            $this->emit('reload');
+            $this->emit('saved');
+        }
+    }
+
+    public function createInstructor()
+    {
+        
+        $user = \App\Models\User::create([
+            'name' => $this->userName,
+            'email' => $this->userEmail,
+            'password' => '',
+            'level' => 2,
+            'enabled' => true
+        ]);
+
+        $this->instructor_id = $user->id;
+        $this->instructors = \App\Models\User::select('*')->where('level', 2)->where('enabled', true)->get();
+        $this->emit('saved');        
+
+    }
+
+
+}

+ 19 - 3
app/Http/Livewire/Record.php

@@ -203,9 +203,9 @@ class Record extends Component
                     }
 
                     // CALCULATE TOTALS HERE (in the same loop)
-                    if (!$data->deleted) {
+                    /*if (!$data->deleted) {
                         $exportTotals[$data->payment_method->name][$data->type] += $amount;
-                    }
+                    }*/
 
                     $isCommercial = ($data->commercial == 1 || $data->commercial === '1' || $data->commercial === true);
                     $typeLabel = $isCommercial ? 'Commerciale' : 'Non Commerciale';
@@ -272,6 +272,9 @@ class Record extends Component
         Log::info('generateExportDataAndTotals: Building final export records');
         $finalStart = microtime(true);
 
+        $tot = 0;
+        $count = 0;
+
         foreach ($groupedData as $groupKey => $group) {
             $causalsInGroup = array_keys($causalsCount[$groupKey]);
 
@@ -290,6 +293,10 @@ class Record extends Component
             }
 
             $exportRecords[$recordKey][$group['payment_method']][$group['transaction_type']] += $group['amount'];
+
+            if (!$group['deleted'])
+                $exportTotals[$group['payment_method']][$group['transaction_type']] += $group['amount'];
+
         }
 
         $finalTime = microtime(true) - $finalStart;
@@ -608,6 +615,8 @@ class Record extends Component
         $causalsAmounts = [];
         $nominativi = [];
 
+        $values = [];
+
         foreach ($datas as $idx => $data) {
 
             $causalCheck = \App\Models\Causal::findOrFail($data->causal_id);
@@ -617,7 +626,8 @@ class Record extends Component
 
                 if (!$data->deleted) {
                     $amount = $data->amount;
-                    $amount += getVatValue($amount, $data->vat_id);
+                    if ($data->vat_id > 0)
+                        $amount += getVatValue($amount, $data->vat_id);
                 } else {
                     $amount = $data->amount;
                 }
@@ -630,6 +640,8 @@ class Record extends Component
                     if ($data->member) {
                         $nominativo = $data->member->last_name . " " . $data->member->first_name;
                     }
+                    if ($data->payment_method_id == 7)
+                        $values[] = array('id' => $data->record_id, 'amount' => $amount);
                 } else {
                     if ($data->supplier) {
                         $nominativo = $data->supplier->name;
@@ -673,6 +685,8 @@ class Record extends Component
             }
         }
 
+        Log::info('values', [$values]);
+
         foreach ($groupedData as $groupKey => $group) {
             $causalsInGroup = array_keys($causalsCount[$groupKey]);
 
@@ -809,6 +823,8 @@ class Record extends Component
             $exportRecords = $result['records'];
             $exportTotals = $result['totals'];
 
+            Log::info('TOTALS', [$exportTotals]);
+
             $dataGenTime = microtime(true) - $startTime;
             Log::info('Export: COMBINED data generation completed (NO SEPARATE TOTALS CALL)', [
                 'records_count' => count($exportRecords),

+ 33 - 0
app/Models/Calendar.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class Calendar extends Model
+{
+    use HasFactory;
+
+    protected $fillable = [
+        'course_id',
+        'court_id',
+        'instructor_id',
+        'from',
+        'to',
+        'note',
+        'motivation_id',
+        'status',
+        'course_type_id',
+        'course_duration_id',
+        'course_frequency_id',
+        'course_level_id',
+        'manual',
+        'motivation_manual_id'
+    ];
+
+    public function course()
+    {
+        return $this->belongsTo(\App\Models\Course::class);
+    }
+}

+ 16 - 0
app/Models/Court.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class Court extends Model
+{
+    use HasFactory;
+
+    protected $fillable = [
+        'name',
+        'enabled'
+    ];
+}

+ 16 - 0
app/Models/Motivation.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class Motivation extends Model
+{
+    use HasFactory;
+
+    protected $fillable = [
+        'name',
+        'enabled'
+    ];
+}

+ 28 - 0
app/Models/Presence.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class Presence extends Model
+{
+    use HasFactory;
+
+    protected $fillable = [
+        'member_id',
+        'calendar_id',
+        'member_course_id',
+        'user_id',
+    ];
+
+    public function member()
+    {
+        return $this->belongsTo(\App\Models\Member::class);
+    }
+
+    public function calendar()
+    {
+        return $this->belongsTo(\App\Models\Calendar::class);
+    }
+}

+ 1 - 0
composer.json

@@ -11,6 +11,7 @@
         "laravel/framework": "^9.19",
         "laravel/sanctum": "^3.0",
         "laravel/tinker": "^2.7",
+        "league/flysystem-aws-s3-v3": "^3.0",
         "livewire/livewire": "^2.12",
         "phpoffice/phpspreadsheet": "^2.0"
     },

+ 285 - 4
composer.lock

@@ -4,8 +4,157 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "929f88783ff020750e47e3ae1b2963b4",
+    "content-hash": "8aacf85495c8b6cb33caeb8ac21b78c3",
     "packages": [
+        {
+            "name": "aws/aws-crt-php",
+            "version": "v1.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/awslabs/aws-crt-php.git",
+                "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e",
+                "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35||^5.6.3||^9.5",
+                "yoast/phpunit-polyfills": "^1.0"
+            },
+            "suggest": {
+                "ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality."
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "AWS SDK Common Runtime Team",
+                    "email": "aws-sdk-common-runtime@amazon.com"
+                }
+            ],
+            "description": "AWS Common Runtime for PHP",
+            "homepage": "https://github.com/awslabs/aws-crt-php",
+            "keywords": [
+                "amazon",
+                "aws",
+                "crt",
+                "sdk"
+            ],
+            "support": {
+                "issues": "https://github.com/awslabs/aws-crt-php/issues",
+                "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7"
+            },
+            "time": "2024-10-18T22:15:13+00:00"
+        },
+        {
+            "name": "aws/aws-sdk-php",
+            "version": "3.263.13",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/aws/aws-sdk-php.git",
+                "reference": "939120791996563677afe75a97ff18f514b7418f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/939120791996563677afe75a97ff18f514b7418f",
+                "reference": "939120791996563677afe75a97ff18f514b7418f",
+                "shasum": ""
+            },
+            "require": {
+                "aws/aws-crt-php": "^1.0.4",
+                "ext-json": "*",
+                "ext-pcre": "*",
+                "ext-simplexml": "*",
+                "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5",
+                "guzzlehttp/promises": "^1.4.0",
+                "guzzlehttp/psr7": "^1.8.5 || ^2.3",
+                "mtdowling/jmespath.php": "^2.6",
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "andrewsville/php-token-reflection": "^1.4",
+                "aws/aws-php-sns-message-validator": "~1.0",
+                "behat/behat": "~3.0",
+                "composer/composer": "^1.10.22",
+                "dms/phpunit-arraysubset-asserts": "^0.4.0",
+                "doctrine/cache": "~1.4",
+                "ext-dom": "*",
+                "ext-openssl": "*",
+                "ext-pcntl": "*",
+                "ext-sockets": "*",
+                "nette/neon": "^2.3",
+                "paragonie/random_compat": ">= 2",
+                "phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5",
+                "psr/cache": "^1.0",
+                "psr/http-message": "<1.1",
+                "psr/simple-cache": "^1.0",
+                "sebastian/comparator": "^1.2.3 || ^4.0",
+                "yoast/phpunit-polyfills": "^1.0"
+            },
+            "suggest": {
+                "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
+                "doctrine/cache": "To use the DoctrineCacheAdapter",
+                "ext-curl": "To send requests using cURL",
+                "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages",
+                "ext-sockets": "To use client-side monitoring"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "Aws\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Amazon Web Services",
+                    "homepage": "http://aws.amazon.com"
+                }
+            ],
+            "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
+            "homepage": "http://aws.amazon.com/sdkforphp",
+            "keywords": [
+                "amazon",
+                "aws",
+                "cloud",
+                "dynamodb",
+                "ec2",
+                "glacier",
+                "s3",
+                "sdk"
+            ],
+            "support": {
+                "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
+                "issues": "https://github.com/aws/aws-sdk-php/issues",
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.263.13"
+            },
+            "time": "2023-04-19T18:23:42+00:00"
+        },
         {
             "name": "barryvdh/laravel-dompdf",
             "version": "v2.1.1",
@@ -1834,6 +1983,72 @@
             ],
             "time": "2023-02-18T15:32:41+00:00"
         },
+        {
+            "name": "league/flysystem-aws-s3-v3",
+            "version": "3.23.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
+                "reference": "97728e7a0d40ec9c6147eb0f4ee4cdc6ff0a8240"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/97728e7a0d40ec9c6147eb0f4ee4cdc6ff0a8240",
+                "reference": "97728e7a0d40ec9c6147eb0f4ee4cdc6ff0a8240",
+                "shasum": ""
+            },
+            "require": {
+                "aws/aws-sdk-php": "^3.220.0",
+                "league/flysystem": "^3.10.0",
+                "league/mime-type-detection": "^1.0.0",
+                "php": "^8.0.2"
+            },
+            "conflict": {
+                "guzzlehttp/guzzle": "<7.0",
+                "guzzlehttp/ringphp": "<1.1.1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\Flysystem\\AwsS3V3\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "info@frankdejonge.nl"
+                }
+            ],
+            "description": "AWS S3 filesystem adapter for Flysystem.",
+            "keywords": [
+                "Flysystem",
+                "aws",
+                "file",
+                "files",
+                "filesystem",
+                "s3",
+                "storage"
+            ],
+            "support": {
+                "issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues",
+                "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.23.1"
+            },
+            "funding": [
+                {
+                    "url": "https://ecologi.com/frankdejonge",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/frankdejonge",
+                    "type": "github"
+                }
+            ],
+            "time": "2024-01-26T18:25:23+00:00"
+        },
         {
             "name": "league/mime-type-detection",
             "version": "1.11.0",
@@ -2320,6 +2535,72 @@
             ],
             "time": "2023-02-06T13:44:46+00:00"
         },
+        {
+            "name": "mtdowling/jmespath.php",
+            "version": "2.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/jmespath/jmespath.php.git",
+                "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc",
+                "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "symfony/polyfill-mbstring": "^1.17"
+            },
+            "require-dev": {
+                "composer/xdebug-handler": "^3.0.3",
+                "phpunit/phpunit": "^8.5.33"
+            },
+            "bin": [
+                "bin/jp.php"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.8-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/JmesPath.php"
+                ],
+                "psr-4": {
+                    "JmesPath\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Declaratively specify how to extract elements from a JSON document",
+            "keywords": [
+                "json",
+                "jsonpath"
+            ],
+            "support": {
+                "issues": "https://github.com/jmespath/jmespath.php/issues",
+                "source": "https://github.com/jmespath/jmespath.php/tree/2.8.0"
+            },
+            "time": "2024-09-04T18:46:31+00:00"
+        },
         {
             "name": "nesbot/carbon",
             "version": "2.66.0",
@@ -9027,12 +9308,12 @@
     ],
     "aliases": [],
     "minimum-stability": "stable",
-    "stability-flags": [],
+    "stability-flags": {},
     "prefer-stable": true,
     "prefer-lowest": false,
     "platform": {
         "php": "^8.0.2"
     },
-    "platform-dev": [],
-    "plugin-api-version": "2.3.0"
+    "platform-dev": {},
+    "plugin-api-version": "2.6.0"
 }

+ 34 - 0
database/migrations/2025_06_14_153500_create_courts_table.php

@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('courts', function (Blueprint $table) {
+            $table->id();
+            $table->string('name');
+            $table->integer('enabled')->default(1);
+            $table->softDeletes();
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('courts');
+    }
+};

+ 41 - 0
database/migrations/2025_06_14_153700_create_calendars_table.php

@@ -0,0 +1,41 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('calendars', function (Blueprint $table) {
+            $table->id();
+            $table->unsignedBigInteger('course_id')->nullable();
+            $table->foreign('course_id')->nullable()->references('id')->on('courses')->onUpdate('cascade')->onDelete('cascade');
+            $table->unsignedBigInteger('court_id')->nullable();
+            $table->foreign('court_id')->nullable()->references('id')->on('courts')->onUpdate('cascade')->onDelete('cascade');
+            $table->unsignedBigInteger('instructor_id')->nullable();
+            $table->foreign('instructor_id')->nullable()->references('id')->on('users')->onUpdate('cascade')->onDelete('cascade');
+            $table->datetime('from')->nullable();
+            $table->datetime('to')->nullable();
+            $table->string('note')->nullable();
+            $table->softDeletes();
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('calendars');
+    }
+};

+ 39 - 0
database/migrations/2025_06_14_154000_create_presences_table.php

@@ -0,0 +1,39 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('presences', function (Blueprint $table) {
+            $table->id();
+            $table->unsignedBigInteger('calendar_id')->nullable();
+            $table->foreign('calendar_id')->nullable()->references('id')->on('calendars')->onUpdate('cascade')->onDelete('cascade');
+            $table->unsignedBigInteger('member_id')->nullable();
+            $table->foreign('member_id')->nullable()->references('id')->on('members')->onUpdate('cascade')->onDelete('cascade');
+            $table->unsignedBigInteger('member_course_id')->nullable();
+            $table->foreign('member_course_id')->nullable()->references('id')->on('member_courses')->onUpdate('cascade')->onDelete('cascade');
+
+            $table->softDeletes();
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('presences');
+    }
+};

+ 33 - 0
database/migrations/2025_06_23_101500_add_field_user_id_to_presences_table.php

@@ -0,0 +1,33 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+        /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('presences', function (Blueprint $table) {
+            $table->unsignedBigInteger('user_id')->nullable();
+            $table->foreign('user_id')->nullable()->references('id')->on('users')->onUpdate('cascade')->onDelete('cascade');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('presences', function (Blueprint $table) {
+            $table->dropColumn('user_id');
+        });
+    }
+};

+ 34 - 0
database/migrations/2025_06_23_103500_create_motivations_table.php

@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('motivations', function (Blueprint $table) {
+            $table->id();
+            $table->string('name');
+            $table->integer('enabled')->default(1);
+            $table->softDeletes();
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('motivations');
+    }
+};

+ 35 - 0
database/migrations/2025_06_23_104500_add_fields_to_calendars_table.php

@@ -0,0 +1,35 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+        /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('calendars', function (Blueprint $table) {
+            $table->unsignedBigInteger('motivation_id')->nullable();
+            $table->foreign('motivation_id')->nullable()->references('id')->on('motivations')->onUpdate('cascade')->onDelete('cascade');
+            $table->integer('status');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('calendars', function (Blueprint $table) {
+            $table->dropColumn('motivation_id');
+            $table->dropColumn('status');
+        });
+    }
+};

+ 32 - 0
database/migrations/2025_06_23_164000_add_color_to_courses_table.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+        /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('courses', function (Blueprint $table) {
+            $table->string('color')->nullable();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('courses', function (Blueprint $table) {
+            $table->dropColumn('color');
+        });
+    }
+};

+ 32 - 0
database/migrations/2025_06_25_141000_add_status_to_presences_table.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+        /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('presences', function (Blueprint $table) {
+            $table->integer('status');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('presences', function (Blueprint $table) {
+            $table->dropColumn('status');
+        });
+    }
+};

+ 45 - 0
database/migrations/2025_06_25_144500_add_more_fields_to_calendars_table.php

@@ -0,0 +1,45 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+        /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('calendars', function (Blueprint $table) {
+            $table->string('name')->nullable();
+            $table->unsignedBigInteger('course_type_id')->nullable();
+            $table->foreign('course_type_id')->nullable()->references('id')->on('course_types')->onUpdate('cascade')->onDelete('cascade');
+            $table->unsignedBigInteger('course_duration_id')->nullable();
+            $table->foreign('course_duration_id')->nullable()->references('id')->on('course_durations')->onUpdate('cascade')->onDelete('cascade');
+            $table->unsignedBigInteger('course_frequency_id')->nullable();
+            $table->foreign('course_frequency_id')->nullable()->references('id')->on('course_frequencies')->onUpdate('cascade')->onDelete('cascade');
+            $table->unsignedBigInteger('course_level_id')->nullable();
+            $table->foreign('course_level_id')->nullable()->references('id')->on('course_levels')->onUpdate('cascade')->onDelete('cascade');
+            
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('calendars', function (Blueprint $table) {
+            $table->dropColumn('name');
+            $table->dropColumn('course_type_id');
+            $table->dropColumn('course_duration_id');
+            $table->dropColumn('course_frequency_id');
+            $table->dropColumn('course_level_id');
+        });
+    }
+};

+ 33 - 0
database/migrations/2025_07_20_205000_add_motivation_id_to_presences_table.php

@@ -0,0 +1,33 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+        /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('presences', function (Blueprint $table) {
+            $table->unsignedBigInteger('motivation_id')->nullable();
+            $table->foreign('motivation_id')->nullable()->references('id')->on('motivations')->onUpdate('cascade')->onDelete('cascade');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('presences', function (Blueprint $table) {
+            $table->dropColumn('motivation_id');
+        });
+    }
+};

+ 32 - 0
database/migrations/2025_07_21_114500_add_type_to_motivations_table.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+        /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('motivations', function (Blueprint $table) {
+            $table->string('type', 10)->nullable();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('motivations', function (Blueprint $table) {
+            $table->dropColumn('type');
+        });
+    }
+};

+ 32 - 0
database/migrations/2025_07_21_123700_add_manual_to_calendars_table.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+        /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('calendars', function (Blueprint $table) {
+            $table->boolean('manual')->nullable();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('calendars', function (Blueprint $table) {
+            $table->dropColumn('manual');
+        });
+    }
+};

+ 33 - 0
database/migrations/2025_07_21_153800_add_motivation_id_to_calendars_table.php

@@ -0,0 +1,33 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+        /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('calendars', function (Blueprint $table) {
+            $table->unsignedBigInteger('motivation_manual_id')->nullable();
+            $table->foreign('motivation_manual_id')->nullable()->references('id')->on('motivations')->onUpdate('cascade')->onDelete('cascade');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('calendars', function (Blueprint $table) {
+            $table->dropColumn('motivation_manual_id');
+        });
+    }
+};

Fichier diff supprimé car celui-ci est trop grand
+ 8 - 0
public/assets/js/fullcalendar.js


Fichier diff supprimé car celui-ci est trop grand
+ 5 - 0
public/assets/js/fullcalendar_locales.js


+ 129 - 119
resources/views/layouts/app.blade.php

@@ -166,6 +166,8 @@
                 print "Corsi";
             if (Request::is('course_member'))
                 print "Iscritti corsi";
+            if (Request::is('calendar'))
+                print "Calendario";
             if (Request::is('course_list'))
                 print "Pagamento corsi";
             if (Request::is('reminders'))
@@ -214,151 +216,159 @@
     </div>
 
     <div class="row flex-nowrap position-relative" id="sidebar--wrapper">
-    <!-- sidebar menu -->
-        <div class="sidebar--ui px-0 filterWrapper filterWrapper_open" id="filter--section">
 
-            <a class="d-lg-none sidebar--opener" data-bs-toggle="offcanvas" href="#offcanvasExample" role="button" aria-controls="offcanvasExample">
-                    <i class="ico--ui hamburger--menu"></i>
-            </a>
+        @if(Auth::user()->level != env('LEVEL_INSTRUCTOR', 2))
+            <!-- sidebar menu -->
+            <div class="sidebar--ui px-0 filterWrapper filterWrapper_open" id="filter--section">
 
-            <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"/>
-                    </a>
-                @endif
+                <a class="d-lg-none sidebar--opener" data-bs-toggle="offcanvas" href="#offcanvasExample" role="button" aria-controls="offcanvasExample">
+                        <i class="ico--ui hamburger--menu"></i>
+                </a>
 
-                <div class="offcanvas-header">
-                    <img src="{{env('LOGO2', env('LOGO', ''))}}" class="fs-5 d-inline img-fluid" alt="logo madonnella"/>
-                        <a class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#offcanvasExample" aria-label="Chiudi">
+                <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"/>
                         </a>
-                </div>
+                    @endif
 
-                <div class="offcanvas-body w-100">
-                <div id="accordionExample" style="width:100%">
-                    <div class="accordion-item">
-                        <h2 class="accordion-header linkMenu" id="headingOne" style="margin-top:50px;">
-                            <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="{{Request::is('members') || Request::is('suppliers') ? 'true' : 'false'}}" aria-controls="collapseOne">
-                                Anagrafiche
-                            </button>
-                        </h2>
-                        <div id="collapseOne" class="accordion-collapse collapse {{Request::is('members') || Request::is('suppliers') ? 'show' : ''}}" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
-                            <div class="accordion-body">
-                                <ul class="nav nav-pills flex-column align-items-center align-items-sm-start w-100" id="menu-anagrafica" style="margin-top:0px;">
-                                    <li class="nav-item" style="{{Request::is('members') ? 'background-color: #c5d9e6;' : ''}}">
-                                        <a href="/members" class="nav-link d-flex align-items-center linkMenu">
-                                            <span class="ms-3 d-md-inline">Utenti</span>
-                                        </a>
-                                    </li>
-                                    @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
-                                        <li class="nav-item"  style="{{Request::is('suppliers') ? 'background-color: #c5d9e6;' : ''}}">
-                                            <a href="/suppliers" class="nav-link d-flex align-items-center linkMenu">
-                                                <span class="ms-3 d-md-inline">Fornitori</span>
+                    <div class="offcanvas-header">
+                        <img src="{{env('LOGO2', env('LOGO', ''))}}" class="fs-5 d-inline img-fluid" alt="logo madonnella"/>
+                            <a class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#offcanvasExample" aria-label="Chiudi">
+                            </a>
+                    </div>
+
+                    <div class="offcanvas-body w-100">
+                    <div id="accordionExample" style="width:100%">
+                        <div class="accordion-item">
+                            <h2 class="accordion-header linkMenu" id="headingOne" style="margin-top:50px;">
+                                <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="{{Request::is('members') || Request::is('suppliers') ? 'true' : 'false'}}" aria-controls="collapseOne">
+                                    Anagrafiche
+                                </button>
+                            </h2>
+                            <div id="collapseOne" class="accordion-collapse collapse {{Request::is('members') || Request::is('suppliers') ? 'show' : ''}}" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
+                                <div class="accordion-body">
+                                    <ul class="nav nav-pills flex-column align-items-center align-items-sm-start w-100" id="menu-anagrafica" style="margin-top:0px;">
+                                        <li class="nav-item" style="{{Request::is('members') ? 'background-color: #c5d9e6;' : ''}}">
+                                            <a href="/members" class="nav-link d-flex align-items-center linkMenu">
+                                                <span class="ms-3 d-md-inline">Utenti</span>
                                             </a>
                                         </li>
-                                    @endif
-                                </ul>
+                                        @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
+                                            <li class="nav-item"  style="{{Request::is('suppliers') ? 'background-color: #c5d9e6;' : ''}}">
+                                                <a href="/suppliers" class="nav-link d-flex align-items-center linkMenu">
+                                                    <span class="ms-3 d-md-inline">Fornitori</span>
+                                                </a>
+                                            </li>
+                                        @endif
+                                    </ul>
+                                </div>
                             </div>
                         </div>
-                    </div>
-                    <div class="accordion-item">
-                        <h2 class="accordion-header linkMenu" id="headingTwo">
-                            <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="{{Request::is('in') || Request::is('out') || Request::is('receipts') || Request::is('records_in_out') || Request::is('records') ? 'true' : 'false'}}" aria-controls="collapseTwo">
-                                Contabilità
-                            </button>
-                        </h2>
-                        <div id="collapseTwo" class="accordion-collapse collapse {{Request::is('in') || Request::is('out') || Request::is('receipts') || Request::is('records_in_out') || Request::is('records') ? 'show' : ''}}" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
-                            <div class="accordion-body">
-                                <ul class="nav nav-pills flex-column align-items-center align-items-sm-start w-100" id="menu-contabilita" style="margin-top:0px;">
-                                    <li class="nav-item" style="{{Request::is('in') ? 'background-color: #c5d9e6;' : ''}}">
-                                        <a href="/in" class="nav-link d-flex align-items-center linkMenu">
-                                            <span class="ms-3 d-md-inline">Entrate</span>
-                                        </a>
-                                    </li>
-                                    @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
-                                        <li class="nav-item" style="{{Request::is('out') ? 'background-color: #c5d9e6;' : ''}}">
-                                            <a href="/out" class="nav-link d-flex align-items-center linkMenu">
-                                                <span class="ms-3 d-md-inline">Uscite</span>
+                        <div class="accordion-item">
+                            <h2 class="accordion-header linkMenu" id="headingTwo">
+                                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="{{Request::is('in') || Request::is('out') || Request::is('receipts') || Request::is('records_in_out') || Request::is('records') ? 'true' : 'false'}}" aria-controls="collapseTwo">
+                                    Contabilità
+                                </button>
+                            </h2>
+                            <div id="collapseTwo" class="accordion-collapse collapse {{Request::is('in') || Request::is('out') || Request::is('receipts') || Request::is('records_in_out') || Request::is('records') ? 'show' : ''}}" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
+                                <div class="accordion-body">
+                                    <ul class="nav nav-pills flex-column align-items-center align-items-sm-start w-100" id="menu-contabilita" style="margin-top:0px;">
+                                        <li class="nav-item" style="{{Request::is('in') ? 'background-color: #c5d9e6;' : ''}}">
+                                            <a href="/in" class="nav-link d-flex align-items-center linkMenu">
+                                                <span class="ms-3 d-md-inline">Entrate</span>
                                             </a>
                                         </li>
-                                    @endif
-                                    <li class="nav-item" style="{{Request::is('receipts') ? 'background-color: #c5d9e6;' : ''}}">
-                                        <a href="/receipts" class="nav-link d-flex align-items-center linkMenu">
-                                            <span class="ms-3 d-md-inline">Ricevute</span>
-                                        </a>
-                                    </li>
-                                    @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
-                                        <li class="nav-item" style="{{Request::is('records_in_out') ? 'background-color: #c5d9e6;' : ''}}">
-                                            <a href="/records_in_out" class="nav-link d-flex align-items-center linkMenu">
-                                                <span class="ms-3 d-md-inline">Gestionale</span>
+                                        @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
+                                            <li class="nav-item" style="{{Request::is('out') ? 'background-color: #c5d9e6;' : ''}}">
+                                                <a href="/out" class="nav-link d-flex align-items-center linkMenu">
+                                                    <span class="ms-3 d-md-inline">Uscite</span>
+                                                </a>
+                                            </li>
+                                        @endif
+                                        <li class="nav-item" style="{{Request::is('receipts') ? 'background-color: #c5d9e6;' : ''}}">
+                                            <a href="/receipts" class="nav-link d-flex align-items-center linkMenu">
+                                                <span class="ms-3 d-md-inline">Ricevute</span>
                                             </a>
                                         </li>
-                                    @endif
-                                    <li class="nav-item" style="{{Request::is('records') ? 'background-color: #c5d9e6;' : ''}}">
-                                        <a href="/records" class="nav-link d-flex align-items-center linkMenu">
-                                            <span class="ms-3 d-md-inline">Prima Nota</span>
-                                        </a>
-                                    </li>
-                                </ul>
-                            </div>
-                        </div>
-                    </div>
-                    <div class="accordion-item">
-                        <h2 class="accordion-header linkMenu" id="headingThree">
-                            <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="{{Request::is('course_list') || Request::is('course_member') ? 'true' : 'false'}}" aria-controls="collapseThree">
-                                Corsi
-                            </button>
-                        </h2>
-                        <div id="collapseThree" class="accordion-collapse collapse {{Request::is('course_list') || Request::is('course_member') ? 'show' : ''}}" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
-                            <div class="accordion-body">
-                                <ul class="nav nav-pills flex-column align-items-center align-items-sm-start w-100" id="menu-contabilita" style="margin-top:0px;">
-                                    <li class="nav-item" style="{{Request::is('course_member') ? 'background-color: #c5d9e6;' : ''}}">
-                                        <a href="/course_member" class="nav-link d-flex align-items-center linkMenu">
-                                            <span class="ms-3 d-md-inline">Iscritti</span>
-                                        </a>
-                                    </li>
-                                    <li class="nav-item" style="{{Request::is('course_list') ? 'background-color: #c5d9e6;' : ''}}">
-                                        <a href="/course_list" class="nav-link d-flex align-items-center linkMenu">
-                                            <span class="ms-3 d-md-inline">Pagamenti</span>
-                                        </a>
-                                    </li>
-                                </ul>
+                                        @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
+                                            <li class="nav-item" style="{{Request::is('records_in_out') ? 'background-color: #c5d9e6;' : ''}}">
+                                                <a href="/records_in_out" class="nav-link d-flex align-items-center linkMenu">
+                                                    <span class="ms-3 d-md-inline">Gestionale</span>
+                                                </a>
+                                            </li>
+                                        @endif
+                                        <li class="nav-item" style="{{Request::is('records') ? 'background-color: #c5d9e6;' : ''}}">
+                                            <a href="/records" class="nav-link d-flex align-items-center linkMenu">
+                                                <span class="ms-3 d-md-inline">Prima Nota</span>
+                                            </a>
+                                        </li>
+                                    </ul>
+                                </div>
                             </div>
                         </div>
-                    </div>
-                    @if(false)
-                        <div class="accordion-item" style="{{Request::is('reminders') ? 'background-color: #c5d9e6;' : ''}}">
-                            <h2 class="accordion-header linkMenu">
-                                <a class="accordion-button collapsed" href="/reminders">
-                                    Scadenze
-                                </a>
-                            </h2>
-                        </div>
-                    @endif
-                    @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
-                        <div class="accordion-item " style="{{Request::is('settings') || 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="/settings">
-                                    Impostazioni
-                                </a>
+                        <div class="accordion-item">
+                            <h2 class="accordion-header linkMenu" id="headingThree">
+                                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="{{Request::is('course_list') || Request::is('course_member') ? 'true' : 'false'}}" aria-controls="collapseThree">
+                                    Corsi
+                                </button>
                             </h2>
+                            <div id="collapseThree" class="accordion-collapse collapse {{Request::is('course_list') || Request::is('course_member') ? 'show' : ''}}" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
+                                <div class="accordion-body">
+                                    <ul class="nav nav-pills flex-column align-items-center align-items-sm-start w-100" id="menu-contabilita" style="margin-top:0px;">
+                                        <li class="nav-item" style="{{Request::is('course_member') ? 'background-color: #c5d9e6;' : ''}}">
+                                            <a href="/course_member" class="nav-link d-flex align-items-center linkMenu">
+                                                <span class="ms-3 d-md-inline">Iscritti</span>
+                                            </a>
+                                        </li>
+                                        <li class="nav-item" style="{{Request::is('calendar') ? 'background-color: #c5d9e6;' : ''}}">
+                                            <a href="/calendar" class="nav-link d-flex align-items-center linkMenu">
+                                                <span class="ms-3 d-md-inline">Calendario</span>
+                                            </a>
+                                        </li>
+                                        <li class="nav-item" style="{{Request::is('course_list') ? 'background-color: #c5d9e6;' : ''}}">
+                                            <a href="/course_list" class="nav-link d-flex align-items-center linkMenu">
+                                                <span class="ms-3 d-md-inline">Pagamenti</span>
+                                            </a>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </div>
                         </div>
                         @if(false)
-                            <div class="accordion-item " style="{{Request::is('users') ? 'background-color: #c5d9e6;' : ''}}">
+                            <div class="accordion-item" style="{{Request::is('reminders') ? 'background-color: #c5d9e6;' : ''}}">
                                 <h2 class="accordion-header linkMenu">
-                                    <a class="accordion-button collapsed" href="/users">
-                                        Utenti
+                                    <a class="accordion-button collapsed" href="/reminders">
+                                        Scadenze
                                     </a>
                                 </h2>
                             </div>
                         @endif
-                    @endif
-                </div>
+                        @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
+                            <div class="accordion-item " style="{{Request::is('settings') || 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="/settings">
+                                        Impostazioni
+                                    </a>
+                                </h2>
+                            </div>
+                            @if(false)
+                                <div class="accordion-item " style="{{Request::is('users') ? 'background-color: #c5d9e6;' : ''}}">
+                                    <h2 class="accordion-header linkMenu">
+                                        <a class="accordion-button collapsed" href="/users">
+                                            Utenti
+                                        </a>
+                                    </h2>
+                                </div>
+                            @endif
+                        @endif
+                    </div>
+                    </div>
                 </div>
             </div>
-        </div>
 
-        <button id="open-filter" onclick="pcsh1()"></button>
+            <button id="open-filter" onclick="pcsh1()"></button>
+        @endif
 
 
         <div class="col">

+ 302 - 0
resources/views/livewire/calendar.blade.php

@@ -0,0 +1,302 @@
+<div class="col card--ui" id="card--dashboard">
+
+
+    <section id="resume-table">
+        
+        <div class="compare--chart_wrapper d-none"></div>
+
+        <div class="row">
+            <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" id="name_filter" onchange="reloadCalendar()">
+                        <option value="">
+                        @foreach($names as $n)
+                            <option value="{{$n}}" {{isset($_GET["name_filter"]) && $_GET["name_filter"] == $n ? 'selected' : ''}}>{{$n}}
+                        @endforeach
+                    </select>
+                </div>
+            </div>
+            <div class="col-6 text-end">
+                <a style="cursor:pointer" href="#" data-bs-toggle="modal" data-bs-target="#calendarNewModal" class="openNewModal addData btn--ui"><i class="fa-solid fa-plus"></i></a>
+            </div>
+        </div>
+
+        <div id='calendar'></div>
+
+    </section>
+
+    <a href="#" data-bs-toggle="modal" data-bs-target="#calendarModal" class="openModal"></a>
+
+    <div  wire:ignore.self class="modal" id="calendarModal" tabindex="-1" aria-labelledby="calendarModalLabel" aria-hidden="true">
+        <div class="modal-dialog">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title" id="calendarModalLabel">Dettaglio</h5>
+                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+                </div>
+                <div class="modal-body">
+                    <div class="row">
+                        <div class="col-md-6">
+                            <label for="course_subscription_id" class="form-label">Ora inizio</label>
+                            <h3 class="time">ORA</h3>
+                        </div>
+                        <div class="col-md-6">
+                            <label class="form-label date">Martdì aaa</label>
+                            <h3 class="title">Padel</h3>
+                        </div>
+                    </div>                    
+                    <div class="row mt-2 showDelete" style="display:none">
+                        <div class="col-md-12">
+                            <label for="newMotivation" class="form-label">Motivazione</label>
+                            <select class="form-select form-select-lg me-1 " id="motivation_id">
+                                <option value="">
+                                @foreach($motivations as $m)
+                                    <option value="{{$m["id"]}}">{{$m["name"]}}</option>
+                                @endforeach
+                            </select>
+                            <br>
+                            <button type="button" class="btn--ui primary" onclick="deleteCalendar()" style="background-color:red !important">Annulla lezione</button>
+                        </div>
+                        
+                    </div>                
+                </div>
+                <div class="modal-footer mt-2">
+                    <button class="btn--ui lightGrey" onclick="showDelete()">Annulla Lezione</a>
+                    <button type="button" class="btn--ui btn-primary" onclick="goPresence()">Presenze</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <!--
+    Giorno e data della lezione
+    Corso, livello, tipologia, frequenza, insegnante, campo, note
+    -->
+
+    <div  wire:ignore.self class="modal" id="calendarNewModal" tabindex="-1" aria-labelledby="calendarNewModalLabel" aria-hidden="true">
+        <div class="modal-dialog">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title" id="calendarNewModalLabel">Dettaglio</h5>
+                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+                </div>
+                <div class="modal-body">
+                    <div class="row">
+                        <div class="col-md-6">
+                            <label for="date" class="form-label">Data</label>
+                            <input class="form-control" type="date" id="date" placeholder="Data">
+                        </div>
+                        <div class="col-md-6">
+                            <label for="date" class="form-label">Nome</label>
+                            <input class="form-control js-keyupTitle" type="name" id="name" placeholder="Nome">
+                        </div>
+                    </div>                    
+                    <div class="row">
+                        <div class="col-md-6">
+                            <label for="course_subscription_id" class="form-label">Ora inizio</label>
+                            <select class="form-select" id="from">
+                                <option value="">--Seleziona--
+                                @for($c=6;$c<=23;$c++)
+                                    <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00
+                                    <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30
+                                @endfor
+                            </select>
+                        </div>
+                        <div class="col-md-6">
+                            <label for="course_subscription_id" class="form-label">Ora fine</label>
+                            <select class="form-select" id="to">
+                                <option value="">--Seleziona--
+                                @for($c=6;$c<=23;$c++)
+                                    <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00
+                                    <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30
+                                @endfor
+                            </select>
+                        </div>
+                    </div>                    
+                    <div class="row">
+                        <div class="col-md-6">
+                            <label for="course_type_id" class="form-label">Corso</label>
+                            <select class="form-select form-select-lg me-1" id="course_type_id">
+                                <option value="">
+                                @foreach($course_types as $c)
+                                    <option value="{{$c["id"]}}">{{$c["name"]}}</option>
+                                @endforeach
+                            </select>
+                        </div>
+                        <div class="col-md-6">
+                            <label for="course_duration_id" class="form-label">Darata</label>
+                            <select class="form-select form-select-lg me-1" id="course_duration_id">
+                                <option value="">
+                                @foreach($course_durations as $c)
+                                    <option value="{{$c["id"]}}">{{$c["name"]}}</option>
+                                @endforeach
+                            </select>
+                            
+                        </div>
+                    </div>    
+                    <div class="row">
+                        <div class="col-md-6">
+                            <label for="course_frequency_id" class="form-label">Frequenza</label>
+                            <select class="form-select form-select-lg me-1" id="course_frequency_id">
+                                <option value="">
+                                @foreach($course_frequencies as $c)
+                                    <option value="{{$c["id"]}}">{{$c["name"]}}</option>
+                                @endforeach
+                            </select>
+                        </div>
+                        <div class="col-md-6">
+                            <label for="course_level_id" class="form-label">Livello</label>
+                            <select class="form-select form-select-lg me-1" id="course_level_id">
+                                <option value="">
+                                @foreach($course_levels as $c)
+                                    <option value="{{$c["id"]}}">{{$c["name"]}}</option>
+                                @endforeach
+                            </select>
+                            
+                        </div>
+                    </div>  
+                    <div class="row">
+                        <div class="col-md-6">
+                            <label for="course_frequency_id" class="form-label">Campo</label>
+                            <select class="form-select form-select-lg me-1" id="court_id">
+                                <option value="">
+                                @foreach($courts as $c)
+                                    <option value="{{$c["id"]}}">{{$c["name"]}}</option>
+                                @endforeach
+                            </select>
+                        </div>
+                        <div class="col-md-6">
+                            <label for="course_level_id" class="form-label">Istruttore</label>
+                            <select class="form-select form-select-lg me-1" id="instructor_id">
+                                <option value="">
+                                @foreach($instructors as $c)
+                                    <option value="{{$c["id"]}}">{{$c["name"]}}</option>
+                                @endforeach
+                            </select>
+                            
+                        </div>
+                    </div>  
+                    <div class="row">
+                        <div class="col-md-12">
+                            <label for="note" class="form-label">Note</label>
+                            <input class="form-control" type="name" id="note" placeholder="Note">                            
+                        </div>
+                    </div>                    
+                </div>
+                <div class="modal-footer mt-2">
+                    <button class="btn--ui lightGrey" >Annulla</a>
+                    <button type="button" class="btn--ui btn-primary" onclick="createCalendar()">Salva</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+</div>
+
+@push('scripts')
+    
+    <script src="/assets/js/fullcalendar.js"></script>
+    <script src="/assets/js/fullcalendar_locales.js"></script>
+@endpush
+
+@push('scripts')
+    <script>
+
+        var currentCalendar = 0;
+        var params = '';
+
+        function goPresence()
+        {
+            document.location.href = '/presences' + params;
+        }
+
+        function createCalendar()
+        {                        
+            
+            console.log($("#course_type_id").val());
+            @this.set('course_type_id',$("#course_type_id").val());
+            console.log($("#course_duration_id").val());
+            @this.set('course_duration_id', $("#course_duration_id").val());
+            console.log($("#course_frequency_id").val());
+            @this.set('course_frequency_id', $("#course_frequency_id").val());
+            console.log($("#course_level_id").val());
+            @this.set('course_level_id', $("#course_level_id").val());
+            console.log($("#date").val() + " " + $("#from").val() + ":00");
+            @this.set('from', $("#date").val() + " " + $("#from").val() + ":00");
+            console.log($("#date").val() + " " + $("#to").val() + ":00");
+            @this.set('to', $("#date").val() + " " + $("#to").val() + ":00");
+            console.log($("#name").val());
+            @this.set('name', $("#name").val());
+            @this.set('note', $("#note").val());
+            @this.set('court_id', $("#court_id").val());
+            @this.set('instructor_id', $("#instructor_id").val());
+            @this.createCalendar();
+        }
+
+        document.addEventListener('DOMContentLoaded', function() {
+            var calendarEl = document.getElementById('calendar');
+            var calendar = new FullCalendar.Calendar(calendarEl, {
+                initialView: 'timeGridWeek',
+                headerToolbar: {
+                    left: 'prevYear,prev,next,nextYear today',
+                    center: 'title',
+                            right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
+
+                },
+                dateClick: function(info) {
+                    var x = info.dateStr.split("T");
+                    $("#date").val(x[0]);
+                    var y = x[1].split("+");
+                    var z = y[0].split(":");
+                    var from = z[0] + ":" + z[1];
+                    console.log(from);
+                    $("#from").val(from);
+                    $('.openNewModal').trigger('click');
+                },
+                eventClick: function(info) {
+                    var eventDate = new Date(info.event.start); 
+                    var datestring = eventDate.getFullYear() + "-" + pad(eventDate.getMonth()+1, 2) + "-" + pad(eventDate.getDate(), 2) + " " + pad(eventDate.getHours(), 2) + ":" + pad(eventDate.getMinutes(), 2) + ":00";
+                    var title = info.event.title;
+                    $(".title").html(title);
+                    $(".time").html(pad(eventDate.getHours(), 2) + ":" + pad(eventDate.getMinutes(), 2));
+                    $(".date").html(eventDate.toLocaleDateString('it-IT', { weekday: 'long' }) + " " + pad(eventDate.getDate(), 2) + " " + eventDate.toLocaleDateString('it-IT', { month: 'long' }));
+                    currentCalendar = info.event.id;
+                    params = '?calendarId=' + info.event.id;// + "&date=" + datestring; 
+                    $('.openModal').trigger('click');
+                },
+                locale: 'it',
+                events: @json($records),
+            });            
+            calendar.render();
+        });
+
+        $(document).ready(function() {
+
+            
+            
+        } );
+
+        function showDelete() {
+            jQuery(".showDelete").show()
+        }
+
+        function pad(num, size) {
+            num = num.toString();
+            while (num.length < size) num = "0" + num;
+            return num;
+        }
+
+        function deleteCalendar()
+        {
+            var motivation = jQuery("#motivation_id").val();
+            @this.cancelCalendar(currentCalendar, motivation);            
+        }
+
+        function reloadCalendar()
+        {
+            document.location.href = '/calendar?name_filter=' + $("#name_filter").val();
+        }
+        
+    </script>
+@endpush

+ 243 - 0
resources/views/livewire/court.blade.php

@@ -0,0 +1,243 @@
+<div class="col card--ui" id="card--dashboard">
+    @if(!$add && !$update)
+
+        <!--<button id="open-filter" onclick="pcsh1()"></button>
+        <button id="close-filter" onclick="pcsh2()"></button>-->
+
+        <a class="btn--ui lightGrey" href="/settings?type=corsi"><i class="fa-solid fa-arrow-left"></i></a><br>
+
+        <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">Campi</h2>
+            </div>
+
+            <div class="title--section_addButton"  wire:click="add()" style="cursor: pointer;">
+                <div class="btn--ui entrata d-flex justify-items-between">
+                    <a href="#" wire:click="add()" style="color:white">Aggiungi</a>
+                </div>
+            </div>
+
+        </header>
+        <!--
+        <section id="subheader" class="d-flex align-items-center justify-content-between">
+            <form action="" class="group--action d-flex align-items-center">
+            <select class="form-select form-select-lg me-1" aria-label=".form-select-lg example">
+                <option selected>Open this select menu</option>
+                <option value="1">One</option>
+                <option value="2">Two</option>
+                <option value="3">Three</option>
+                </select>
+                <button type="submit" class="btn--ui">applica</button>
+            </form>
+
+            <form action="" class="search--form d-flex align-items-center">
+                <div class="input-group mb-3">
+                    <input type="text" class="form-control" placeholder="Cerca utente" aria-label="cerca utent" aria-describedby="button-addon2">
+                    <button class="btn--ui" type="button" id="button-addon2"><i class="ico--ui search"></i>Cerca</button>
+                </div>
+            </form>
+        </section>
+        -->
+        <section id="resume-table">
+            <div class="compare--chart_wrapper d-none"></div>
+
+            <table class="table tablesaw tableHead tablesaw-stack" id="tablesaw-350" width="100%">
+                <thead>
+                    <tr>
+                        <th scope="col">Nome</th>
+                        <th scope="col">Abilitato</th>
+                        <th scope="col">...</th>
+                    </tr>
+                </thead>
+                <tbody id="checkall-target">
+                    @foreach($records as $record)
+                        <tr>
+                            <td>{{$record->name}}</td>
+                            <td> <span class="tablesaw-cell-content"><span class="badge tessera-badge {{$record->enabled ? 'active' : 'suspended'}}">{{$record->enabled ? 'attivo' : 'disattivo'}}</span></span></td>
+                            <td>
+                                <button type="button" class="btn" wire:click="edit({{ $record->id }})" data-bs-toggle="popover"  data-bs-trigger="hover focus" data-bs-placement="bottom" data-bs-content="Modifica"><i class="fa-regular fa-pen-to-square"></i></button>
+                                <button type="button" class="btn" onclick="confirm('Sei sicuro?') || event.stopImmediatePropagation()" wire:click="delete({{ $record->id }})" data-bs-toggle="popover"  data-bs-trigger="hover focus" data-bs-placement="bottom" data-bs-content="cestina"><i class="fa-regular fa-trash-can"></i></button>
+                            </td>
+                        </tr>
+                    @endforeach
+
+                </tbody>
+            </table>
+            <!--
+            <div class="paginator d-flex justify-content-center">
+                <nav aria-label="Page navigation example">
+                    <ul class="pagination">
+                        <li class="page-item">
+                        <a class="page-link" href="#" aria-label="Previous">
+                            <span aria-hidden="true"></span>
+                        </a>
+                        </li>
+                        <li class="page-item"><a class="page-link" href="#">1</a></li>
+                        <li class="page-item"><a class="page-link" href="#">2</a></li>
+                        <li class="page-item"><a class="page-link" href="#">3</a></li>
+                        <li class="page-item"><a class="page-link" href="#">3</a></li>
+
+                        <li class="page-item"><span class="more-page">...</span></li>
+
+                        <li class="page-item">
+                        <a class="page-link" href="#" aria-label="Next">
+                            <span aria-hidden="true"></span>
+                        </a>
+                        </li>
+                    </ul>
+                    </nav>
+            </div>
+            -->
+        </section>
+
+    @else
+
+        <div class="container">
+
+            <a class="btn--ui lightGrey" href="/banks"><i class="fa-solid fa-arrow-left"></i></a><br><br>
+
+            @if (session()->has('error'))
+                <div class="alert alert-danger" role="alert">
+                    {{ session()->get('error') }}
+                </div>
+            @endif
+
+            <div class="row">
+                <div class="col">
+
+                    <form action="">
+
+                        <div class="row mb-3">
+                            <div class="col">
+                                <div class="form--item">
+                                    <label for="inputName" class="form-label">Nome</label>
+                                    <input class="form-control js-keyupTitle @error('name') is-invalid @enderror" type="text" id="name" placeholder="Nome" wire:model="name">
+                                    @error('name')
+                                        <div class="invalid-feedback">{{ $message }}</div>
+                                    @enderror
+                                </div>
+                            </div>
+                            <div class="col">
+                                <div class="form--item">
+                                    <label for="enabled" class="form-label">Abilitato</label>
+                                    <input class="form-check-input form-control" style="width:22px; height:22px;" type="checkbox" id="enabled" wire:model="enabled">
+                                </div>
+                            </div>
+                        </div>
+
+                        <!-- // inline input field -->
+
+                        <div class="form--item">
+                            <button type="button" class="btn--ui lightGrey" wire:click="cancel()">Annulla</button>
+                        @if($add)
+                            <button type="submit" class="btn--ui" wire:click.prevent="store()">Salva</button>
+                        @endif
+                        @if($update)
+                            <button type="submit" class="btn--ui" wire:click.prevent="update()">Salva</button>
+                        @endif
+                        </div>
+
+                    </form>
+                </div>
+            </div>
+        </div>
+
+    @endif
+</div>
+
+@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="/assets/js/datatables.js"></script>
+    <script src="https://cdn.datatables.net/buttons/3.0.2/js/buttons.dataTables.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/pdfmake.min.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/vfs_fonts.js"></script>
+@endpush
+
+@push('scripts')
+    <script>
+
+        $(document).ready(function() {
+            loadDataTable();
+        } );
+
+        Livewire.on('load-data-table', () => {
+            loadDataTable();
+        });
+
+        function loadDataTable(){
+            if ( $.fn.DataTable.isDataTable('#tablesaw-350') ) {
+                $('#tablesaw-350').DataTable().destroy();
+            }
+            $('#tablesaw-350').DataTable({
+                thead: {
+                'th': {'background-color': 'blue'}
+                },
+                layout: {
+                    topStart : null,
+                    topEnd : null,
+                    top1A: {
+                        buttons: [
+                            {
+                                extend: 'collection',
+                                text: 'ESPORTA',
+                                buttons: [
+                                    {
+                                    extend: 'excelHtml5',
+                                        title: 'Campi',
+                                        exportOptions: {
+                                            columns: ":not(':last')"
+                                        }
+                                    },
+                                    {
+                                        extend: 'pdfHtml5',
+                                        title: 'Campi',
+                                        exportOptions: {
+                                            columns: ":not(':last')"
+                                        }
+                                    },
+                                    {
+                                        extend: 'print',
+                                        text: 'Stampa',
+                                        title: 'Campi',
+                                        exportOptions: {
+                                            columns: ":not(':last')"
+                                        }
+                                    }
+                                ],
+                                dropup: true
+                            }
+                        ]
+                    },
+                    top1B : {
+                        pageLength: {
+                            menu: [[10, 25, 50, 100, 100000], [10, 25, 50, 100, "Tutti"]]
+                        }
+                    },
+                    top1C :'search',
+                },
+                pagingType: 'numbers',
+                "language": {
+                    "url": "/assets/js/Italian.json"
+                },
+                "fnInitComplete": function (oSettings, json) {
+                    var html = '&nbsp;<a href="#" class="addData btn--ui"><i class="fa-solid fa-plus"></i></a>';
+                    $(".dt-search").append(html);
+                }
+            });
+            $('#tablesaw-350 thead tr th').addClass('col');
+            $('#tablesaw-350 thead tr th').css("background-color", "#f6f8fa");
+
+            $(document).ready(function() {
+                $(document).on("click",".addData",function() {
+                    $(".title--section_addButton").trigger("click")
+                });
+            } );
+
+        }
+
+    </script>
+@endpush
+

+ 212 - 0
resources/views/livewire/motivation.blade.php

@@ -0,0 +1,212 @@
+<div class="col card--ui" id="card--dashboard">
+    @if(!$add && !$update)
+
+        <!--<button id="open-filter" onclick="pcsh1()"></button>
+        <button id="close-filter" onclick="pcsh2()"></button>-->
+
+        <a class="btn--ui lightGrey" href="/settings?type=corsi"><i class="fa-solid fa-arrow-left"></i></a><br>
+
+        <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">Motivazioni annullamento</h2>
+            </div>
+
+            <div class="title--section_addButton"  wire:click="add()" style="cursor: pointer;">
+                <div class="btn--ui entrata d-flex justify-items-between">
+                    <a href="#" wire:click="add()" style="color:white">Aggiungi</a>
+                </div>
+            </div>
+
+        </header>
+        
+        <section id="resume-table">
+            <div class="compare--chart_wrapper d-none"></div>
+
+            <table class="table tablesaw tableHead tablesaw-stack" id="tablesaw-350" width="100%">
+                <thead>
+                    <tr>
+                        <th scope="col">Nome</th>
+                        <th scope="col">Tipologia</th>
+                        <th scope="col">Abilitato</th>
+                        <th scope="col">...</th>
+                    </tr>
+                </thead>
+                <tbody id="checkall-target">
+                    @foreach($records as $record)
+                        <tr>
+                            <td>{{$record->name}}</td>
+                            <td>{{$record->type == 'add' ? 'Inserimento' : 'Eliminazione'}}</td>
+                            <td> <span class="tablesaw-cell-content"><span class="badge tessera-badge {{$record->enabled ? 'active' : 'suspended'}}">{{$record->enabled ? 'attivo' : 'disattivo'}}</span></span></td>
+                            <td>
+                                <button type="button" class="btn" wire:click="edit({{ $record->id }})" data-bs-toggle="popover"  data-bs-trigger="hover focus" data-bs-placement="bottom" data-bs-content="Modifica"><i class="fa-regular fa-pen-to-square"></i></button>
+                                <button type="button" class="btn" onclick="confirm('Sei sicuro?') || event.stopImmediatePropagation()" wire:click="delete({{ $record->id }})" data-bs-toggle="popover"  data-bs-trigger="hover focus" data-bs-placement="bottom" data-bs-content="cestina"><i class="fa-regular fa-trash-can"></i></button>
+                            </td>
+                        </tr>
+                    @endforeach
+
+                </tbody>
+            </table>
+          
+        </section>
+
+    @else
+
+        <div class="container">
+
+            <a class="btn--ui lightGrey" href="/banks"><i class="fa-solid fa-arrow-left"></i></a><br><br>
+
+            @if (session()->has('error'))
+                <div class="alert alert-danger" role="alert">
+                    {{ session()->get('error') }}
+                </div>
+            @endif
+
+            <div class="row">
+                <div class="col">
+
+                    <form action="">
+
+                        <div class="row mb-3">
+                            <div class="col">
+                                <div class="form--item">
+                                    <label for="inputName" class="form-label">Nome</label>
+                                    <input class="form-control js-keyupTitle @error('name') is-invalid @enderror" type="text" id="name" placeholder="Nome" wire:model="name">
+                                    @error('name')
+                                        <div class="invalid-feedback">{{ $message }}</div>
+                                    @enderror
+                                </div>
+                            </div>
+                            <div class="col">
+                                <div class="form--item">
+                                    <label for="type" class="form-label">Tipologia</label>
+                                    <select class="form-control" wire:model="type">
+                                        <option value=""></option>
+                                        <option value="add">Inserimento</option>
+                                        <option value="del">Eliminazione</option>
+                                    </select>
+                                </div>
+                            </div>
+                            <div class="col">
+                                <div class="form--item">
+                                    <label for="enabled" class="form-label">Abilitato</label>
+                                    <input class="form-check-input form-control" style="width:22px; height:22px;" type="checkbox" id="enabled" wire:model="enabled">
+                                </div>
+                            </div>
+                        </div>
+
+                        <!-- // inline input field -->
+
+                        <div class="form--item">
+                            <button type="button" class="btn--ui lightGrey" wire:click="cancel()">Annulla</button>
+                        @if($add)
+                            <button type="submit" class="btn--ui" wire:click.prevent="store()">Salva</button>
+                        @endif
+                        @if($update)
+                            <button type="submit" class="btn--ui" wire:click.prevent="update()">Salva</button>
+                        @endif
+                        </div>
+
+                    </form>
+                </div>
+            </div>
+        </div>
+
+    @endif
+</div>
+
+@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="/assets/js/datatables.js"></script>
+    <script src="https://cdn.datatables.net/buttons/3.0.2/js/buttons.dataTables.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/pdfmake.min.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/vfs_fonts.js"></script>
+@endpush
+
+@push('scripts')
+    <script>
+
+        $(document).ready(function() {
+            loadDataTable();
+        } );
+
+        Livewire.on('load-data-table', () => {
+            loadDataTable();
+        });
+
+        function loadDataTable(){
+            if ( $.fn.DataTable.isDataTable('#tablesaw-350') ) {
+                $('#tablesaw-350').DataTable().destroy();
+            }
+            $('#tablesaw-350').DataTable({
+                thead: {
+                'th': {'background-color': 'blue'}
+                },
+                layout: {
+                    topStart : null,
+                    topEnd : null,
+                    top1A: {
+                        buttons: [
+                            {
+                                extend: 'collection',
+                                text: 'ESPORTA',
+                                buttons: [
+                                    {
+                                    extend: 'excelHtml5',
+                                        title: 'Motivazioni annullamento',
+                                        exportOptions: {
+                                            columns: ":not(':last')"
+                                        }
+                                    },
+                                    {
+                                        extend: 'pdfHtml5',
+                                        title: 'Motivazioni annullamento',
+                                        exportOptions: {
+                                            columns: ":not(':last')"
+                                        }
+                                    },
+                                    {
+                                        extend: 'print',
+                                        text: 'Stampa',
+                                        title: 'Motivazioni annullamento',
+                                        exportOptions: {
+                                            columns: ":not(':last')"
+                                        }
+                                    }
+                                ],
+                                dropup: true
+                            }
+                        ]
+                    },
+                    top1B : {
+                        pageLength: {
+                            menu: [[10, 25, 50, 100, 100000], [10, 25, 50, 100, "Tutti"]]
+                        }
+                    },
+                    top1C :'search',
+                },
+                pagingType: 'numbers',
+                "language": {
+                    "url": "/assets/js/Italian.json"
+                },
+                "fnInitComplete": function (oSettings, json) {
+                    var html = '&nbsp;<a href="#" class="addData btn--ui"><i class="fa-solid fa-plus"></i></a>';
+                    $(".dt-search").append(html);
+                }
+            });
+            $('#tablesaw-350 thead tr th').addClass('col');
+            $('#tablesaw-350 thead tr th').css("background-color", "#f6f8fa");
+
+            $(document).ready(function() {
+                $(document).on("click",".addData",function() {
+                    $(".title--section_addButton").trigger("click")
+                });
+            } );
+
+        }
+
+    </script>
+@endpush
+

+ 402 - 0
resources/views/livewire/presence.blade.php

@@ -0,0 +1,402 @@
+<div class="col card--ui" id="card--dashboard">
+
+    <a class="btn--ui lightGrey" href="/calendar"><i class="fa-solid fa-arrow-left"></i></a><br><br>
+
+    
+        
+        <div class="compare--chart_wrapper d-none"></div>
+
+        <div class="row">
+            <div class="col-md-6">
+                <h3>{{$calendar->course ? $calendar->course->name : $calendar->name}}</h3>
+            </div>
+            <div class="col-md-6">
+                <h3>{{date("l d F", strtotime($calendar->from))}}, ora inizio {{date("H:i", strtotime($calendar->from))}}</h3>
+            </div>
+
+            @if($manual)
+
+                <div class="col-md-6">
+                    <label for="court_id" class="form-label">Motivazione</label>
+                    <select class="form-select form-select-lg me-1 " wire:model="motivation_manual_id">
+                        <option value="">
+                        @foreach($motivations_add as $m)
+                            <option value="{{$m->id}}">{{$m->name}}</option>
+                        @endforeach
+                    </select>
+                </div>
+
+            @else
+                <div class="col-md-6">
+                    <label for="court_id" class="form-label">Campo</label>
+                    <select class="form-select form-select-lg me-1 " wire:model="court_id">
+                        <option value="">
+                        @foreach($courts as $c)
+                            <option value="{{$c["id"]}}">{{$c["name"]}}</option>
+                        @endforeach
+                    </select>
+                </div>
+                <div class="col-md-5">
+                    <label for="instructor_id" class="form-label">Istruttore</label>
+                    <select class="form-select form-select-lg me-1 " wire:model="instructor_id">
+                        <option value="">
+                        @foreach($instructors as $i)
+                            <option value="{{$i["id"]}}">{{$i["name"]}}</option>
+                        @endforeach
+                    </select>
+                </div>
+                <div class="col-md-1">
+                    <br>
+                    <button type="button" class="btn--ui primary" data-bs-toggle="modal" data-bs-target="#instructorModal" style="width:50px">&nbsp;<i class="fa-solid fa-plus"></i></button>
+                </div>
+
+                <div class="col-md-12 mt-3">
+                    <input class="form-control" id="note" placeholder="Note" wire:model="note"></input>
+                </div>
+            @endif
+
+        </div>    
+        
+        <section id="resume-table" class="mt-3"  style="margin-bottom:20px">
+            <div class="compare--chart_wrapper d-none"></div>
+
+            <table class="table tablesaw tableHead tablesaw-stack" id="tablesaw-350" width="100%">
+                <thead>
+                    <tr>
+                        <th scope="col">#</th>
+                        <th scope="col">Cognome</th>
+                        <th scope="col">Nome</th>
+                        <th scope="col">Certificato</th>
+                        <th scope="col">Presenza</th>
+                    </tr>
+                </thead>
+                <tbody id="checkall-target">
+                    @foreach($records as $idx => $record)
+                        <tr>
+                            <td>{{$idx}}</td>
+                            <td>{{$record["last_name"]}}</td>
+                            <td>{{$record["first_name"]}}</td>                            
+                            <td>
+                                <span class="tablesaw-cell-content d-flex align-items-center">
+                                    @php
+                                    list($status, $date) = explode("|", $record["certificate"]);
+                                    @endphp
+                                    @if($status == 0)
+                                        <i class="ico--ui check suspended me-2"></i>Scaduto
+                                    @endif
+                                    @if($status == 1)
+                                        <i class="ico--ui check due me-2"></i>In scadenza
+                                    @endif
+                                    @if($status == 2)
+                                        <i class="ico--ui check active me-2"></i> Scadenza
+                                    @endif
+                                    {{$date}}
+                                </span>
+                            <td>
+                                @if ($record["status"] != 99)
+                                    @if ($record["presence"])
+                                        @if ($record["my_presence"])
+                                            <input class="member" type="checkbox" value="{{$record["id"]}}" {{$record["presence"] ? 'checked' : ''}}>
+                                        @else
+                                            V
+                                        @endif
+                                    @else
+                                        <input class="member" type="checkbox" value="{{$record["id"]}}" {{$record["presence"] ? 'checked' : ''}}>
+                                    @endif
+                                @else
+                                    Annullata
+                                @endif
+                            </td>
+                        </tr>
+                    @endforeach
+
+                </tbody>
+            </table>
+
+        </section>
+
+        <div class="row">
+            @if($calendar->status == 0)
+                <div class="col-md-6">      
+                    @if(!$manual)
+                        <div class="col-md-12 showDelete" style="display:none">
+                            <label for="newMotivation" class="form-label">Motivazione</label>
+                            <select class="form-select form-select-lg me-1 " id="motivation_id">
+                                <option value="">
+                                @foreach($motivations as $m)
+                                    <option value="{{$m["id"]}}">{{$m["name"]}}</option>
+                                @endforeach
+                            </select>
+                        </div>
+                        
+                    @endif              
+                    <button type="button" class="btn--ui primary btSave" data-bs-toggle="modal" data-bs-target="#userModal" >Aggiungi utente</button>
+                </div>
+                <div class="col-md-6 text-end">
+                    <div class="showDelete" style="display:none;"><br><button type="button" class="btn--ui " style="background-color:red !important" onclick="cancel()">Annulla lezione selezionati</button></div>
+                    <button type="button" class="btn--ui btSave" onclick="save()">Salva</button>             
+                </div>
+            @endif
+        </div>
+
+        <div class="row mt-3">
+            @if($calendar->status == 0)
+                <div class="col-md-6">
+                    <button type="button" class="btn--ui primary btSave" style="background-color:red !important" onclick="showHideDelete()">Annulla lezione per selezionati</button>                    
+                </div>                                
+            @else
+                LEZIONE ANNULLATA
+            @endif
+        </div>
+
+    
+
+    <div  wire:ignore.self class="modal fade" id="userModal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true">
+        <div class="modal-dialog">
+            <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="userModalLabel">Inserimento nuovo utente</h5>
+                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+            </div>
+            <div class="modal-body">
+                <div class="row mt-2">
+                    <div class="col-md-6">
+                        <label for="member_id" class="form-label">Aggiunge una persona</label>
+                        <select name="member_id" class="form-select memberClass" aria-label="Seleziona una persona" wire:model="member_id">
+                            <option value="">--Seleziona--
+                            @foreach($members as $member)
+                                <option value="{{$member->id}}">{{$member->last_name}} {{$member->first_name}} ({{$member->fiscal_code}})
+                            @endforeach
+                        </select>
+                    </div>
+                    <div class="col-md-6">
+                        <label for="newMotivation" class="form-label">Motivazione</label>
+                        <select class="form-select form-select-lg me-1 " wire:model="newMemberMotivationId">
+                            <option value="">
+                            @foreach($motivations_add as $m)
+                                <option value="{{$m["id"]}}">{{$m["name"]}}</option>
+                            @endforeach
+                        </select>
+                    </div>
+                </div>
+                <br>
+                ----- Oppure inserisci un nuovo utente ------
+                <br><br>
+                <div class="row">
+                    <div class="col-md-6">
+                        <label for="newMemberFirstName" class="form-label">Nome</label>
+                        <input class="form-control @error('newMemberFirstName') is-invalid @enderror" type="text" id="newMemberFirstName" placeholder="Nome" wire:model="newMemberFirstName">
+                    </div>
+                    <div class="col-md-6">
+                        <label for="newMemberLastName" class="form-label">Cognome</label>
+                        <input class="form-control @error('newMemberLastName') is-invalid @enderror" type="text" id="newMemberLastName" placeholder="Cognome" wire:model="newMemberLastName">
+                    </div>
+                </div>
+                <div class="row mt-2">
+                    <div class="col-md-6">
+                        <label for="newMemberEmail" class="form-label">Email</label>
+                        <input class="form-control @error('newMemberEmail') is-invalid @enderror" type="text" id="newMemberEmail" placeholder="Email" wire:model="newMemberEmail">
+                    </div>
+                    <div class="col-md-6">
+                        <label for="newMemberFiscalCode" class="form-label">Codice fiscale</label>
+                        <input class="form-control @error('newMemberFiscalCode') is-invalid @enderror" type="text" id="newMemberFiscalCode" placeholder="Codice fiscale" maxlength="16" wire:model="newMemberFiscalCode">                        
+                    </div>
+                </div>
+                <div class="row mt-2">
+                    <div class="col-md-6">
+                        <input type="checkbox" id="newMemberToComplete" wire:model="newMemberToComplete">
+                        <label for="newMemberToComplete" class="form-label">Tesserato</label>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button class="btn--ui lightGrey" onclick="annulla()">annulla</a>
+                <button type="button" class="btn--ui btn-primary" wire:click.prevent="createMember()">Salva</button>
+            </div>
+            </div>
+        </div>
+    </div>
+
+    <div  wire:ignore.self class="modal fade" id="instructorModal" tabindex="-1" aria-labelledby="instructorModalLabel" aria-hidden="true">
+        <div class="modal-dialog">
+            <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="instructorModalLabel">Inserimento nuovo istruttore</h5>
+                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-md-6">
+                        <label for="userName" class="form-label">Nome</label>
+                        <input class="form-control @error('userName') is-invalid @enderror" type="text" id="userName" placeholder="Nome" wire:model="userName">
+                    </div>
+                    <div class="col-md-6">
+                        <label for="userEmail" class="form-label">Email</label>
+                        <input class="form-control @error('userEmail') is-invalid @enderror" type="text" id="userEmail" placeholder="Email" wire:model="userEmail">
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button class="btn--ui lightGrey" onclick="annulla()">annulla</a>
+                <button type="button" class="btn--ui btn-primary" wire:click.prevent="createInstructor()">Salva</button>
+            </div>
+            </div>
+        </div>
+    </div>
+    <br><br>
+
+</div>
+
+@push('scripts')
+    <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
+    <style>
+        table.tableHead thead {
+        /* Important */
+            position: sticky;
+            z-index: 100;
+            top: 0;
+        }
+        .select2-container--default .select2-selection--single{
+            background-color: #E9F0F5;
+            border: 0.0625rem solid #DFE5EB;
+            font-size: 0.75rem;
+        }
+        .select2-selection
+        {
+            height: 38px !important;
+        }
+        .select2-selection__rendered
+        {
+            padding-top:3px;
+        }
+        .select2 {
+            width:100% !important;
+        }
+        .page-link.active, .active > .page-link {
+            background-color:#006099 !important;
+        }
+
+        .select2-selection--multiple{
+            overflow: hidden !important;
+            height: auto !important;
+        }
+        .select2-container {
+            box-sizing: border-box;
+            display: inline-block;
+            margin: 0;
+            position: relative;
+            vertical-align: middle;
+        }
+        .select2-container .select2-selection--single {
+            box-sizing: border-box;
+            cursor: pointer;
+            display: block;
+            height: 38px;
+            user-select: none;
+            -webkit-user-select: none;
+        }
+        .select2-container .select2-selection--single .select2-selection__rendered {
+            display: block;
+            padding-left: 8px;
+            padding-right: 20px;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+        }
+        /* .total.primary
+        {
+            font-size:38px !important;
+        } */
+        /* .total.primary.comp
+        {
+            font-size:32px !important;
+        } */
+    </style>
+    <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')
+    <script>
+
+        $(document).ready(function() {
+            
+            setTimeout(() => {
+                $('.memberClass').select2({
+                    tags: false
+                });
+                $('.memberClass').on('change', function (e) {
+                    var data = $('.memberClass').select2("val");
+                    @this.addMember(data);
+                });                
+            }, 100);
+
+        } );
+
+        Livewire.on('reload', () => {
+            setTimeout(() => {
+                $('.memberClass').select2({
+                    tags: false
+                });
+                $('.memberClass').on('change', function (e) {
+                    var data = $('.memberClass').select2("val");
+                    @this.addMember(data);
+                });                
+            }, 100);
+            $(".showDelete").hide();
+            $(".btSave").show();
+        });
+
+        window.livewire.on('saved', () => {
+            $('#userModal').modal('hide');
+            $('#deleteModal').modal('hide');
+            $('#instructorModal').modal('hide');
+        });
+
+        window.livewire.on('deleteSaved', () => {
+            $('#deleteModal').modal('hide');
+        });
+
+        function save()
+        {
+            var ids = [];
+            $('input[type=checkbox]').each(function () {
+                if ($(this).is(":checked")) 
+                {
+                    var val = $(this).val();
+                    ids.push(val);
+                }
+            });
+
+            @this.save(ids);
+        }
+
+        function cancel()
+        {
+            var ids = [];
+            $('input[type=checkbox]').each(function () {
+                if ($(this).is(":checked")) 
+                {
+                    var val = $(this).val();
+                    ids.push(val);
+                }
+            });
+            var motivation_id = $("#motivation_id").val();
+            @this.cancel(ids, motivation_id);
+        }
+
+        function annulla()
+        {
+            $('#userModal').modal('hide');
+            $('#deleteModal').modal('hide');
+            $('#instructorModal').modal('hide');
+        }
+
+        function showHideDelete()
+        {
+            $(".showDelete").show();
+            $(".btSave").hide();
+        }
+        
+    </script>
+@endpush

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

@@ -1230,6 +1230,7 @@
                     url : '/get_record_in?filterCommercial=' + filterCommercial + "&filterMember=" + filterMember + "&filterPaymentMethod=" + filterPaymentMethod + "&filterCausals=" + filterCausals + "&filterFrom=" + filterFrom + "&filterTo=" + filterTo,
                     dataSrc: function (json){
                         if(json.totals){
+                            
                             $(".totalDiv").html('Totale&nbsp;:&nbsp;<b>' + json.totals + '</b>');
                         }
                         else

+ 14 - 0
resources/views/livewire/settings.blade.php

@@ -59,6 +59,13 @@
                     </div>
                 </a>
                 <hr size="1">
+                <a href="/courts">
+                    <div class="row">
+                        <div class="col-md-11 p-2"><h5>Campi</h5></div>
+                        <div class="col-md-1 p-2"><i class="fa-solid fa-chevron-right"></i></div>
+                    </div>
+                </a>
+                <hr size="1">
                 <a href="/courses">
                     <div class="row">
                         <div class="col-md-11 p-2"><h5>Corsi</h5></div>
@@ -87,6 +94,13 @@
                     </div>
                 </a>
                 <hr size="1">
+                <a href="/motivations">
+                    <div class="row">
+                        <div class="col-md-11 p-2"><h5>Motivazioni annullamento</h5></div>
+                        <div class="col-md-1 p-2"><i class="fa-solid fa-chevron-right"></i></div>
+                    </div>
+                </a>
+                <hr size="1">
                 <a href="/course_types">
                     <div class="row">
                         <div class="col-md-11 p-2"><h5>Tipologie</h5></div>

+ 16 - 4
routes/web.php

@@ -83,6 +83,10 @@ 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('/calendar', \App\Http\Livewire\Calendar::class);
+    Route::get('/presences', \App\Http\Livewire\Presence::class);
+    Route::get('/courts', \App\Http\Livewire\Court::class);
+    Route::get('/motivations', \App\Http\Livewire\Motivation::class);
 });
 
 Route::get('/receipt/{id}', function ($id) {
@@ -461,7 +465,7 @@ Route::get('/get_record_in', function () {
                 ->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.record_id', 'records_rows.amount', 'records.member_id', 'records.corrispettivo_fiscale', 'records.commercial', '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')
@@ -592,23 +596,31 @@ Route::get('/get_record_in', function () {
     if ($_GET["filterCausals"] != "null")
         $causals = explode(",", $_GET["filterCausals"]);
 
+    $aIds = [];
     foreach ($y->get() as $r) {
 
         if (!in_array($r->payment_method_id, $moneys)) {
-            if ((!in_array($r->member_id, $exclude_from_records) || in_array($r->causal_id, $moneysCausal)) && (!$r->deleted || $r->deleted == null) && (!in_array($r->causal_id, $excludeCausals) || in_array($r->causal_id, $moneysCausal)) && (!$r->financial_movement || $r->financial_movement == null) && (!$r->corrispettivo_fiscale || $r->corrispettivo_fiscale == null)) {
+            //if ((!in_array($r->member_id, $exclude_from_records) || in_array($r->causal_id, $moneysCausal)) && (!$r->deleted || $r->deleted == null) && (!in_array($r->causal_id, $excludeCausals) || in_array($r->causal_id, $moneysCausal)) && (!$r->financial_movement || $r->financial_movement == null) && (!$r->corrispettivo_fiscale || $r->corrispettivo_fiscale == null) && !$r->commercial) {
+            if ((!in_array($r->member_id, $exclude_from_records) || in_array($r->causal_id, $moneysCausal)) && (!$r->deleted || $r->deleted == null) && (!in_array($r->causal_id, $excludeCausals) || in_array($r->causal_id, $moneysCausal)) && (!$r->financial_movement || $r->financial_movement == null)) {
                 if (sizeof($causals) == 0 || in_array($r->causal_id, $causals)) {
                     $total += $r->amount;
                     if ($r->vat_id > 0)
                         $total += getVatValue($r->amount, $r->vat_id);
+
+                    $aIds[] = array('id' => $r->record_id, 'amount' => $r->amount);
                 }
             }
         }
+
+        
     }
+    print_r($aIds);
+    die;
+    Log::info('values', [$aIds]);
+    die;
 
     $count = $x->count();
 
-
-
     if (isset($_GET["order"])) {
         $column = '';
         if ($_GET["order"][0]["column"] == 0)

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff