Le piantine non dipendono piu' da un territorio esistente: territorio_id e' nullable, la chiave esterna e' nullOnDelete invece di cascadeOnDelete, e `nome` fa da etichetta finche' il collegamento manca. Si disegna guardando le strade e si decide dopo di chi e' il disegno; se il territorio non c'e', lo si crea dall'editor con numero, zona, tipologia e confini — zona e tipologia comprese, create sul posto quando mancano. Il cambio di cascata chiude anche una perdita di dati silenziosa: ogni import XML svuota `territori` in blocco e finora si portava via tutte le piantine. Il disegno passa su /piantine/editor, a schermo intero con telaio proprio: su una mappa larga un terzo di schermo un confine si sbaglia, e finisce stampato. /piantine resta la gestione — area, generazione, collegamenti, storico. Cartolina S-12 reimpaginata: pagina 1 il modulo con la Localita' (che ora e' il nome della zona, non un campo duplicato) e i confini scritti nello spazio centrale, con il corpo del testo che si adatta alla lunghezza; pagina 2 la piantina sola al 98%. Aggiunta l'anteprima, che compone il PDF al volo senza toccare pdf_path ne' lo storico. Immagine: JPEG invece di PNG (0,9 MB contro 5 per territorio), composta alle tessere dello zoom piu' alto e ridotta con Lanczos, nelle proporzioni della pagina cosi' riempie il foglio invece di lasciare fasce bianche. Il tratto del confine e' espresso in pixel dell'immagine finale: si vede il confine e si legge la via che ci passa sotto. L'attribuzione OSM si dimensiona con l'immagine, come la ODbL richiede. Rimossa la rigenerazione massiva: job, stato in cache e interfaccia. Sistema visivo ripassato su tutta l'applicazione: le scale gray e indigo di Tailwind sono ridefinite nei token, cosi' le 37 viste si riallineano insieme invece di essere riscritte una per una. Grigi caldi, un accento solo con quattro significati, carattere servito dall'applicazione e non da Google Fonts, via il gradiente della testata. Aggiunto il link "salta al contenuto", che mancava. Corretto un guasto d'ambiente che bloccava TUTTI gli upload in sviluppo: dalla 1.26 il fastcgi_params di nginx passa HTTP_HOST come $host, che perde la porta, e ogni URL firmato smetteva di validare — l'endpoint di upload di Livewire rispondeva 401. 294 test verdi. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
211 lines
13 KiB
PHP
211 lines
13 KiB
PHP
<div>
|
|
<header class="tm-pagina-testata">
|
|
<div>
|
|
<h1 class="tm-pagina-titolo">Piantine dei territori</h1>
|
|
<p class="tm-pagina-nota">
|
|
Il disegno dei confini si fa nell'editor a schermo intero. Qui si tiene l'area della
|
|
città, si generano immagini e PDF sul modulo S-12, e si ripescano le versioni sostituite.
|
|
</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>
|
|
|
|
{{-- ── Area della città ──────────────────────────────────────────────── --}}
|
|
<section class="tm-card tm-card-primaria {{ $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 su cui si disegnano le piantine: è il primo passo,
|
|
senza l'editor parte su una mappa senza contorni.
|
|
</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>
|
|
|
|
{{-- ── Piantine ──────────────────────────────────────────────────────── --}}
|
|
<section class="tm-card">
|
|
<div class="tm-card-testata">
|
|
<h2 class="tm-card-titolo">Piantine <span class="tm-conteggio">{{ $piantine->count() }}</span></h2>
|
|
<p class="tm-card-nota">
|
|
Una piantina può vivere scollegata: per il PDF sul modulo S-12 serve invece un territorio.
|
|
</p>
|
|
</div>
|
|
|
|
@if($piantine->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="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
|
|
<ul class="tm-elenco">
|
|
@foreach($piantine as $piantina)
|
|
<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>
|
|
@unless($piantina->collegata())
|
|
<span class="tm-tag">scollegata</span>
|
|
@endunless
|
|
@if($piantina->collegata() && ! $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 {{ $piantina->image_path ? 'tm-elenco-stato-ok' : '' }}">
|
|
{{ $piantina->image_path
|
|
? 'immagine del ' . $piantina->rendered_at?->format('d/m/Y H:i')
|
|
: 'immagine da generare' }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="tm-elenco-azioni">
|
|
<x-btn variante="secondary" tono="tenue" dimensione="sm"
|
|
wire:click="generaImmagine({{ $piantina->id }})"
|
|
wire:loading.attr="disabled" wire:target="generaImmagine({{ $piantina->id }})">
|
|
<span wire:loading.remove wire:target="generaImmagine({{ $piantina->id }})">Genera immagine</span>
|
|
<span wire:loading wire:target="generaImmagine({{ $piantina->id }})">Rendering…</span>
|
|
</x-btn>
|
|
|
|
@if($piantina->image_path)
|
|
<a href="{{ asset('storage/' . $piantina->image_path) }}" target="_blank" rel="noopener" class="tm-collegamento">Immagine</a>
|
|
@endif
|
|
|
|
@if($piantina->collegata())
|
|
@if($piantina->image_path)
|
|
<a href="{{ route('piantine.anteprima', $piantina) }}" target="_blank" rel="noopener" class="tm-collegamento">Anteprima PDF</a>
|
|
<x-btn variante="primary" tono="tenue" dimensione="sm"
|
|
wire:click="generaPdf({{ $piantina->id }})"
|
|
wire:loading.attr="disabled" wire:target="generaPdf({{ $piantina->id }})"
|
|
wire:confirm="Sostituire il PDF del territorio {{ $piantina->territorio->numero }}? Quello attuale finisce nello storico versioni.">
|
|
<span wire:loading.remove wire:target="generaPdf({{ $piantina->id }})">Genera PDF</span>
|
|
<span wire:loading wire:target="generaPdf({{ $piantina->id }})">Composizione…</span>
|
|
</x-btn>
|
|
@endif
|
|
<x-btn variante="ghost" dimensione="sm" wire:click="scollega({{ $piantina->id }})"
|
|
wire:confirm="Scollegare questa piantina dal territorio {{ $piantina->territorio->numero }}? Il PDF del territorio resta com'è.">
|
|
Scollega
|
|
</x-btn>
|
|
@else
|
|
<x-btn variante="secondary" dimensione="sm" wire:click="apriCollegamento({{ $piantina->id }})">
|
|
Collega a un territorio
|
|
</x-btn>
|
|
@endif
|
|
|
|
<x-btn variante="danger" tono="tenue" dimensione="sm"
|
|
wire:click="eliminaPiantina({{ $piantina->id }})"
|
|
wire:confirm="Eliminare {{ $piantina->etichetta() }}? Poligono e immagine vengono rimossi; il PDF del territorio resta com'è.">
|
|
Elimina
|
|
</x-btn>
|
|
</div>
|
|
|
|
@if($collegamentoAperto === $piantina->id)
|
|
<div class="tm-elenco-pannello">
|
|
<div class="tm-campo-riga">
|
|
<select wire:model="territorioDaCollegare" aria-label="Territorio a cui collegare la piantina">
|
|
<option value="">— scegli il territorio —</option>
|
|
@foreach($territori as $territorio)
|
|
<option value="{{ $territorio->id }}">{{ $territorio->numero }}</option>
|
|
@endforeach
|
|
</select>
|
|
<x-btn variante="primary" dimensione="sm" wire:click="collega({{ $piantina->id }})">Collega</x-btn>
|
|
<x-btn variante="ghost" dimensione="sm" wire:click="chiudiCollegamento">Annulla</x-btn>
|
|
</div>
|
|
<p class="tm-campo-aiuto">
|
|
Il territorio da creare non c'è? Si crea dall'editor, senza perdere il disegno.
|
|
</p>
|
|
</div>
|
|
@endif
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
@endif
|
|
</section>
|
|
|
|
{{-- ── Storico versioni PDF ──────────────────────────────────────────── --}}
|
|
<section class="tm-card">
|
|
<div class="tm-card-testata">
|
|
<h2 class="tm-card-titolo">Storico versioni PDF</h2>
|
|
<p class="tm-card-nota">I PDF sostituiti da una piantina. Il ripristino è sempre reversibile.</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="{{ asset('storage/' . $versione->pdf_path) }}" 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>
|
|
@endif
|
|
</section>
|
|
</div>
|