Editor piantine: bozza automatica e pubblicazione con anteprima
- Il disegno è bozza e si salva da solo; i dati del territorio si modificano dall'editor e valgono subito nel gestionale. - Un solo "Pubblica" (immagine + PDF), uguale in editor e gestione: anteprima preparata in coda su disco privato, conferma obbligatoria, rifiutata se nel frattempo è cambiato qualcosa di stampato. - Stati Bozza / Pubblicata / Modifiche da pubblicare, calcolati sull'impronta di confine e dati stampati e sul PDF in uso. - Gestione riscritta: stato, ricerca, filtri, storico paginato. - Editor ridisegnato a tre zone, utilizzabile anche su tablet e telefono. - Avvisi via evento (trait Avvisa + <x-avvisi>). - .gitattributes: script shell sempre LF, altrimenti il container locale su Windows non parte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,21 @@
|
||||
@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">
|
||||
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.
|
||||
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')">
|
||||
@@ -13,8 +24,101 @@
|
||||
</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="{{ asset('storage/' . $piantina->territorio->pdf_path) }}" 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 tm-card-primaria {{ $config['kml_nome'] ? '' : 'tm-card-manca' }}">
|
||||
<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>
|
||||
@@ -29,8 +133,7 @@
|
||||
@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.
|
||||
Il perimetro di riferimento che l'editor mostra sotto le piantine.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,110 +162,14 @@
|
||||
</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>
|
||||
<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 o da un ripristino. Ripristinare una versione
|
||||
rimette in uso quel PDF: la piantina del territorio torna "da pubblicare".
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@if($versioni->isEmpty())
|
||||
@@ -205,6 +212,10 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">{{ $versioni->links() }}</div>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
<livewire:piantine.pubblicazione />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user