Blocco 2 - Esiti delle azioni Livewire come avvisi (trait Avvisa) invece di flash che comparivano alla pagina dopo. - PDF, miniature e versioni dei territori non più serviti da nginx su /storage: TerritorioFileController con login, permessi e cache privata. - Ripristino tutto o niente: verifica dell'archivio, una transazione con import, piantine, blocco account e scambio dei file annullabile. - Backup formato 2 con piantine, storico PDF e area KML. - Posto unico preso in un passo (Cache::lock) e rispettato anche da import XML e PDF. - Import PDF da cartella: abbinamento corretto (1-A, 12 B, ambigui), caricamento a lotti, file non PDF ignorati, PDF sostituito nello storico. - L'import XML elimina i file dei territori che non usa più; comando territori:file-orfani per quelli accumulati prima. Blocco 3 - Password attuale richiesta al cambio volontario; AuthenticateSession chiude le sessioni aperte altrove. - Niente nomi dei proclamatori in chiaro nel registro attività. - Export XML senza remember_token; chi riceve l'hash dal file deve cambiare password. - Campagne: ultimo giorno incluso, percentuale sui territori distinti. - Coda: retry_after 960 s, stop_grace_period 16m, Redis volatile-lru, impostazioni rilette a ogni job. - Azzeramento che cancella anche piantine, area KML e anteprime. - Pulsanti profilo e menu che non si nascondevano; icona del sito. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
223 lines
13 KiB
PHP
223 lines
13 KiB
PHP
@php
|
|
use App\Models\TerritorioMappa;
|
|
|
|
$tagStato = [
|
|
TerritorioMappa::STATO_BOZZA => 'tm-tag-attesa',
|
|
TerritorioMappa::STATO_MODIFICATA => 'tm-tag-attesa',
|
|
TerritorioMappa::STATO_PUBBLICATA => 'tm-tag-ok',
|
|
];
|
|
@endphp
|
|
|
|
<div>
|
|
<header class="tm-pagina-testata">
|
|
<div>
|
|
<h1 class="tm-pagina-titolo">Piantine dei territori</h1>
|
|
<p class="tm-pagina-nota">
|
|
A che punto è ogni piantina. Il disegno e i dati del territorio si modificano nell'editor,
|
|
dove le modifiche si salvano da sole come bozza; il PDF del territorio cambia solo quando
|
|
si pubblica, dopo averne visto l'anteprima.
|
|
</p>
|
|
</div>
|
|
<x-btn variante="primary" :href="route('piantine.editor')">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"/></svg>
|
|
Apri l'editor
|
|
</x-btn>
|
|
</header>
|
|
|
|
{{-- ── Piantine ──────────────────────────────────────────────────────── --}}
|
|
<section class="tm-card">
|
|
<div class="tm-card-testata">
|
|
<h2 class="tm-card-titolo">Piantine <span class="tm-conteggio">{{ $totale }}</span></h2>
|
|
</div>
|
|
|
|
@if($totale === 0)
|
|
<div class="tm-vuoto">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"/></svg>
|
|
<p>Nessuna piantina disegnata.</p>
|
|
<x-btn variante="primary" dimensione="sm" :href="route('piantine.editor')">Disegna la prima</x-btn>
|
|
</div>
|
|
@else
|
|
<div class="tm-filtri">
|
|
<input type="search" wire:model.live.debounce.300ms="cerca" class="tm-filtri-cerca"
|
|
placeholder="Cerca numero, nome o zona…" aria-label="Cerca una piantina">
|
|
<div class="tm-filtri-schede" role="group" aria-label="Filtra per stato">
|
|
@foreach(\App\Livewire\Piantine\PiantineManager::FILTRI as $chiave => $etichetta)
|
|
<button type="button" wire:click="$set('filtro', '{{ $chiave }}')"
|
|
class="tm-filtri-scheda {{ $filtro === $chiave ? 'tm-filtri-scheda-attiva' : '' }}"
|
|
aria-pressed="{{ $filtro === $chiave ? 'true' : 'false' }}">
|
|
{{ $etichetta }} <span class="tm-conteggio">{{ $conteggi[$chiave] }}</span>
|
|
</button>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
@if($elenco->isEmpty())
|
|
<p class="tm-campo-aiuto">Nessuna piantina corrisponde alla ricerca.</p>
|
|
@else
|
|
<ul class="tm-elenco">
|
|
@foreach($elenco as $piantina)
|
|
@php
|
|
$stato = $piantina->stato();
|
|
$motivo = $piantina->motivoNonPubblicabile();
|
|
@endphp
|
|
<li class="tm-elenco-riga" wire:key="piantina-{{ $piantina->id }}">
|
|
<div class="tm-elenco-intestazione">
|
|
<a href="{{ route('piantine.editor', $piantina->id) }}" class="tm-elenco-nome">
|
|
{{ $piantina->etichetta() }}
|
|
</a>
|
|
<span class="tm-tag {{ $tagStato[$stato] }}">{{ $piantina->statoEtichetta() }}</span>
|
|
@if($piantina->territorioNelCestino())
|
|
<span class="tm-tag tm-tag-attesa">Territorio nel cestino</span>
|
|
@elseif(! $piantina->collegata())
|
|
<span class="tm-tag">Scollegata</span>
|
|
@elseif(! $piantina->territorio->zona_id)
|
|
<span class="tm-tag tm-tag-attesa" title="La zona viene stampata come «Località»: senza, l'intestazione della cartolina resta vuota">Zona mancante</span>
|
|
@endif
|
|
<span class="tm-elenco-stato">
|
|
@if($piantina->territorio?->zona)
|
|
{{ $piantina->territorio->zona->nome }} ·
|
|
@endif
|
|
{{ $piantina->pubblicata_at
|
|
? 'pubblicata il ' . $piantina->pubblicata_at->format('d/m/Y H:i')
|
|
: 'mai pubblicata' }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="tm-elenco-azioni">
|
|
<x-btn variante="secondary" dimensione="sm" :href="route('piantine.editor', $piantina->id)">
|
|
Apri
|
|
</x-btn>
|
|
|
|
@if($stato !== TerritorioMappa::STATO_PUBBLICATA)
|
|
<x-btn variante="primary" tono="tenue" dimensione="sm"
|
|
wire:click="pubblica({{ $piantina->id }})"
|
|
:disabled="$motivo !== null" :title="$motivo">
|
|
Pubblica
|
|
</x-btn>
|
|
@endif
|
|
|
|
@if($piantina->collegata() && $piantina->territorio->pdf_path)
|
|
<a href="{{ $piantina->territorio->pdfUrl() }}" target="_blank" rel="noopener" class="tm-collegamento">PDF in uso</a>
|
|
@endif
|
|
|
|
<x-btn variante="danger" tono="tenue" dimensione="sm"
|
|
wire:click="elimina({{ $piantina->id }})"
|
|
wire:confirm="Eliminare {{ $piantina->etichetta() }}? Il disegno viene rimosso; il territorio e il suo PDF restano com'erano.">
|
|
Elimina
|
|
</x-btn>
|
|
</div>
|
|
|
|
@if($motivo && $stato !== TerritorioMappa::STATO_PUBBLICATA)
|
|
<p class="tm-campo-aiuto">{{ $motivo }}</p>
|
|
@endif
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
@endif
|
|
@endif
|
|
</section>
|
|
|
|
{{-- ── Area della città ──────────────────────────────────────────────── --}}
|
|
<section class="tm-card {{ $config['kml_nome'] ? '' : 'tm-card-manca' }}">
|
|
<div class="tm-card-testata tm-card-testata-icona">
|
|
<span class="tm-card-icona" aria-hidden="true">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.7" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"/></svg>
|
|
</span>
|
|
<div>
|
|
<h2 class="tm-card-titolo">
|
|
Area della città
|
|
@if($config['kml_nome'])
|
|
<span class="tm-tag tm-tag-ok">caricata</span>
|
|
@else
|
|
<span class="tm-tag tm-tag-attesa">da caricare</span>
|
|
@endif
|
|
</h2>
|
|
<p class="tm-card-nota">
|
|
Il perimetro di riferimento che l'editor mostra sotto le piantine.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<form wire:submit="caricaKml" class="tm-campo">
|
|
<label for="kmlFile">File .kml dell'area</label>
|
|
<div class="tm-campo-riga">
|
|
<input type="file" id="kmlFile" wire:model="kmlFile" accept=".kml,.xml" class="tm-file">
|
|
<x-btn variante="secondary" type="submit" wire:loading.attr="disabled" wire:target="kmlFile,caricaKml">
|
|
Carica
|
|
</x-btn>
|
|
</div>
|
|
<p wire:loading wire:target="kmlFile" class="tm-campo-aiuto">Caricamento del file…</p>
|
|
@error('kmlFile') <p class="tm-campo-errore">{{ $message }}</p> @enderror
|
|
|
|
@if($config['kml_nome'])
|
|
<p class="tm-campo-aiuto">
|
|
Area attiva: <strong>{{ $config['kml_nome'] }}</strong>, aggiornata il {{ $config['aggiornato_il'] }}.
|
|
Caricare un nuovo KML la sostituisce; le piantine già disegnate restano.
|
|
</p>
|
|
@else
|
|
<p class="tm-campo-aiuto tm-campo-aiuto-attesa">
|
|
Nessuna area caricata: l'editor parte senza perimetro di riferimento.
|
|
</p>
|
|
@endif
|
|
</form>
|
|
</section>
|
|
|
|
{{-- ── Storico versioni PDF ──────────────────────────────────────────── --}}
|
|
<section class="tm-card">
|
|
<div class="tm-card-testata">
|
|
<h2 class="tm-card-titolo">Storico versioni PDF <span class="tm-conteggio">{{ $versioni->total() }}</span></h2>
|
|
<p class="tm-card-nota">
|
|
I PDF dei territori sostituiti: da una pubblicazione, da un caricamento (dalla scheda o
|
|
dall'import da cartella) o da un ripristino. Ripristinare una versione
|
|
rimette in uso quel PDF: la piantina del territorio torna "da pubblicare".
|
|
</p>
|
|
</div>
|
|
|
|
@if($versioni->isEmpty())
|
|
<div class="tm-vuoto">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
|
<p>Nessun PDF è ancora stato sostituito.</p>
|
|
</div>
|
|
@else
|
|
<div class="tm-tabella-scorre">
|
|
<table class="tm-tabella table-cards">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Territorio</th>
|
|
<th scope="col">Sostituito il</th>
|
|
<th scope="col">Origine</th>
|
|
<th scope="col"><span class="tm-solo-lettori">Azioni</span></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($versioni as $versione)
|
|
<tr wire:key="versione-{{ $versione->id }}">
|
|
<td data-label="Territorio" class="tm-tabella-forte">{{ $versione->territorio?->numero ?? '—' }}</td>
|
|
<td data-label="Sostituito il">{{ $versione->sostituito_il?->format('d/m/Y H:i') }}</td>
|
|
<td data-label="Origine">{{ $versione->origine }}</td>
|
|
<td class="cell-actions">
|
|
<a href="{{ route('territori.file.versione', $versione) }}" target="_blank" rel="noopener" class="tm-collegamento">Apri</a>
|
|
<x-btn variante="secondary" tono="tenue" dimensione="sm"
|
|
wire:click="ripristinaVersione({{ $versione->id }})"
|
|
wire:confirm="Ripristinare questa versione per il territorio {{ $versione->territorio?->numero }}? Il PDF attuale finisce a sua volta nello storico.">
|
|
Ripristina
|
|
</x-btn>
|
|
<x-btn variante="danger" tono="tenue" dimensione="sm"
|
|
wire:click="eliminaVersione({{ $versione->id }})"
|
|
wire:confirm="Eliminare definitivamente questa versione archiviata?">
|
|
Elimina
|
|
</x-btn>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="mt-4">{{ $versioni->links() }}</div>
|
|
@endif
|
|
</section>
|
|
|
|
<livewire:piantine.pubblicazione />
|
|
</div>
|