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:
2026-10-12 12:08:34 +02:00
parent d433c9392d
commit 056d95cfcb
25 changed files with 3084 additions and 980 deletions

View File

@@ -0,0 +1,56 @@
<?php
use App\Models\TerritorioMappa;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Bozza e pubblicazione delle piantine.
*
* `geojson` diventa la bozza, su cui l'editor salva da solo a ogni modifica.
* Accanto si tiene quello che è stato pubblicato: il confine (per poter
* scartare la bozza), l'impronta del contenuto finito nel PDF (per sapere se
* da allora qualcosa è cambiato) e il momento della pubblicazione.
*
* Le piantine esistenti nascono "pubblicate" solo se il PDF in vigore del
* loro territorio è uno di quelli composti dalle piantine
* (territori-pdf/piantina-{territorio}-…): è l'unico caso in cui si sa che quel
* PDF viene da questo disegno. Tutte le altre partono come bozza.
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('territorio_mappe', function (Blueprint $table) {
$table->longText('geojson_pubblicato')->nullable()->after('geojson');
$table->string('impronta_pubblicata', 64)->nullable()->after('geojson_pubblicato');
$table->timestamp('pubblicata_at')->nullable()->after('impronta_pubblicata');
});
TerritorioMappa::with('territorio.zona')
->whereNotNull('territorio_id')
->whereNotNull('image_path')
->get()
->each(function (TerritorioMappa $mappa) {
$pdf = (string) $mappa->territorio?->pdf_path;
if (! str_starts_with($pdf, 'territori-pdf/piantina-' . $mappa->territorio_id . '-')) {
return;
}
$mappa->forceFill([
'geojson_pubblicato' => $mappa->geojson,
'impronta_pubblicata' => $mappa->impronta(),
'pubblicata_at' => $mappa->rendered_at ?? now(),
])->saveQuietly();
});
}
public function down(): void
{
Schema::table('territorio_mappe', function (Blueprint $table) {
$table->dropColumn(['geojson_pubblicato', 'impronta_pubblicata', 'pubblicata_at']);
});
}
};