++ gruppi di servizio: sorvegliante con utenza limitata, pacchetto di territori, assegnazione ai membri
Il gruppo ha un sorvegliante (proclamatore con utenza: email e password temporanea, senza conferma dell'email) e dei membri; l'amministratore mette in blocco dei territori nel pacchetto del gruppo. Il pacchetto non e' un'assegnazione e non tocca le statistiche; quando il sorvegliante da' un territorio a un membro nasce un'assegnazione normale con gruppo_servizio_id. Al rientro il territorio torna in reparto. Sciogliere il gruppo libera pacchetto e membri e disattiva l'utenza. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,9 @@
|
||||
|
||||
namespace App\Livewire\Assegnazioni;
|
||||
|
||||
use App\Livewire\Concerns\InviaLink;
|
||||
use App\Livewire\Concerns\RicordaRitorno;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\On;
|
||||
use App\Models\Territorio;
|
||||
use App\Models\Proclamatore;
|
||||
use App\Models\Assegnazione;
|
||||
@@ -15,25 +14,7 @@ use Illuminate\Support\Facades\DB;
|
||||
class Assegna extends Component
|
||||
{
|
||||
use RicordaRitorno;
|
||||
|
||||
// ─── Invio del link ─────────────────────────────────────────
|
||||
//
|
||||
// Dopo l'assegnazione non si torna subito alla pagina: si sceglie il
|
||||
// canale (copia link o Condividi: il destinatario lo sceglie chi usa
|
||||
// l'app dai suoi contatti, i proclamatori non hanno numero né email),
|
||||
// si dice se il link è partito, e solo allora si va avanti. Se la
|
||||
// procedura si interrompe, l'assegnazione resta con link_da_inviare e lo
|
||||
// si segnala dove compare.
|
||||
|
||||
/** L'assegnazione di cui si sta mandando il link. */
|
||||
#[Locked]
|
||||
public ?int $invioId = null;
|
||||
|
||||
/** Il canale usato in questo invio (Assegnazione::CANALI_LINK). */
|
||||
#[Locked]
|
||||
public ?string $canaleUsato = null;
|
||||
|
||||
public string $erroreInvio = '';
|
||||
use InviaLink;
|
||||
|
||||
public ?int $territorio_id = null;
|
||||
public ?int $proclamatore_id = null;
|
||||
@@ -83,6 +64,12 @@ class Assegna extends Component
|
||||
return;
|
||||
}
|
||||
|
||||
// Nel pacchetto di un gruppo di servizio: lo assegna il sorvegliante.
|
||||
if ($territorio->gruppo_servizio_id) {
|
||||
$this->addError('territorio_id', "Il territorio {$territorio->numero} è nel pacchetto del gruppo {$territorio->gruppo?->nome}: lo assegna il sorvegliante, oppure va prima tolto dal pacchetto.");
|
||||
return;
|
||||
}
|
||||
|
||||
$proclamatore = Proclamatore::findOrFail($this->proclamatore_id);
|
||||
if (!$proclamatore->attivo) {
|
||||
$this->addError('proclamatore_id', "Il proclamatore {$proclamatore->nome_completo} è inattivo.");
|
||||
@@ -140,91 +127,16 @@ class Assegna extends Component
|
||||
$this->apriInvio($assegnazione->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apre l'invio del link: subito dopo l'assegnazione, o da un'assegnazione
|
||||
* rimasta in sospeso ("Invia link" nell'elenco, che è un componente a sé
|
||||
* e lo chiede con l'evento apri-invio).
|
||||
*/
|
||||
#[On('apri-invio')]
|
||||
public function apriInvio(int $assegnazioneId): void
|
||||
// ─── Invio del link (trait InviaLink) ──────────────────────
|
||||
|
||||
protected function puoInviare(Assegnazione $assegnazione): bool
|
||||
{
|
||||
$this->authorize('territori.assign');
|
||||
|
||||
$assegnazione = Assegnazione::aperte()->find($assegnazioneId);
|
||||
if (! $assegnazione) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->invioId = $assegnazione->id;
|
||||
$this->canaleUsato = null;
|
||||
$this->erroreInvio = '';
|
||||
return auth()->user()->can('territori.assign');
|
||||
}
|
||||
|
||||
/**
|
||||
* Copia link o Condividi: l'invio vero lo fa chi usa l'app (il
|
||||
* destinatario lo sceglie lui dai suoi contatti), qui si annota il canale.
|
||||
*/
|
||||
public function canale(string $canale): void
|
||||
protected function paginaDopoInvio(): string
|
||||
{
|
||||
$assegnazione = $this->assegnazioneInInvio();
|
||||
if (! $assegnazione || ! in_array($canale, Assegnazione::CANALI_ATTIVI, true) || ! $assegnazione->shortPdfUrl()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->canaleUsato = $canale;
|
||||
$this->erroreInvio = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* La risposta a "Hai inviato il link?": la si registra e solo adesso si
|
||||
* va avanti. "Sì" vuole un canale usato; "No" lascia il link da inviare.
|
||||
*/
|
||||
public function concludiInvio(bool $inviato)
|
||||
{
|
||||
$assegnazione = $this->assegnazioneInInvio();
|
||||
if (! $assegnazione) {
|
||||
$this->invioId = null;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($inviato && $this->canaleUsato === null) {
|
||||
$this->erroreInvio = 'Scegli prima come mandare il link.';
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$assegnazione->registraInvioLink($inviato, $this->canaleUsato);
|
||||
|
||||
// Nel log il canale e l'id del proclamatore, mai nome, numero o email.
|
||||
activity()->causedBy(auth()->user())
|
||||
->performedOn($assegnazione)
|
||||
->withProperties([
|
||||
'territorio' => $assegnazione->territorio?->numero,
|
||||
'proclamatore_id' => $assegnazione->proclamatore_id,
|
||||
'canale' => $inviato ? $this->canaleUsato : null,
|
||||
])
|
||||
->log($inviato ? 'link_inviato' : 'link_non_inviato');
|
||||
|
||||
$numero = $assegnazione->territorio?->numero;
|
||||
$nome = $assegnazione->proclamatore?->nome_completo;
|
||||
$this->reset(['invioId', 'canaleUsato', 'erroreInvio']);
|
||||
|
||||
session()->flash('success', $inviato
|
||||
? "Territorio {$numero} assegnato a {$nome}: link inviato."
|
||||
: "Territorio {$numero} assegnato a {$nome}. Il link è ancora da inviare: lo trovi segnalato nell'elenco.");
|
||||
return $this->redirect($this->destinazione(route('assegnazioni.assegna')), navigate: true);
|
||||
}
|
||||
|
||||
private function assegnazioneInInvio(): ?Assegnazione
|
||||
{
|
||||
if (! $this->invioId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->authorize('territori.assign');
|
||||
|
||||
return Assegnazione::with(['territorio', 'proclamatore'])->find($this->invioId);
|
||||
return $this->destinazione(route('assegnazioni.assegna'));
|
||||
}
|
||||
|
||||
public function render()
|
||||
@@ -233,30 +145,27 @@ class Assegna extends Component
|
||||
// i territori già assegnati, che non si possono scegliere: cercandone
|
||||
// uno si legge a chi è, invece di un "nessun risultato". La miniatura
|
||||
// serve all'anteprima, che si aggiorna nel browser.
|
||||
// Quelli nel pacchetto di un gruppo di servizio li assegna il
|
||||
// sorvegliante: qui si trovano ma non si scelgono.
|
||||
$territori = Territorio::attivi()
|
||||
->with(['zona', 'tipologia', 'assegnazioneCorrente.proclamatore'])
|
||||
->with(['zona', 'tipologia', 'assegnazioneCorrente.proclamatore', 'gruppo'])
|
||||
->get()
|
||||
->sortBy('numero', SORT_NATURAL);
|
||||
|
||||
$opzioniTerritori = $territori
|
||||
->map(fn (Territorio $t) => $t->opzioneScelta(
|
||||
$t->assegnazioneCorrente ? 'già assegnato a ' . ($t->assegnazioneCorrente->proclamatore?->nome_completo ?? '—') : null
|
||||
) + array_filter(['miniatura' => $t->miniaturaUrl()]))
|
||||
->map(fn (Territorio $t) => $t->opzioneScelta(match (true) {
|
||||
(bool) $t->assegnazioneCorrente => 'già assegnato a ' . ($t->assegnazioneCorrente->proclamatore?->nome_completo ?? '—'),
|
||||
(bool) $t->gruppo_servizio_id => 'nel pacchetto del gruppo ' . ($t->gruppo?->nome ?? ''),
|
||||
default => null,
|
||||
}) + array_filter(['miniatura' => $t->miniaturaUrl()]))
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$opzioniProclamatori = Proclamatore::opzioniScelta(Proclamatore::attivi()->get());
|
||||
|
||||
// L'elenco dei territori assegnati sta in ElencoAssegnati.
|
||||
// L'invio in corso; se nel frattempo l'assegnazione è rientrata o
|
||||
// sparita non c'è più niente da mandare.
|
||||
$invio = $this->invioId ? Assegnazione::aperte()->with(['territorio', 'proclamatore'])->find($this->invioId) : null;
|
||||
if ($this->invioId && ! $invio) {
|
||||
$this->reset(['invioId', 'canaleUsato', 'erroreInvio']);
|
||||
}
|
||||
|
||||
return view('livewire.assegnazioni.assegna', [
|
||||
'invio' => $invio,
|
||||
'invio' => $this->invioCorrente(),
|
||||
'opzioniTerritori' => $opzioniTerritori,
|
||||
'opzioniProclamatori' => $opzioniProclamatori,
|
||||
'anteprimaIniziale' => $this->territorio_id ? $territori->firstWhere('id', $this->territorio_id)?->miniaturaUrl() : null,
|
||||
|
||||
@@ -27,6 +27,7 @@ class Rientra extends Component
|
||||
|
||||
public function mount(Assegnazione $assegnazione)
|
||||
{
|
||||
$this->autorizza($assegnazione);
|
||||
$this->assegnazione = $assegnazione->load(['territorio', 'proclamatore']);
|
||||
|
||||
if ($assegnazione->returned_at) {
|
||||
@@ -69,8 +70,28 @@ class Rientra extends Component
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Chi ha territori.return registra qualsiasi rientro. Il sorvegliante di
|
||||
* gruppo (gruppo.gestisci) solo quelli del suo gruppo: assegnazioni nate
|
||||
* dal gruppo o territori del suo pacchetto.
|
||||
*/
|
||||
private function autorizza(Assegnazione $assegnazione): void
|
||||
{
|
||||
$utente = auth()->user();
|
||||
if ($utente->can('territori.return')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$gruppo = $utente->can('gruppo.gestisci') ? $utente->gruppoSorvegliato() : null;
|
||||
abort_unless(
|
||||
$gruppo && ($assegnazione->gruppo_servizio_id === $gruppo->id || $assegnazione->territorio?->gruppo_servizio_id === $gruppo->id),
|
||||
403
|
||||
);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->autorizza($this->assegnazione);
|
||||
$this->validate();
|
||||
|
||||
// mount() rejects an already-returned assignment, but the component
|
||||
@@ -112,7 +133,18 @@ class Rientra extends Component
|
||||
}
|
||||
|
||||
session()->flash('success', "Territorio {$territorio->numero} rientrato da {$proclamatore->nome_completo} dopo {$this->assegnazione->giorni} giorni.");
|
||||
return $this->redirect($this->destinazione(route('territori.show', $territorio)), navigate: true);
|
||||
return $this->redirect($this->destinazione($this->paginaPredefinita()), navigate: true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dove si torna senza una pagina di partenza: la scheda del territorio,
|
||||
* o "Il mio gruppo" per il sorvegliante, che la scheda non la vede.
|
||||
*/
|
||||
public function paginaPredefinita(): string
|
||||
{
|
||||
return auth()->user()->can('territori.assign')
|
||||
? route('territori.show', $this->assegnazione->territorio_id)
|
||||
: route('mio-gruppo');
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
||||
Reference in New Issue
Block a user