++ Modulo pubblico: nella riconsegna il territorio si sceglie con il completamento automatico

Elenco con solo numero e zona (territori attivi e quelli ancora fuori),
mostrato solo per la riconsegna; il server accetta solo un numero
dell'elenco. Privacy, README e HANDOFF aggiornati.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Francesco Picone
2026-10-21 17:49:08 +02:00
parent 2e1da51080
commit d198ad0b54
6 changed files with 96 additions and 15 deletions

View File

@@ -4,15 +4,20 @@ namespace App\Livewire\Richieste;
use App\Models\Richiesta;
use App\Models\Setting;
use App\Models\Territorio;
use App\Services\RichiesteServizio;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Livewire\Attributes\Locked;
use Livewire\Component;
/**
* Il modulo pubblico (/richiesta, senza login): un proclamatore chiede un
* territorio nuovo o annuncia una riconsegna. Non mostra nessun dato già
* presente: chi lo apre vede solo i campi da compilare.
* territorio nuovo o annuncia una riconsegna. L'unico dato già presente che
* mostra è l'elenco dei territori nella riconsegna, per il completamento
* automatico del numero: numero e zona, mai a chi è assegnato né se lo è.
* Nomi e altri dati non si vedono.
*
* Contro lo spam, tre difese che a chi compila davvero non costano niente:
* - un campo trappola ($sito) nascosto alle persone e riempito dai programmi;
@@ -67,12 +72,17 @@ class ModuloRichiesta extends Component
$this->validate([
'tipo' => 'required|in:' . Richiesta::NUOVO . ',' . Richiesta::RICONSEGNA,
'nome' => 'required|string|min:3|max:100',
'numero' => 'nullable|required_if:tipo,' . Richiesta::RICONSEGNA . '|string|max:20',
'numero' => ['nullable', 'required_if:tipo,' . Richiesta::RICONSEGNA, 'string', 'max:20',
function ($attributo, $valore, $errore) {
if ($this->tipo === Richiesta::RICONSEGNA && ! self::territori()->whereRaw('LOWER(numero) = ?', [Str::lower(trim((string) $valore))])->exists()) {
$errore("Scegli il territorio dall'elenco.");
}
}],
'note' => 'nullable|string|max:1000',
], [
'nome.required' => 'Scrivi nome e cognome.',
'nome.min' => 'Scrivi nome e cognome.',
'numero.required_if' => 'Scrivi il numero del territorio che riconsegni.',
'numero.required_if' => 'Scegli il territorio che riconsegni.',
]);
RateLimiter::hit($chiave, 3600);
@@ -88,10 +98,40 @@ class ModuloRichiesta extends Component
$this->apertoAlle = now()->timestamp;
}
/**
* I territori che si possono riconsegnare: quelli attivi e quelli ancora
* fuori anche se nel frattempo disattivati.
*/
private static function territori(): Builder
{
return Territorio::query()->where(fn ($q) => $q->where('attivo', true)
->orWhereHas('assegnazioni', fn ($a) => $a->whereNull('returned_at')));
}
/**
* Le proposte del campo con ricerca (<x-scelta>): solo numero e zona. Il
* valore è il numero, come lo salva la richiesta.
*/
private static function opzioniTerritori(): array
{
return self::territori()->with('zona')->get()
->sortBy('numero', SORT_NATURAL)
->map(fn (Territorio $t) => array_filter([
'valore' => (string) $t->numero,
'etichetta' => "N° {$t->numero}",
'dettaglio' => $t->zona?->nome,
'chiavi' => [(string) $t->numero, (string) $t->zona?->nome],
], fn ($v) => $v !== null))
->values()
->all();
}
public function render()
{
return view('livewire.richieste.modulo-richiesta', [
'congregazione' => Setting::instance()->congregazione_nome ?: 'TerManager2',
// Solo quando serve: chi chiede un territorio nuovo non vede l'elenco.
'opzioniTerritori' => $this->tipo === Richiesta::RICONSEGNA && ! $this->inviata ? self::opzioniTerritori() : [],
])->layout('components.layouts.guest', ['title' => 'Richiesta territorio']);
}
}