localita = $area->localita(); $this->operazioneId = $state->corrente(); } // ─── Area città (KML) ─────────────────────────────────────────────────── public function caricaKml(PiantineArea $area) { $this->validate( ['kmlFile' => 'required|file|max:10240'], [], ['kmlFile' => 'file KML'], ); $estensione = strtolower($this->kmlFile->getClientOriginalExtension()); if (! in_array($estensione, ['kml', 'xml'], true)) { $this->addError('kmlFile', 'Il file deve essere un .kml (o .xml in formato KML).'); return; } try { $quante = $area->salvaKml( file_get_contents($this->kmlFile->getRealPath()), $this->kmlFile->getClientOriginalName(), ); } catch (\Throwable $e) { $this->addError('kmlFile', $e->getMessage()); return; } $this->kmlFile = null; activity()->causedBy(auth()->user())->log('piantine_kml_caricato'); session()->flash('success', "Area della città aggiornata ({$quante} geometrie riconosciute nel KML)."); $this->aggiornaMappaJs(); } public function salvaLocalita(PiantineArea $area): void { $area->impostaLocalita($this->localita); session()->flash('success', 'Località aggiornata: comparirà sull\'intestazione dei prossimi PDF.'); } // ─── Editor dei poligoni ──────────────────────────────────────────────── #[On('poligono-disegnato')] public function poligonoDisegnato($geometry): void { if (! is_array($geometry) || ! in_array($geometry['type'] ?? null, ['Polygon', 'MultiPolygon'], true) || ! is_array($geometry['coordinates'] ?? null)) { return; } $this->geometriaInAttesa = json_encode($geometry); } #[On('territorio-selezionato-mappa')] public function territorioSelezionatoDaMappa($territorioId): void { $this->territorioSelezionato = is_numeric($territorioId) ? (int) $territorioId : null; $this->aggiornaMappaJs(); } public function updatedTerritorioSelezionato(): void { $this->territorioSelezionato = $this->territorioSelezionato ?: null; $this->aggiornaMappaJs(); } public function salvaPoligono() { if (! $this->territorioSelezionato) { session()->flash('error', 'Scegli prima a quale territorio associare il poligono.'); return; } if (! $this->geometriaInAttesa) { session()->flash('error', 'Disegna prima il poligono sulla mappa.'); return; } $territorio = Territorio::findOrFail($this->territorioSelezionato); TerritorioMappa::updateOrCreate( ['territorio_id' => $territorio->id], ['geojson' => $this->geometriaInAttesa], ); $this->geometriaInAttesa = null; activity()->causedBy(auth()->user())->performedOn($territorio)->log('piantina_poligono_salvato'); session()->flash('success', "Poligono salvato per il territorio {$territorio->numero}. Ora puoi generare l'immagine."); $this->aggiornaMappaJs(); } public function scartaPoligono(): void { $this->geometriaInAttesa = null; $this->aggiornaMappaJs(); } public function eliminaPoligono(int $mappaId) { $mappa = TerritorioMappa::with('territorio')->findOrFail($mappaId); $numero = $mappa->territorio?->numero ?? '?'; if ($mappa->image_path) { Storage::disk('public')->delete($mappa->image_path); } $mappa->delete(); session()->flash('success', "Poligono e immagine del territorio {$numero} eliminati (il PDF resta com'è)."); $this->aggiornaMappaJs(); } // ─── Generazione ──────────────────────────────────────────────────────── public function generaImmagine(int $mappaId, PiantinaRenderer $renderer) { $mappa = TerritorioMappa::with('territorio')->findOrFail($mappaId); try { $renderer->render($mappa); } catch (\Throwable $e) { session()->flash('error', $e->getMessage()); return; } session()->flash('success', "Immagine generata per il territorio {$mappa->territorio?->numero}."); } public function generaPdf(int $mappaId, PiantinaPdfService $pdfService) { $mappa = TerritorioMappa::with('territorio')->findOrFail($mappaId); $territorio = $mappa->territorio; if (! $territorio) { session()->flash('error', 'Il territorio associato non esiste più.'); return; } try { $pdfService->sostituisci($territorio, $mappa); } catch (\Throwable $e) { session()->flash('error', $e->getMessage()); return; } session()->flash('success', "PDF del territorio {$territorio->numero} sostituito. Il precedente è nello storico versioni."); } // ─── Rigenerazione massiva ────────────────────────────────────────────── public function rigeneraTutto(bool $anchePdf, PiantineOperationState $state) { if ($state->corrente() !== null) { session()->flash('error', 'C\'è già una rigenerazione in corso: attendi che finisca.'); return; } $totale = TerritorioMappa::count(); if ($totale === 0) { session()->flash('error', 'Nessun territorio ha ancora un poligono disegnato.'); return; } $id = (string) Str::uuid(); $state->initialize($id, $totale, $anchePdf); RigeneraPiantine::dispatch($id, $anchePdf); $this->operazioneId = $id; activity()->causedBy(auth()->user())->log($anchePdf ? 'piantine_rigenerazione_pdf' : 'piantine_rigenerazione'); } // ─── Storico versioni ─────────────────────────────────────────────────── public function ripristinaVersione(int $versioneId, PiantinaPdfService $pdfService) { $versione = TerritorioPdfVersione::with('territorio')->findOrFail($versioneId); $numero = $versione->territorio?->numero ?? '?'; try { $pdfService->ripristina($versione); } catch (\Throwable $e) { session()->flash('error', $e->getMessage()); return; } session()->flash('success', "Versione precedente ripristinata per il territorio {$numero}."); } public function eliminaVersione(int $versioneId) { $versione = TerritorioPdfVersione::findOrFail($versioneId); if (Storage::disk('public')->exists($versione->pdf_path)) { Storage::disk('public')->delete($versione->pdf_path); } $versione->delete(); session()->flash('success', 'Versione eliminata definitivamente.'); } // ─── Dati per la mappa e rendering ────────────────────────────────────── /** * Il payload che il JavaScript della mappa usa per disegnare: area della * città, poligoni salvati, selezione corrente. */ public function datiMappa(?PiantineArea $area = null): array { $area ??= app(PiantineArea::class); $poligoni = TerritorioMappa::with('territorio')->get() ->filter(fn ($mappa) => $mappa->territorio !== null && $mappa->geometria() !== null) ->map(fn ($mappa) => [ 'territorioId' => $mappa->territorio_id, 'numero' => $mappa->territorio->numero, 'geometry' => $mappa->geometria(), ]) ->values() ->all(); return [ 'area' => $area->area(), 'poligoni' => $poligoni, 'selezionato' => $this->territorioSelezionato, ]; } private function aggiornaMappaJs(): void { $this->dispatch('piantine-dati-aggiornati', dati: $this->datiMappa()); } public function render(PiantineArea $area, PiantineOperationState $state) { $mappe = TerritorioMappa::with('territorio')->get()->keyBy('territorio_id'); $territori = Territorio::attivi() ->orderByRaw('LENGTH(numero), numero') ->get(['id', 'numero', 'pdf_path']); $stato = $this->operazioneId ? $state->get($this->operazioneId) : null; return view('livewire.piantine.piantine-manager', [ 'config' => $area->config(), 'datiMappaIniziali' => $this->datiMappa($area), 'territori' => $territori, 'mappe' => $mappe, 'versioni' => TerritorioPdfVersione::with('territorio') ->orderByDesc('sostituito_il') ->limit(30) ->get(), 'stato' => $stato, 'title' => 'Piantine dei territori', ]); } }