From 69072b6f09852770837813faee818dded18bb27d Mon Sep 17 00:00:00 2001 From: Francesco Picone Date: Sat, 25 Jul 2026 23:48:56 +0200 Subject: [PATCH] ++ fix permessi e mobile portrait --- HANDOFF.md | 34 ++++++++++ app/Livewire/Campagne/CampagnaCreate.php | 3 + .../Proclamatori/ProclamatoreCestino.php | 4 ++ .../Proclamatori/ProclamatoreCreate.php | 2 + .../Proclamatori/ProclamatoreEdit.php | 2 + .../Proclamatori/ProclamatoreIndex.php | 10 +++ resources/css/app.css | 65 +++++++++++++++++++ resources/views/livewire/audit-log.blade.php | 12 ++-- .../campagne/campagna-index.blade.php | 16 ++--- .../proclamatore-cestino.blade.php | 10 +-- .../proclamatori/proclamatore-index.blade.php | 18 ++--- .../proclamatori/proclamatore-show.blade.php | 2 +- resources/views/livewire/registro.blade.php | 20 +++--- .../livewire/settings/users-index.blade.php | 10 +-- .../territori/territorio-cestino.blade.php | 8 +-- .../territori/territorio-index.blade.php | 14 ++-- routes/web.php | 20 +++++- 17 files changed, 194 insertions(+), 56 deletions(-) diff --git a/HANDOFF.md b/HANDOFF.md index f36a1d6..67e37c9 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -147,6 +147,40 @@ assistente e operatore); creazione/modifica/cestino restano su `territori.manage sono sotto `@can('territori.manage')`; il link "Dettaglio" resta visibile a tutti. Il menu laterale mostra "Territori" a chi ha `territori.assign`. +### Sola lettura per proclamatori e campagne (non-admin) + +Stessa impostazione dei territori estesa ad altre due sezioni: + +- **Proclamatori**: `index` e `show` restano su `proclamatori.manage` (l'assistente + li vede in sola lettura); `create`/`edit`/`cestino` e tutte le mutazioni + (`ProclamatoreIndex::toggleActive`/`deleteProclamatore`/`anonimizza`, + `ProclamatoreCreate::save`, `ProclamatoreEdit::save`, + `ProclamatoreCestino::restore`/`forceDelete`) richiedono `settings.manage`. + `ProclamatoreIndex` è servito da una rotta rilassata, quindi i suoi metodi di + mutazione hanno il check interno; i pulsanti in `proclamatore-index`/`-show`/ + `-cestino` sono sotto `@can('settings.manage')`. Le rotte letterali + create/cestino restano prima del wildcard `{proclamatore}`. +- **Campagne**: la **creazione** è ora admin-only come già modifica/eliminazione + — `CampagnaCreate::save` chiama `authorize('settings.manage')`, la rotta + `campagne/create` è nel gruppo `settings.manage` (registrata prima del + wildcard) e il pulsante "Nuova Campagna" è sotto `@can('settings.manage')`. + `index`/`show` restano su `campagne.manage` (sola lettura per l'assistente). + +### Tabelle-elenco responsive su mobile (portrait) + +Le liste principali usano una sola "politica" CSS invece di layout duplicati: +la classe `table-cards` (in `resources/css/app.css`) sotto `640px` nasconde il +`thead` e trasforma ogni riga in una scheda, con ogni cella resa come +"ETICHETTA … valore" (l'etichetta viene dall'attributo `data-label` della ``). +Un telefono in portrait sta sotto la soglia → schede; ruotato in landscape la +larghezza supera i 640px → torna la tabella normale (per questo landscape "non ha +problemi"). Convenzioni: ogni `` dati ha `data-label="..."`; la cella azioni +ha classe `cell-actions` (niente label, occupa la riga a piena larghezza); le +celle `colspan` (stato vuoto, form di modifica utente inline) restano `block` a +piena larghezza. Applicata a: territori, proclamatori, campagne, registro, +utenti, audit-log e i due cestini. La pagina *Assegnazioni* era già ottimizzata e +non è stata toccata. + ### Assegnare un territorio ne azzera il flag "prioritario" `prioritario` è un flag **manuale**; `is_prioritario` è true se il flag è acceso diff --git a/app/Livewire/Campagne/CampagnaCreate.php b/app/Livewire/Campagne/CampagnaCreate.php index b3fad84..4ebf7a7 100644 --- a/app/Livewire/Campagne/CampagnaCreate.php +++ b/app/Livewire/Campagne/CampagnaCreate.php @@ -22,6 +22,9 @@ class CampagnaCreate extends Component public function save() { + // Creazione campagne riservata all'amministratore. + $this->authorize('settings.manage'); + $this->validate(); $campagna = Campagna::create([ diff --git a/app/Livewire/Proclamatori/ProclamatoreCestino.php b/app/Livewire/Proclamatori/ProclamatoreCestino.php index 4dfb2a7..a08f4ac 100644 --- a/app/Livewire/Proclamatori/ProclamatoreCestino.php +++ b/app/Livewire/Proclamatori/ProclamatoreCestino.php @@ -12,6 +12,8 @@ class ProclamatoreCestino extends Component public function restore(int $id) { + $this->authorize('settings.manage'); + $proclamatore = Proclamatore::onlyTrashed()->findOrFail($id); $proclamatore->restore(); activity()->causedBy(auth()->user()) @@ -22,6 +24,8 @@ class ProclamatoreCestino extends Component public function forceDelete(int $id) { + $this->authorize('settings.manage'); + $proclamatore = Proclamatore::onlyTrashed()->findOrFail($id); if ($proclamatore->assegnazioni()->exists()) { diff --git a/app/Livewire/Proclamatori/ProclamatoreCreate.php b/app/Livewire/Proclamatori/ProclamatoreCreate.php index 333a74d..3eecb5d 100644 --- a/app/Livewire/Proclamatori/ProclamatoreCreate.php +++ b/app/Livewire/Proclamatori/ProclamatoreCreate.php @@ -22,6 +22,8 @@ class ProclamatoreCreate extends Component public function save() { + $this->authorize('settings.manage'); + $this->validate(); $proclamatore = Proclamatore::create([ diff --git a/app/Livewire/Proclamatori/ProclamatoreEdit.php b/app/Livewire/Proclamatori/ProclamatoreEdit.php index a8cd876..637624f 100644 --- a/app/Livewire/Proclamatori/ProclamatoreEdit.php +++ b/app/Livewire/Proclamatori/ProclamatoreEdit.php @@ -31,6 +31,8 @@ class ProclamatoreEdit extends Component public function save() { + $this->authorize('settings.manage'); + $this->validate(); $this->proclamatore->update([ diff --git a/app/Livewire/Proclamatori/ProclamatoreIndex.php b/app/Livewire/Proclamatori/ProclamatoreIndex.php index 27637ee..c1946ce 100644 --- a/app/Livewire/Proclamatori/ProclamatoreIndex.php +++ b/app/Livewire/Proclamatori/ProclamatoreIndex.php @@ -42,12 +42,20 @@ class ProclamatoreIndex extends Component public function toggleActive(int $id) { + // La lista è in sola lettura per i non-admin: creare/modificare/ + // eliminare proclamatori è riservato all'amministratore. Il check sta + // nel metodo perché la rotta index resta accessibile con + // proclamatori.manage. + $this->authorize('settings.manage'); + $proclamatore = Proclamatore::findOrFail($id); $proclamatore->update(['attivo' => !$proclamatore->attivo]); } public function deleteProclamatore(int $id) { + $this->authorize('settings.manage'); + $proclamatore = Proclamatore::findOrFail($id); if ($proclamatore->assegnazioni()->aperte()->exists()) { @@ -61,6 +69,8 @@ class ProclamatoreIndex extends Component public function anonimizza(int $id) { + $this->authorize('settings.manage'); + $proclamatore = Proclamatore::findOrFail($id); $proclamatore->anonimizza(); session()->flash('success', "Dati personali del proclamatore anonimizzati (GDPR)."); diff --git a/resources/css/app.css b/resources/css/app.css index ff4686e..3d992eb 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -101,3 +101,68 @@ 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } } + +/* ─── Responsive list tables ─────────────────────────────────────────────── + Under 640px (a phone held in portrait; rotate to landscape and the width + grows past the breakpoint, restoring the normal table) every table with the + `table-cards` class reflows: the header is hidden and each row becomes a + card, with each cell shown as "LABEL … value" (the label comes from the + cell's data-label attribute). Cells with no data-label (the actions column, + marked `.cell-actions`) span the row full width. */ +@media (max-width: 640px) { + table.table-cards, + table.table-cards tbody { + display: block; + width: 100%; + } + table.table-cards thead { + display: none; + } + table.table-cards tr { + display: block; + border: 1px solid #e5e7eb; + border-radius: 10px; + margin-bottom: 12px; + padding: 4px 2px; + background: #fff; + } + /* zebra striping would fight the card borders */ + table.table-cards.table-striped tbody tr:nth-child(even) { + background: #fff; + } + table.table-cards td { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + width: 100%; + text-align: right; + padding: 6px 12px; + border: none !important; + } + table.table-cards td[data-label]::before { + content: attr(data-label); + flex: 0 0 auto; + margin-right: auto; + text-align: left; + font-size: 11px; + font-weight: 600; + color: #6b7280; + text-transform: uppercase; + letter-spacing: .3px; + } + /* Actions row: full width, buttons wrapped to the right */ + table.table-cards td.cell-actions { + justify-content: flex-end; + flex-wrap: wrap; + margin-top: 2px; + padding-top: 10px; + border-top: 1px solid #f3f4f6 !important; + } + /* Cells spanning all columns (empty state, inline edit form) stay full + width and block, so their inner layout isn't squeezed by the flex row. */ + table.table-cards td[colspan] { + display: block; + text-align: center; + } +} diff --git a/resources/views/livewire/audit-log.blade.php b/resources/views/livewire/audit-log.blade.php index e43eaee..8c99f1b 100644 --- a/resources/views/livewire/audit-log.blade.php +++ b/resources/views/livewire/audit-log.blade.php @@ -26,7 +26,7 @@ {{-- Table --}}
- +
@@ -39,9 +39,9 @@ @forelse($activities as $activity) - - - + + -
Data/Ora
{{ $activity->created_at->format('d/m/Y H:i:s') }}{{ $activity->causer?->name ?? data_get($activity->properties, 'causer_name') ?? 'Sistema' }} + {{ $activity->created_at->format('d/m/Y H:i:s') }}{{ $activity->causer?->name ?? data_get($activity->properties, 'causer_name') ?? 'Sistema' }} + @if($activity->subject_type) {{ class_basename($activity->subject_type) }} #{{ $activity->subject_id }} @else - @endif + @if($activity->properties->isNotEmpty())
Mostra diff --git a/resources/views/livewire/campagne/campagna-index.blade.php b/resources/views/livewire/campagne/campagna-index.blade.php index 624cfc9..899a83c 100644 --- a/resources/views/livewire/campagne/campagna-index.blade.php +++ b/resources/views/livewire/campagne/campagna-index.blade.php @@ -1,7 +1,7 @@

Campagne

- @can('campagne.manage') + @can('settings.manage') Nuova Campagna @@ -11,7 +11,7 @@
- +
@@ -25,10 +25,10 @@ @forelse($campagne as $campagna) - - - - + + + - -
Descrizione
{{ $campagna->descrizione }}{{ $campagna->start_date->format('d/m/Y') }}{{ $campagna->end_date->format('d/m/Y') }} + {{ $campagna->descrizione }}{{ $campagna->start_date->format('d/m/Y') }}{{ $campagna->end_date->format('d/m/Y') }} @if($campagna->is_attiva) Attiva @elseif($campagna->end_date->isPast()) @@ -37,7 +37,7 @@ Futura @endif +
@@ -45,7 +45,7 @@ {{ $campagna->percentuale_percorrenza }}%
+
diff --git a/resources/views/livewire/proclamatori/proclamatore-cestino.blade.php b/resources/views/livewire/proclamatori/proclamatore-cestino.blade.php index 1e63dbf..250c396 100644 --- a/resources/views/livewire/proclamatori/proclamatore-cestino.blade.php +++ b/resources/views/livewire/proclamatori/proclamatore-cestino.blade.php @@ -9,7 +9,7 @@
- +
@@ -20,13 +20,15 @@ @forelse($proclamatori as $proclamatore) - - - + + @empty diff --git a/resources/views/livewire/proclamatori/proclamatore-index.blade.php b/resources/views/livewire/proclamatori/proclamatore-index.blade.php index fe248a4..a8244b3 100644 --- a/resources/views/livewire/proclamatori/proclamatore-index.blade.php +++ b/resources/views/livewire/proclamatori/proclamatore-index.blade.php @@ -1,7 +1,7 @@

Proclamatori

- @can('proclamatori.manage') + @can('settings.manage') Nuovo Proclamatore @@ -19,7 +19,7 @@ @@ -29,7 +29,7 @@ {{-- Table --}}
-
Nome Completo
{{ $proclamatore->nome_completo }}{{ $proclamatore->deleted_at->format('d/m/Y H:i') }} + {{ $proclamatore->nome_completo }}{{ $proclamatore->deleted_at->format('d/m/Y H:i') }} + @can('settings.manage')
+ @endcan
+
@forelse($proclamatori as $proclamatore) - - - + + - - +
@@ -46,22 +46,22 @@
{{ $proclamatore->cognome }}{{ $proclamatore->nome }} + {{ $proclamatore->cognome }}{{ $proclamatore->nome }} @if($proclamatore->attivo) Attivo @else Inattivo @endif {{ $proclamatore->assegnazioni_aperte_count ?? $proclamatore->assegnazioni()->aperte()->count() }} + {{ $proclamatore->assegnazioni_aperte_count ?? $proclamatore->assegnazioni()->aperte()->count() }}
- @can('proclamatori.manage') + @can('settings.manage') diff --git a/resources/views/livewire/proclamatori/proclamatore-show.blade.php b/resources/views/livewire/proclamatori/proclamatore-show.blade.php index aca6a5f..eb2bc1e 100644 --- a/resources/views/livewire/proclamatori/proclamatore-show.blade.php +++ b/resources/views/livewire/proclamatori/proclamatore-show.blade.php @@ -7,7 +7,7 @@

{{ $proclamatore->nome_completo }}

- @can('proclamatori.manage') + @can('settings.manage') Modifica diff --git a/resources/views/livewire/registro.blade.php b/resources/views/livewire/registro.blade.php index ce81745..9bfd043 100644 --- a/resources/views/livewire/registro.blade.php +++ b/resources/views/livewire/registro.blade.php @@ -42,7 +42,7 @@ {{-- Table --}}
- +
@php($temporaryPdfUrl = !$a->returned_at ? $a->shortPdfUrl() : null) - - - - + + - - - + + @can('settings.manage') -
@@ -67,7 +67,7 @@ @forelse($assegnazioni as $a)
+
@if($a->territorio?->thumbnail_path)
{{ $a->proclamatore?->nome_completo ?? 'N/A' }}{{ $a->assigned_at->format('d/m/Y') }} + {{ $a->proclamatore?->nome_completo ?? 'N/A' }}{{ $a->assigned_at->format('d/m/Y') }} @if($a->returned_at) {{ $a->returned_at->format('d/m/Y') }} @else In corso @endif {{ $a->giorni }}{{ $a->annoTeocratico?->label }} + {{ $a->giorni }}{{ $a->annoTeocratico?->label }} @if($a->counted_in_campaign) {{ $a->campagna?->descrizione }} @else @@ -99,8 +99,8 @@ @endif -
+
+
@if($temporaryPdfUrl) Utenti esistenti
- +
@@ -111,9 +111,9 @@ @else - - - + + -
Nome {{ $user->name }}{{ $user->email }} + {{ $user->name }}{{ $user->email }}
@forelse($user->roles as $role) {{ $role->name }} @@ -122,7 +122,7 @@ @endforelse
+