exists()) { $this->apri($mappa); } } // ─── Selezione ────────────────────────────────────────────────────────── #[On('piantina-selezionata-mappa')] public function apri($mappaId): void { $mappa = TerritorioMappa::with('territorio')->find(is_numeric($mappaId) ? (int) $mappaId : null); if (! $mappa) { return; } $this->mappaId = $mappa->id; $this->nome = (string) $mappa->nome; $this->geometriaInAttesa = null; $this->pannelloAperto = true; $this->caricaScheda($mappa->territorio); $this->aggiornaMappaJs(inquadra: true); } public function nuova(): void { $this->reset([ 'mappaId', 'nome', 'geometriaInAttesa', 'territorioScelto', 'numero', 'zonaId', 'tipologiaId', 'confini', 'nuovaZona', 'nuovaTipologia', ]); $this->resetValidation(); $this->pannelloAperto = true; $this->aggiornaMappaJs(); } public function chiudiPannello(): void { $this->pannelloAperto = false; } /** Riempie i campi della scheda dal territorio collegato (o li svuota). */ private function caricaScheda(?Territorio $territorio): void { $this->territorioScelto = $territorio ? (string) $territorio->id : ''; $this->numero = (string) ($territorio->numero ?? ''); $this->zonaId = (string) ($territorio->zona_id ?? ''); $this->tipologiaId = (string) ($territorio->tipologia_id ?? ''); $this->confini = (string) ($territorio->confini ?? ''); } /** Cambiare territorio nella tendina ricarica la sua scheda. */ public function updatedTerritorioScelto(string $valore): void { $this->resetValidation(); if ($valore === 'nuovo') { $this->numero = ''; $this->zonaId = ''; $this->tipologiaId = ''; $this->confini = ''; return; } $this->caricaScheda($valore === '' ? null : Territorio::find($valore)); } // ─── Disegno ──────────────────────────────────────────────────────────── #[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); $this->pannelloAperto = true; } public function scartaDisegno(): void { $this->geometriaInAttesa = null; $this->aggiornaMappaJs(); } // ─── Zona e tipologia create sul posto ────────────────────────────────── public function creaZona() { $dati = $this->validate( ['nuovaZona' => ['required', 'string', 'max:100', Rule::unique('zone', 'nome')]], [ 'nuovaZona.required' => 'Scrivi il nome della zona.', 'nuovaZona.unique' => 'Questa zona esiste già: sceglila dall\'elenco.', ], ); $zona = Zona::create(['nome' => trim($dati['nuovaZona']), 'attivo' => true]); $this->zonaId = (string) $zona->id; $this->nuovaZona = ''; session()->flash('success', "Zona «{$zona->nome}» creata e selezionata."); } public function creaTipologia() { $dati = $this->validate( ['nuovaTipologia' => ['required', 'string', 'max:100', Rule::unique('tipologie', 'nome')]], [ 'nuovaTipologia.required' => 'Scrivi il nome della tipologia.', 'nuovaTipologia.unique' => 'Questa tipologia esiste già: sceglila dall\'elenco.', ], ); $tipologia = Tipologia::create(['nome' => trim($dati['nuovaTipologia']), 'attivo' => true]); $this->tipologiaId = (string) $tipologia->id; $this->nuovaTipologia = ''; session()->flash('success', "Tipologia «{$tipologia->nome}» creata e selezionata."); } // ─── Salvataggio ──────────────────────────────────────────────────────── public function salva() { $mappa = $this->mappaId ? TerritorioMappa::find($this->mappaId) : null; if (! $mappa && ! $this->geometriaInAttesa) { session()->flash('error', 'Disegna il poligono prima di salvare.'); return; } $territorio = $this->salvaScheda($mappa); // salvaScheda() torna false quando la validazione della scheda non // passa: in quel caso il messaggio è già a schermo e non si tocca nulla. if ($territorio === false) { return; } $valori = [ 'territorio_id' => $territorio?->id, 'nome' => trim($this->nome) !== '' ? trim($this->nome) : null, ]; if ($this->geometriaInAttesa) { $valori['geojson'] = $this->geometriaInAttesa; } if ($mappa) { $mappa->update($valori); } else { $mappa = TerritorioMappa::create($valori); $this->mappaId = $mappa->id; } $this->geometriaInAttesa = null; activity()->causedBy(auth()->user())->log('piantina_salvata'); session()->flash('success', $mappa->fresh()->load('territorio')->etichetta() . ' salvata.'); $this->aggiornaMappaJs(); } /** * Crea o aggiorna il territorio della scheda. * * @return Territorio|null|false Il territorio collegato, null se la * piantina resta scollegata, false se la * scheda non è valida. */ private function salvaScheda(?TerritorioMappa $mappa): Territorio|null|false { if ($this->territorioScelto === '') { return null; } $esistente = $this->territorioScelto === 'nuovo' ? null : Territorio::find($this->territorioScelto); if ($this->territorioScelto !== 'nuovo' && ! $esistente) { session()->flash('error', 'Il territorio scelto non esiste più.'); return false; } try { $this->validate( [ 'numero' => [ 'required', 'string', 'max:20', Rule::unique('territori', 'numero')->ignore($esistente?->id), ], 'zonaId' => ['nullable', Rule::exists('zone', 'id')], 'tipologiaId' => ['nullable', Rule::exists('tipologie', 'id')], 'confini' => ['nullable', 'string', 'max:5000'], ], [ 'numero.required' => 'Il territorio ha bisogno di un numero.', 'numero.unique' => 'Esiste già un territorio con questo numero.', ], ['numero' => 'numero'], ); } catch (\Illuminate\Validation\ValidationException $e) { $this->pannelloAperto = true; throw $e; } // Un territorio ha una piantina sola. if ($esistente) { $occupato = TerritorioMappa::where('territorio_id', $esistente->id) ->when($mappa, fn ($q) => $q->where('id', '!=', $mappa->id)) ->exists(); if ($occupato) { session()->flash('error', "Il territorio {$esistente->numero} ha già una piantina: scollegala prima di assegnargli questa."); return false; } } $valori = [ 'numero' => trim($this->numero), 'zona_id' => $this->zonaId !== '' ? (int) $this->zonaId : null, 'tipologia_id' => $this->tipologiaId !== '' ? (int) $this->tipologiaId : null, 'confini' => trim($this->confini) !== '' ? trim($this->confini) : null, ]; if ($esistente) { $esistente->update($valori); return $esistente; } $territorio = Territorio::create($valori + ['attivo' => true]); $this->territorioScelto = (string) $territorio->id; activity()->causedBy(auth()->user())->performedOn($territorio)->log('territorio_creato_da_piantine'); return $territorio; } // ─── Immagine ed eliminazione ─────────────────────────────────────────── public function generaImmagine(PiantinaRenderer $renderer) { $mappa = $this->mappaId ? TerritorioMappa::with('territorio')->find($this->mappaId) : null; if (! $mappa) { session()->flash('error', 'Salva la piantina prima di generarne l\'immagine.'); return; } try { $renderer->render($mappa); } catch (\Throwable $e) { session()->flash('error', $e->getMessage()); return; } session()->flash('success', "Immagine generata per {$mappa->etichetta()}."); } public function elimina() { $mappa = $this->mappaId ? TerritorioMappa::with('territorio')->find($this->mappaId) : null; if (! $mappa) { return; } $etichetta = $mappa->etichetta(); if ($mappa->image_path) { Storage::disk('public')->delete($mappa->image_path); } $mappa->delete(); $this->nuova(); session()->flash('success', "{$etichetta} eliminata."); } // ─── Dati per la mappa ────────────────────────────────────────────────── public function datiMappa(?PiantineArea $area = null): array { $area ??= app(PiantineArea::class); $piantine = TerritorioMappa::with('territorio')->get() ->filter(fn ($m) => $m->geometria() !== null) ->map(fn ($m) => [ 'mappaId' => $m->id, 'etichetta' => $m->etichetta(), 'collegata' => $m->collegata(), 'geometry' => $m->geometria(), ]) ->values() ->all(); return [ 'area' => $area->area(), 'piantine' => $piantine, 'selezionata' => $this->mappaId, ]; } private function aggiornaMappaJs(bool $inquadra = false): void { $this->dispatch('piantine-dati-aggiornati', dati: $this->datiMappa(), inquadra: $inquadra); } public function render(PiantineArea $area) { $mappa = $this->mappaId ? TerritorioMappa::with('territorio')->find($this->mappaId) : null; return view('livewire.piantine.piantine-editor', [ 'mappa' => $mappa, 'areaCaricata' => $area->area() !== null, 'datiMappaIniziali' => $this->datiMappa($area), 'territori' => Territorio::attivi() ->orderByRaw('LENGTH(numero), numero') ->get(['id', 'numero']), 'zone' => Zona::orderBy('nome')->get(['id', 'nome']), 'tipologie' => Tipologia::orderBy('nome')->get(['id', 'nome']), 'title' => 'Editor delle piantine', ]); } }