From 95c9180f5265f234be4142579db2c7c58c19ea4f Mon Sep 17 00:00:00 2001 From: Francesco Picone Date: Sat, 25 Jul 2026 23:28:41 +0200 Subject: [PATCH] ++ fix grafici, aggiunta statistiche su area assegnatario, sezione profilo --- HANDOFF.md | 19 ++++++++ app/Livewire/Profile.php | 15 +++++++ app/Models/Assegnazione.php | 34 ++++++++++++++ .../views/assignments/pdf-viewer.blade.php | 21 +++++++++ .../views/components/layouts/app.blade.php | 16 ++++--- resources/views/livewire/profile.blade.php | 44 +++++++++++++++++++ routes/web.php | 10 ++++- 7 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 app/Livewire/Profile.php create mode 100644 resources/views/livewire/profile.blade.php diff --git a/HANDOFF.md b/HANDOFF.md index 9e11a9b..af85aaa 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -162,6 +162,25 @@ regola è un no-op (nessun territorio assegnato è prioritario); su backup vecch con stato contraddittorio l'import normalizza il flag anziché riprodurlo tale e quale. +### Profilo utente, viewer PDF, privacy admin-only + +Tre ritocchi minori ma con qualche implicazione: + +- **Pagina profilo** (`Profile` → rotta `profilo`, nome `profile`) accessibile a + ogni utente loggato (nel gruppo `auth`, nessun permesso): mostra nome, email e + qualifica del solo utente corrente (sola lettura). Nell'header il blocco utente + è ora un link a questa pagina, con nome in evidenza ed `email · qualifica` + sotto. Non esiste un campo "username": l'email fa da identificativo. +- **Viewer PDF pubblico**: oltre a territorio/proclamatore/data mostra ora + *giorni assegnato*, *consegna prevista* e *scadenza*. Sono attributi calcolati + su `Assegnazione` (`data_consegna_presunta`, `giorni_alla_scadenza`) basati + sulla soglia `giorni_per_smarrito` — la stessa oltre cui `Territorio::stato` + diventa `da_rientrare`. Il viewer è servito solo per assegnazioni aperte, ma + gli accessor gestiscono comunque il caso `returned_at` (ritornano null). +- **Privacy solo admin**: la rotta `privacy` è passata dal solo `auth` al gruppo + `permission:settings.manage` e il link in sidebar è sotto + `@can('settings.manage')`. + ### Export/Import XML: backup completo e round-trip `Livewire/Settings/XmlExchange` esporta e reimporta **tutti** i dati gestionali, diff --git a/app/Livewire/Profile.php b/app/Livewire/Profile.php new file mode 100644 index 0000000..88a802e --- /dev/null +++ b/app/Livewire/Profile.php @@ -0,0 +1,15 @@ + auth()->user(), + ]); + } +} diff --git a/app/Models/Assegnazione.php b/app/Models/Assegnazione.php index 2dd0846..01e99d6 100644 --- a/app/Models/Assegnazione.php +++ b/app/Models/Assegnazione.php @@ -103,6 +103,40 @@ class Assegnazione extends Model return is_null($this->returned_at); } + /** + * Scadenza dell'assegnazione: data entro cui il territorio andrebbe + * riconsegnato, cioè assigned_at + la soglia "giorni_per_smarrito" (la + * stessa oltre cui Territorio::stato diventa "da_rientrare"). Null per le + * assegnazioni già rientrate. + */ + public function getDataConsegnaPresuntaAttribute(): ?Carbon + { + if ($this->returned_at !== null) { + return null; + } + + $soglia = (int) Setting::getValue('giorni_per_smarrito', 120); + + return Carbon::parse($this->assigned_at)->addDays($soglia); + } + + /** + * Giorni che mancano alla scadenza: positivo se ancora entro i termini, + * negativo (giorni di ritardo) se già scaduta. Null se rientrata. + */ + public function getGiorniAllaScadenzaAttribute(): ?int + { + $scadenza = $this->data_consegna_presunta?->copy()->startOfDay(); + if (! $scadenza) { + return null; + } + + $oggi = Carbon::today(); + $giorni = (int) $oggi->diffInDays($scadenza); + + return $oggi->lessThanOrEqualTo($scadenza) ? $giorni : -$giorni; + } + public function ensurePdfAccessCode(): string { if ($this->pdf_access_code) { diff --git a/resources/views/assignments/pdf-viewer.blade.php b/resources/views/assignments/pdf-viewer.blade.php index 1fc0323..9e2476c 100644 --- a/resources/views/assignments/pdf-viewer.blade.php +++ b/resources/views/assignments/pdf-viewer.blade.php @@ -215,6 +215,27 @@ Data {{ $assignment->assigned_at->format('d/m/Y') }} +
+ Giorni assegnato + {{ $assignment->giorni }} +
+
+ Consegna prevista + {{ optional($assignment->data_consegna_presunta)->format('d/m/Y') ?? '—' }} +
+ @php($giorniScadenza = $assignment->giorni_alla_scadenza) +
+ Scadenza + + @if($giorniScadenza === null) + — + @elseif($giorniScadenza >= 0) + tra {{ $giorniScadenza }} {{ $giorniScadenza === 1 ? 'giorno' : 'giorni' }} + @else + scaduto da {{ abs($giorniScadenza) }} {{ abs($giorniScadenza) === 1 ? 'giorno' : 'giorni' }} + @endif + +
@if($showDownload) diff --git a/resources/views/components/layouts/app.blade.php b/resources/views/components/layouts/app.blade.php index 99e6ba0..8a45775 100644 --- a/resources/views/components/layouts/app.blade.php +++ b/resources/views/components/layouts/app.blade.php @@ -32,13 +32,15 @@
-
@csrf
@endcan + @can('settings.manage')
@@ -156,6 +159,7 @@ Privacy
+ @endcan diff --git a/resources/views/livewire/profile.blade.php b/resources/views/livewire/profile.blade.php new file mode 100644 index 0000000..fb4493c --- /dev/null +++ b/resources/views/livewire/profile.blade.php @@ -0,0 +1,44 @@ +
+
+ + + Torna alla home + +

Il mio profilo

+
+ +
+ {{-- Intestazione: avatar iniziale + nome --}} +
+
+ {{ strtoupper(mb_substr($user->name, 0, 1)) }} +
+
+

{{ $user->name }}

+

+ {{ $user->email }} + + {{ ucfirst($user->roles->first()?->name ?? 'utente') }} + +

+
+
+ + {{-- Dettagli --}} +
+
+
Nome
+
{{ $user->name }}
+
+
+
Email
+
{{ $user->email }}
+
+
+
Qualifica
+
{{ ucfirst($user->roles->first()?->name ?? 'utente') }}
+
+
+
+
diff --git a/routes/web.php b/routes/web.php index f12d339..ff077f4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -31,6 +31,7 @@ use App\Livewire\Settings\TipologieIndex; use App\Livewire\Settings\UsersIndex; use App\Livewire\Settings\XmlExchange; use App\Livewire\Privacy; +use App\Livewire\Profile; /* |-------------------------------------------------------------------------- @@ -66,6 +67,9 @@ Route::middleware('auth')->group(function () { // Home Dashboard Route::get('/', Home::class)->name('dashboard'); + // Profilo personale (ogni utente loggato) + Route::get('profilo', Profile::class)->name('profile'); + // Territori — visualizzazione (lista + dettaglio) per tutti gli utenti // operativi, cioè chi può assegnare i territori. Route::middleware('permission:territori.assign')->group(function () { @@ -137,6 +141,8 @@ Route::middleware('auth')->group(function () { Route::post('imports/territori/pdf-zip', [TerritoryPdfImportController::class, 'storeZip'])->name('imports.territori.pdf-zip'); }); - // Privacy / Informativa GDPR - Route::get('privacy', Privacy::class)->name('privacy'); + // Privacy / Informativa GDPR — visibile solo all'amministratore. + Route::middleware('permission:settings.manage')->group(function () { + Route::get('privacy', Privacy::class)->name('privacy'); + }); });