diff --git a/HANDOFF.md b/HANDOFF.md index 5d82247..37a5ce9 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -111,6 +111,19 @@ lavoro nuovi, stesso master), `php-fpm -i` riporta `session.upload_progress.enabled => Off` e `session.use_strict_mode => On`, login 200 su entrambi. +### Anteprima dal numero in "Il mio gruppo" (21 ottobre 2026, solo su dev) + +Richiesta dell'utente: anche il sorvegliante deve poter vedere l'anteprima del +territorio, con un popup al clic del numero. Il numero diventa un pulsante +(stesso aspetto, `button.tm-assegnato-numero` in `app.css`) che apre una +finestra (`tm-modale`, stato Alpine `anteprima` sulla radice della vista) con +la miniatura e "Apri la piantina (PDF)". La miniatura passa da una rotta +nuova, `mio-gruppo.miniatura` (`TerritorioFileController::miniaturaGruppo`, +`Territorio::miniaturaGruppoUrl()`), con la stessa regola di +`mio-gruppo.pdf`: solo i territori del pacchetto del gruppo che l'utente +sorveglia. La rotta normale delle miniature resta chiusa al sorvegliante. +Senza miniatura né PDF il numero resta testo. + ### Assegna aperta dalla Home: senza l'elenco dei territori assegnati (20 ottobre 2026) Richiesta dell'utente: "quando provengo da un territorio dalla home, non @@ -197,7 +210,8 @@ qui sotto **com'è oggi**, in fondo la storia. (nell'indirizzo). Il sorvegliante assegna, manda il link (copia o condividi: trait `Concerns\InviaLink` + partial `assegnazioni/_invio-link`, gli stessi di *Assegna*), registra i rientri, apre la piantina, rimanda in - reparto quelli non dati. + reparto quelli non dati. Il numero del territorio apre l'anteprima (finestra + Alpine nella vista, miniatura e collegamento al PDF). - **Assegnazione dal pacchetto**: un'**assegnazione normale**, con `gruppo_servizio_id` e `gruppo_nome` ("via gruppo …" in registro e schede). `GruppiServizio::assegna()` rilegge territorio e membro e ricontrolla il diff --git a/README.md b/README.md index 3ff1eba..7884d08 100644 --- a/README.md +++ b/README.md @@ -774,7 +774,8 @@ docker compose --profile dev up -d --build pacchetto è in ordine di priorità (prima i da dare, prioritari in testa, poi quelli già dati) e si filtra per stato, zona, tipologia e "solo prioritari"; sotto il nome del gruppo compaiono nome e cognome del - sorvegliante + sorvegliante. Toccando il numero di un territorio si apre l'**anteprima** + (la miniatura della piantina) con il collegamento al PDF - Al **rientro** il territorio torna in reparto ed esce dal pacchetto - **Sciogliere il gruppo** toglie il ruolo al sorvegliante e **elimina il gruppo**, che non resta nello storico: i territori solo nel pacchetto diff --git a/app/Http/Controllers/TerritorioFileController.php b/app/Http/Controllers/TerritorioFileController.php index 0b87e62..a6497a3 100644 --- a/app/Http/Controllers/TerritorioFileController.php +++ b/app/Http/Controllers/TerritorioFileController.php @@ -31,8 +31,7 @@ class TerritorioFileController extends Controller */ public function pdfGruppo(Territorio $territorio): Response { - $gruppo = auth()->user()->gruppoSorvegliato(); - abort_unless($gruppo && $territorio->gruppo_servizio_id === $gruppo->id, 403); + $this->delPacchetto($territorio); return $this->pdf($territorio); } @@ -42,6 +41,21 @@ class TerritorioFileController extends Controller return $this->servi($territorio->thumbnail_path, null, null); } + /** La miniatura per l'anteprima in "Il mio gruppo", con la stessa regola del PDF. */ + public function miniaturaGruppo(Territorio $territorio): Response + { + $this->delPacchetto($territorio); + + return $this->miniatura($territorio); + } + + /** Il territorio deve stare nel pacchetto del gruppo che l'utente sorveglia. */ + private function delPacchetto(Territorio $territorio): void + { + $gruppo = auth()->user()->gruppoSorvegliato(); + abort_unless($gruppo && $territorio->gruppo_servizio_id === $gruppo->id, 403); + } + public function versione(TerritorioPdfVersione $versione): Response { $numero = $versione->territorio?->numero ?? 'senza-territorio'; diff --git a/app/Models/Territorio.php b/app/Models/Territorio.php index 85ff8a7..4e08821 100644 --- a/app/Models/Territorio.php +++ b/app/Models/Territorio.php @@ -328,4 +328,12 @@ class Territorio extends Model ? route('territori.file.miniatura', ['territorio' => $this->id, 'v' => substr(md5($this->thumbnail_path), 0, 8)]) : null; } + + /** La miniatura per il sorvegliante di gruppo ("Il mio gruppo"): solo del suo pacchetto. */ + public function miniaturaGruppoUrl(): ?string + { + return $this->thumbnail_path + ? route('mio-gruppo.miniatura', ['territorio' => $this->id, 'v' => substr(md5($this->thumbnail_path), 0, 8)]) + : null; + } } diff --git a/resources/css/app.css b/resources/css/app.css index 1a7ccb6..b7a0552 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1805,6 +1805,14 @@ html { scroll-behavior: smooth; } } .tm-assegnato-numero { color: #4f46e5; font-size: 14px; font-weight: 700; } .tm-assegnato-numero:hover { text-decoration: underline; } +/* Il numero come pulsante ("Il mio gruppo": apre l'anteprima): stesso aspetto. */ +button.tm-assegnato-numero { + background: none; border: 0; padding: 0; cursor: pointer; + font-family: inherit; line-height: inherit; +} +button.tm-assegnato-numero:focus-visible { + outline: 2px solid var(--tm-accento); outline-offset: 2px; border-radius: 2px; +} .tm-assegnato-zona { margin-left: 6px; padding: 2px 6px; border-radius: 4px; background: #f3f4f6; color: #9ca3af; font-size: 12px; diff --git a/resources/views/livewire/gruppi/mio-gruppo.blade.php b/resources/views/livewire/gruppi/mio-gruppo.blade.php index f9f255a..9424b0f 100644 --- a/resources/views/livewire/gruppi/mio-gruppo.blade.php +++ b/resources/views/livewire/gruppi/mio-gruppo.blade.php @@ -1,8 +1,11 @@ -
+
{{-- Il mio gruppo (MioGruppo): la sola pagina del sorvegliante di gruppo. Pensata prima per il telefono: schede una sotto l'altra, pulsanti grandi. Copia, Condividi e ricerca vengono da elencoCercabile - (resources/js/app.js), l'invio del link dal partial _invio-link. --}} + (resources/js/app.js), l'invio del link dal partial _invio-link. + Il numero del territorio apre l'anteprima (stato Alpine `anteprima`, + finestra in fondo alla vista): miniatura e PDF dalle rotte + mio-gruppo.*, che rispondono solo per il pacchetto del gruppo. --}} @if(! $gruppo)
Non sei il sorvegliante di un gruppo di servizio attivo. @@ -81,7 +84,11 @@ @endphp
- N° {{ $t->numero }} @if($t->zona?->nome){{ $t->zona->nome }}@endif + @if($t->thumbnail_path || $t->pdf_path)@elseN° {{ $t->numero }}@endif @if($t->zona?->nome){{ $t->zona->nome }}@endif @if($t->in_priorita)★ Prioritario@endif @if($a) {{ $a->proclamatore?->nome_completo }} · dal {{ $a->assigned_at->format('d/m/Y') }} · {{ $a->giorni }}g @@ -156,5 +163,27 @@ @endforeach + + {{-- Anteprima del territorio: si apre dal numero, si chiude con la X, + con Esc o toccando fuori. --}} +
+ +
@endif
diff --git a/routes/web.php b/routes/web.php index 2fa1219..afec0c2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -178,6 +178,8 @@ Route::middleware(['auth', 'verified'])->group(function () { Route::get('mio-gruppo', MioGruppo::class)->name('mio-gruppo'); Route::get('mio-gruppo/territori/{territorio}/pdf', [TerritorioFileController::class, 'pdfGruppo']) ->name('mio-gruppo.pdf'); + Route::get('mio-gruppo/territori/{territorio}/miniatura', [TerritorioFileController::class, 'miniaturaGruppo']) + ->name('mio-gruppo.miniatura'); }); // Campagne — visualizzazione per chi ha campagne.manage; sola lettura per i diff --git a/tests/Feature/GruppiServizioTest.php b/tests/Feature/GruppiServizioTest.php index a65b99c..d0ea87d 100644 --- a/tests/Feature/GruppiServizioTest.php +++ b/tests/Feature/GruppiServizioTest.php @@ -352,6 +352,29 @@ class GruppiServizioTest extends TestCase $this->assertSame('in_reparto', $territori[2]->fresh()->stato); } + public function test_il_sorvegliante_apre_l_anteprima_dal_numero_del_territorio(): void + { + ['sorvegliante' => $s, 'territori' => $territori] = $this->gruppo(); + $fuori = $this->territorio('88'); + foreach ([$territori[0], $fuori] as $t) { + Storage::disk('public')->put("territori-thumbnails/t{$t->numero}.png", 'PNG'); + $t->forceFill(['thumbnail_path' => "territori-thumbnails/t{$t->numero}.png"])->save(); + } + $dodici = $territori[0]->fresh(); + $this->flushSession(); + + // Il numero è un pulsante che porta miniatura e PDF del pacchetto. + $this->actingAs($s)->get(route('mio-gruppo'))->assertOk() + ->assertSee('data-miniatura="' . $dodici->miniaturaGruppoUrl() . '"', false) + ->assertSee('data-pdf="' . route('mio-gruppo.pdf', $dodici) . '"', false) + ->assertSee('Apri la piantina (PDF)'); + + // La miniatura si apre solo per il proprio pacchetto, e solo da qui. + $this->actingAs($s)->get($dodici->miniaturaGruppoUrl())->assertOk(); + $this->actingAs($s)->get(route('mio-gruppo.miniatura', $fuori))->assertForbidden(); + $this->actingAs($s)->get($dodici->miniaturaUrl())->assertForbidden(); + } + public function test_la_piantina_del_pacchetto_si_apre_solo_per_il_proprio_gruppo(): void { ['sorvegliante' => $s, 'territori' => $territori] = $this->gruppo();