++ gruppi: sorvegliante non piu' attivo scioglie il gruppo; dettagli del gruppo nella scheda e nel profilo
- Proclamatore::booted: reso non attivo, spostato nel cestino o anonimizzato, il sorvegliante scioglie il gruppo (territori non dati in reparto, membri liberi, utenza disattivata); il motivo va nel registro. L'elenco chiede conferma, la modifica avvisa sotto "Attivo". Un membro nel cestino esce dal gruppo. - Scheda del proclamatore: gruppo guidato con membri, pacchetto, da dare, assegnazioni dal gruppo, utenza e ultimo accesso; oppure gruppo di cui e' membro; gruppi sciolti guidati in passato. - Profilo del sorvegliante: il suo gruppo con gli stessi numeri. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
27
HANDOFF.md
27
HANDOFF.md
@@ -4,7 +4,7 @@ Stato del progetto per chi ci mette mano dopo. Per installazione, deploy e uso
|
|||||||
quotidiano vedi [README.md](README.md); qui c'è il contesto che il README non
|
quotidiano vedi [README.md](README.md); qui c'è il contesto che il README non
|
||||||
può dare: perché le cose sono come sono, e cosa resta aperto.
|
può dare: perché le cose sono come sono, e cosa resta aperto.
|
||||||
|
|
||||||
Ultimo aggiornamento: **19 ottobre 2026** (gruppi di servizio, solo su dev) — vedi [Lavori in corso](#lavori-in-corso--12-ottobre-2026-riprendere-da-qui).
|
Ultimo aggiornamento: **20 ottobre 2026** (gruppi di servizio, solo su dev) — vedi [Lavori in corso](#lavori-in-corso--12-ottobre-2026-riprendere-da-qui).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -161,11 +161,26 @@ Casi aggiunti di mio, da far confermare all'utente:
|
|||||||
- Un ex sorvegliante può guidare un gruppo nuovo: `crea()` **riattiva** la
|
- Un ex sorvegliante può guidare un gruppo nuovo: `crea()` **riattiva** la
|
||||||
sua utenza disattivata (un proclamatore, un'utenza: `users.proclamatore_id`
|
sua utenza disattivata (un proclamatore, un'utenza: `users.proclamatore_id`
|
||||||
è unico), con email e password temporanea nuove.
|
è unico), con email e password temporanea nuove.
|
||||||
- Il sorvegliante di un gruppo **attivo** non si sposta nel cestino né si
|
- `Proclamatore::anonimizza()` anonimizza anche l'utenza dell'ex sorvegliante
|
||||||
anonimizza (`ProclamatoreIndex`): prima si scioglie il gruppo. Un membro
|
(nome, email, password, ruoli).
|
||||||
spostato nel cestino lascia il gruppo. `Proclamatore::anonimizza()`
|
|
||||||
anonimizza anche l'utenza dell'ex sorvegliante (nome, email, password,
|
Decisioni successive dell'utente (20 ottobre 2026):
|
||||||
ruoli).
|
- **Sorvegliante non più attivo ⇒ gruppo sciolto** e territori non dati
|
||||||
|
rilasciati, come "Togli il ruolo". La regola sta nel modello
|
||||||
|
(`Proclamatore::booted`, eventi `updated` su `attivo` e `deleted`), così
|
||||||
|
vale dall'elenco, dalla modifica, dal cestino e dall'anonimizzazione; il
|
||||||
|
motivo finisce nel registro (`gruppo_sciolto`, proprietà `motivo`).
|
||||||
|
L'elenco chiede conferma prima di disattivare o cestinare un sorvegliante;
|
||||||
|
la modifica lo avvisa sotto la spunta "Attivo". Il cestino e
|
||||||
|
l'anonimizzazione erano prima bloccati per il sorvegliante: li ho allineati
|
||||||
|
alla stessa regola. Un membro non attivo resta membro (non riceve
|
||||||
|
territori); nel cestino esce dal gruppo.
|
||||||
|
- **Dettagli nel profilo**: la scheda del proclamatore (`ProclamatoreShow`)
|
||||||
|
mostra il gruppo che guida con i numeri (`GruppoServizio::riepilogo()`),
|
||||||
|
l'utenza (email solo all'amministratore, password temporanea o no, ultimo
|
||||||
|
accesso dal registro), oppure il gruppo di cui è membro, e i gruppi sciolti
|
||||||
|
che ha guidato. Il *Profilo* del sorvegliante mostra il suo gruppo con gli
|
||||||
|
stessi numeri e "Torna al mio gruppo".
|
||||||
- L'utenza del sorvegliante porta nome ed email del proclamatore: nel registro
|
- L'utenza del sorvegliante porta nome ed email del proclamatore: nel registro
|
||||||
delle attività `User::tapActivity()` li sostituisce con "proclamatore #id",
|
delle attività `User::tapActivity()` li sostituisce con "proclamatore #id",
|
||||||
come vale per tutti i proclamatori. Restano invece in chiaro nella tabella
|
come vale per tutti i proclamatori. Restano invece in chiaro nella tabella
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -759,9 +759,18 @@ docker compose --profile dev up -d --build
|
|||||||
disattivata (anche una sessione aperta si chiude) e il gruppo resta nello
|
disattivata (anche una sessione aperta si chiude) e il gruppo resta nello
|
||||||
storico. I territori già dati restano ai membri. Per ripartire si crea un
|
storico. I territori già dati restano ai membri. Per ripartire si crea un
|
||||||
gruppo nuovo; se il sorvegliante è lo stesso, si riattiva la sua utenza
|
gruppo nuovo; se il sorvegliante è lo stesso, si riattiva la sua utenza
|
||||||
- Il sorvegliante di un gruppo attivo non si sposta nel cestino né si
|
- **Un sorvegliante che non è più attivo scioglie il gruppo**, come togliergli
|
||||||
anonimizza: prima si scioglie il gruppo. Anonimizzando un ex sorvegliante
|
il ruolo: succede se lo si rende non attivo (dall'elenco o dalla modifica),
|
||||||
si anonimizza anche la sua utenza
|
se lo si sposta nel cestino o lo si anonimizza. Prima di disattivarlo o
|
||||||
|
cestinarlo dall'elenco si chiede conferma; riattivarlo non ricostruisce il
|
||||||
|
gruppo. Un membro non attivo resta nel gruppo ma non riceve territori; nel
|
||||||
|
cestino esce dal gruppo. Anonimizzando un ex sorvegliante si anonimizza
|
||||||
|
anche la sua utenza
|
||||||
|
- **Dettagli del gruppo**: la scheda del proclamatore dice se è sorvegliante
|
||||||
|
(gruppo, membri, territori nel pacchetto e da dare, assegnazioni nate dal
|
||||||
|
gruppo, stato dell'utenza e ultimo accesso), membro, o sorvegliante di
|
||||||
|
gruppi ormai sciolti; il sorvegliante ritrova gli stessi numeri nel suo
|
||||||
|
*Profilo*
|
||||||
|
|
||||||
### Campagne
|
### Campagne
|
||||||
- CRUD con date di inizio e fine
|
- CRUD con date di inizio e fine
|
||||||
|
|||||||
@@ -38,13 +38,17 @@ class ProclamatoreEdit extends Component
|
|||||||
|
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
|
||||||
|
// Un sorvegliante reso non attivo scioglie il gruppo (Proclamatore::booted).
|
||||||
|
$gruppo = ! $this->attivo ? $this->proclamatore->gruppoSorvegliato() : null;
|
||||||
|
|
||||||
$this->proclamatore->update([
|
$this->proclamatore->update([
|
||||||
'nome' => $this->nome,
|
'nome' => $this->nome,
|
||||||
'cognome' => $this->cognome,
|
'cognome' => $this->cognome,
|
||||||
'attivo' => $this->attivo,
|
'attivo' => $this->attivo,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
session()->flash('success', "Proclamatore {$this->proclamatore->nome_completo} aggiornato.");
|
$avviso = "Proclamatore {$this->proclamatore->nome_completo} aggiornato." . ($gruppo ? ' ' . ProclamatoreIndex::gruppoSciolto($gruppo) : '');
|
||||||
|
session()->flash('success', $avviso);
|
||||||
return $this->redirect($this->destinazione(route('proclamatori.index')), navigate: true);
|
return $this->redirect($this->destinazione(route('proclamatori.index')), navigate: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +57,7 @@ class ProclamatoreEdit extends Component
|
|||||||
return view('livewire.proclamatori.proclamatore-form', [
|
return view('livewire.proclamatori.proclamatore-form', [
|
||||||
'titolo' => "Modifica {$this->proclamatore->nome_completo}",
|
'titolo' => "Modifica {$this->proclamatore->nome_completo}",
|
||||||
'btnLabel' => 'Salva Modifiche',
|
'btnLabel' => 'Salva Modifiche',
|
||||||
|
'gruppoSorvegliato' => $this->proclamatore->gruppoSorvegliato(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,13 @@ class ProclamatoreIndex extends Component
|
|||||||
$this->authorize('settings.manage');
|
$this->authorize('settings.manage');
|
||||||
|
|
||||||
$proclamatore = Proclamatore::findOrFail($id);
|
$proclamatore = Proclamatore::findOrFail($id);
|
||||||
|
$gruppo = $proclamatore->attivo ? $proclamatore->gruppoSorvegliato() : null;
|
||||||
|
// Un sorvegliante reso non attivo scioglie il gruppo (Proclamatore::booted).
|
||||||
$proclamatore->update(['attivo' => !$proclamatore->attivo]);
|
$proclamatore->update(['attivo' => !$proclamatore->attivo]);
|
||||||
|
|
||||||
|
if ($gruppo) {
|
||||||
|
$this->avvisa("{$proclamatore->nome_completo} non è più attivo. " . self::gruppoSciolto($gruppo));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteProclamatore(int $id)
|
public function deleteProclamatore(int $id)
|
||||||
@@ -71,14 +77,12 @@ class ProclamatoreIndex extends Component
|
|||||||
$this->avvisaErrore("Il proclamatore {$proclamatore->nome_completo} ha assegnazioni aperte. Rientra prima i territori.");
|
$this->avvisaErrore("Il proclamatore {$proclamatore->nome_completo} ha assegnazioni aperte. Rientra prima i territori.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($this->sorveglianteDiUnGruppo($proclamatore, 'spostarlo nel cestino')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nel cestino non resta membro di un gruppo.
|
// Nel cestino lascia il gruppo; se ne era il sorvegliante, il gruppo
|
||||||
$proclamatore->update(['gruppo_servizio_id' => null]);
|
// si scioglie (Proclamatore::booted).
|
||||||
|
$gruppo = $proclamatore->gruppoSorvegliato();
|
||||||
$proclamatore->delete();
|
$proclamatore->delete();
|
||||||
$this->avvisa("Proclamatore {$proclamatore->nome_completo} spostato nel cestino.");
|
$this->avvisa("Proclamatore {$proclamatore->nome_completo} spostato nel cestino." . ($gruppo ? ' ' . self::gruppoSciolto($gruppo) : ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function anonimizza(int $id)
|
public function anonimizza(int $id)
|
||||||
@@ -86,25 +90,15 @@ class ProclamatoreIndex extends Component
|
|||||||
$this->authorize('settings.manage');
|
$this->authorize('settings.manage');
|
||||||
|
|
||||||
$proclamatore = Proclamatore::findOrFail($id);
|
$proclamatore = Proclamatore::findOrFail($id);
|
||||||
if ($this->sorveglianteDiUnGruppo($proclamatore, 'anonimizzarlo')) {
|
$gruppo = $proclamatore->gruppoSorvegliato();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$proclamatore->anonimizza();
|
$proclamatore->anonimizza();
|
||||||
$this->avvisa("Dati personali del proclamatore anonimizzati (GDPR).");
|
$this->avvisa("Dati personali del proclamatore anonimizzati (GDPR)." . ($gruppo ? ' ' . self::gruppoSciolto($gruppo) : ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Il sorvegliante di un gruppo attivo si toglie sciogliendo il gruppo, non da qui. */
|
/** L'avviso quando un sorvegliante esce di scena: anche per ProclamatoreEdit. */
|
||||||
private function sorveglianteDiUnGruppo(Proclamatore $proclamatore, string $azione): bool
|
public static function gruppoSciolto(GruppoServizio $gruppo): string
|
||||||
{
|
{
|
||||||
$gruppo = GruppoServizio::attivi()->where('sorvegliante_id', $proclamatore->id)->first();
|
return "Era il sorvegliante del gruppo {$gruppo->nome}: il gruppo è stato sciolto, i territori non ancora dati sono tornati in reparto e la sua utenza è disattivata.";
|
||||||
if (! $gruppo) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->avvisaErrore("{$proclamatore->nome_completo} è il sorvegliante del gruppo {$gruppo->nome}: per {$azione} va prima sciolto il gruppo (Gruppi di servizio).");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
@@ -176,6 +170,8 @@ class ProclamatoreIndex extends Component
|
|||||||
|
|
||||||
return view('livewire.proclamatori.proclamatore-index', [
|
return view('livewire.proclamatori.proclamatore-index', [
|
||||||
'proclamatori' => $proclamatori,
|
'proclamatori' => $proclamatori,
|
||||||
|
// Per avvisare, prima di disattivare o cestinare, che il gruppo si scioglie.
|
||||||
|
'gruppiSorvegliati' => GruppoServizio::attivi()->pluck('nome', 'sorvegliante_id'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ use App\Livewire\Concerns\RicordaRitorno;
|
|||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use App\Models\Proclamatore;
|
use App\Models\Proclamatore;
|
||||||
use App\Models\Assegnazione;
|
use App\Models\Assegnazione;
|
||||||
|
use App\Models\GruppoServizio;
|
||||||
|
use Spatie\Activitylog\Models\Activity;
|
||||||
|
|
||||||
class ProclamatoreShow extends Component
|
class ProclamatoreShow extends Component
|
||||||
{
|
{
|
||||||
@@ -37,9 +39,25 @@ class ProclamatoreShow extends Component
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Gruppo di servizio: sorvegliante (con i dettagli del gruppo e della
|
||||||
|
// sua utenza), membro, o sorvegliante di gruppi ormai sciolti.
|
||||||
|
$gruppoSorvegliato = $this->proclamatore->gruppoSorvegliato();
|
||||||
|
$utenza = $gruppoSorvegliato ? $this->proclamatore->utente : null;
|
||||||
|
|
||||||
return view('livewire.proclamatori.proclamatore-show', [
|
return view('livewire.proclamatori.proclamatore-show', [
|
||||||
'assegnazioniPerAnno' => $assegnazioni,
|
'assegnazioniPerAnno' => $assegnazioni,
|
||||||
'stats' => $stats,
|
'stats' => $stats,
|
||||||
|
'gruppoSorvegliato' => $gruppoSorvegliato,
|
||||||
|
'riepilogoGruppo' => $gruppoSorvegliato?->riepilogo(),
|
||||||
|
'gruppoMembro' => $gruppoSorvegliato ? null : $this->proclamatore->gruppo()->with('sorvegliante')->first(),
|
||||||
|
'gruppiPassati' => GruppoServizio::whereNotNull('sciolto_at')
|
||||||
|
->where('sorvegliante_id', $this->proclamatore->id)
|
||||||
|
->orderByDesc('sciolto_at')
|
||||||
|
->get(),
|
||||||
|
'utenza' => $utenza,
|
||||||
|
'ultimoAccesso' => $utenza
|
||||||
|
? Activity::whereMorphedTo('causer', $utenza)->where('description', 'login')->max('created_at')
|
||||||
|
: null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,8 +87,13 @@ class Profile extends Component
|
|||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
// Il sorvegliante vede qui i dettagli del suo gruppo di servizio.
|
||||||
|
$gruppo = auth()->user()->gruppoSorvegliato();
|
||||||
|
|
||||||
return view('livewire.profile', [
|
return view('livewire.profile', [
|
||||||
'user' => auth()->user(),
|
'user' => auth()->user(),
|
||||||
|
'gruppo' => $gruppo,
|
||||||
|
'riepilogoGruppo' => $gruppo?->riepilogo(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,4 +72,24 @@ class GruppoServizio extends Model
|
|||||||
{
|
{
|
||||||
return $query->whereNull('sciolto_at');
|
return $query->whereNull('sciolto_at');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I numeri del gruppo per la scheda del proclamatore e il profilo del
|
||||||
|
* sorvegliante: solo conteggi in SQL, nessun nome da decifrare.
|
||||||
|
*
|
||||||
|
* @return array{membri: int, pacchetto: int, da_dare: int, dati: int, assegnazioni: int}
|
||||||
|
*/
|
||||||
|
public function riepilogo(): array
|
||||||
|
{
|
||||||
|
$pacchetto = $this->territori()->count();
|
||||||
|
$dati = $this->territori()->whereHas('assegnazioni', fn ($q) => $q->whereNull('returned_at'))->count();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'membri' => $this->membri()->count(),
|
||||||
|
'pacchetto' => $pacchetto,
|
||||||
|
'da_dare' => $pacchetto - $dati,
|
||||||
|
'dati' => $dati,
|
||||||
|
'assegnazioni' => $this->assegnazioni()->count(),
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,46 @@ class Proclamatore extends Model
|
|||||||
return $this->hasOne(User::class, 'proclamatore_id');
|
return $this->hasOne(User::class, 'proclamatore_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Un sorvegliante che non è più attivo — reso non attivo, spostato nel
|
||||||
|
* cestino o anonimizzato — non guida più il gruppo: il gruppo si scioglie
|
||||||
|
* (GruppiServizio::sciogli), da qualunque pagina arrivi la modifica. Un
|
||||||
|
* membro nel cestino lascia il gruppo.
|
||||||
|
*/
|
||||||
|
protected static function booted(): void
|
||||||
|
{
|
||||||
|
static::updated(function (self $proclamatore) {
|
||||||
|
if ($proclamatore->wasChanged('attivo') && ! $proclamatore->attivo) {
|
||||||
|
$proclamatore->lasciaIlGruppo('sorvegliante non più attivo');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
static::deleted(function (self $proclamatore) {
|
||||||
|
$proclamatore->lasciaIlGruppo('sorvegliante eliminato');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Il gruppo attivo di cui è sorvegliante, se c'è. */
|
||||||
|
public function gruppoSorvegliato(): ?GruppoServizio
|
||||||
|
{
|
||||||
|
return GruppoServizio::attivi()->where('sorvegliante_id', $this->id)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function lasciaIlGruppo(string $motivo): void
|
||||||
|
{
|
||||||
|
$gruppo = $this->gruppoSorvegliato();
|
||||||
|
if ($gruppo) {
|
||||||
|
app(\App\Services\GruppiServizio::class)->sciogli($gruppo, $motivo);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->trashed() && $this->gruppo_servizio_id) {
|
||||||
|
static::withTrashed()->whereKey($this->id)->update(['gruppo_servizio_id' => null]);
|
||||||
|
$this->gruppo_servizio_id = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -231,13 +231,13 @@ class GruppiServizio
|
|||||||
* fatte restano com'erano, con il loro riferimento al gruppo, che resta
|
* fatte restano com'erano, con il loro riferimento al gruppo, che resta
|
||||||
* nello storico come sciolto.
|
* nello storico come sciolto.
|
||||||
*/
|
*/
|
||||||
public function sciogli(GruppoServizio $gruppo): void
|
public function sciogli(GruppoServizio $gruppo, ?string $motivo = null): void
|
||||||
{
|
{
|
||||||
if ($gruppo->sciolto_at) {
|
if ($gruppo->sciolto_at) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::transaction(function () use ($gruppo) {
|
DB::transaction(function () use ($gruppo, $motivo) {
|
||||||
// Esce dal pacchetto solo quello non ancora dato: un territorio
|
// Esce dal pacchetto solo quello non ancora dato: un territorio
|
||||||
// assegnato resta del suo gruppo finché non rientra (Assegnazione
|
// assegnato resta del suo gruppo finché non rientra (Assegnazione
|
||||||
// lo libera al rientro), così non può essere ridato due volte.
|
// lo libera al rientro), così non può essere ridato due volte.
|
||||||
@@ -258,7 +258,11 @@ class GruppiServizio
|
|||||||
$gruppo->update(['sciolto_at' => now()]);
|
$gruppo->update(['sciolto_at' => now()]);
|
||||||
|
|
||||||
activity()->causedBy(auth()->user())->performedOn($gruppo)
|
activity()->causedBy(auth()->user())->performedOn($gruppo)
|
||||||
->withProperties(['territori_liberati' => $liberati->all(), 'utente_disattivato' => $utente?->id])
|
->withProperties(array_filter([
|
||||||
|
'territori_liberati' => $liberati->all(),
|
||||||
|
'utente_disattivato' => $utente?->id,
|
||||||
|
'motivo' => $motivo,
|
||||||
|
], fn ($v) => $v !== null))
|
||||||
->log('gruppo_sciolto');
|
->log('gruppo_sciolto');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,12 @@
|
|||||||
<input wire:model="attivo" type="checkbox" id="attivo" class="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500">
|
<input wire:model="attivo" type="checkbox" id="attivo" class="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500">
|
||||||
<label for="attivo" class="text-sm text-gray-700">Attivo</label>
|
<label for="attivo" class="text-sm text-gray-700">Attivo</label>
|
||||||
</div>
|
</div>
|
||||||
|
@if($gruppoSorvegliato ?? null)
|
||||||
|
<p class="-mt-2 text-xs text-amber-800">
|
||||||
|
È il sorvegliante del gruppo <strong>{{ $gruppoSorvegliato->nome }}</strong>: se non è più attivo il gruppo si scioglie,
|
||||||
|
i territori non ancora dati tornano in reparto e la sua utenza viene disattivata.
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="flex items-center gap-3 pt-4">
|
<div class="flex items-center gap-3 pt-4">
|
||||||
<x-btn variante="primary" type="submit">{{ $btnLabel }}</x-btn>
|
<x-btn variante="primary" type="submit">{{ $btnLabel }}</x-btn>
|
||||||
|
|||||||
@@ -73,6 +73,15 @@
|
|||||||
title="Modifica" aria-label="Modifica il proclamatore">
|
title="Modifica" aria-label="Modifica il proclamatore">
|
||||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>
|
||||||
</x-btn>
|
</x-btn>
|
||||||
|
@if($proclamatore->attivo && isset($gruppiSorvegliati[$proclamatore->id]))
|
||||||
|
{{-- Sorvegliante: disattivarlo scioglie il gruppo, si chiede conferma --}}
|
||||||
|
<x-btn variante="secondary" tono="tenue" dimensione="sm" icona
|
||||||
|
wire:click="toggleActive({{ $proclamatore->id }})"
|
||||||
|
wire:confirm="{{ $proclamatore->nome_completo }} è il sorvegliante del gruppo {{ $gruppiSorvegliati[$proclamatore->id] }}. Se non è più attivo il gruppo si scioglie: i territori non ancora dati tornano in reparto, i membri restano senza gruppo e la sua utenza viene disattivata. Procedere?"
|
||||||
|
title="Disattiva" aria-label="Disattiva il proclamatore">
|
||||||
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"/></svg>
|
||||||
|
</x-btn>
|
||||||
|
@else
|
||||||
<x-btn variante="secondary" tono="tenue" dimensione="sm" icona
|
<x-btn variante="secondary" tono="tenue" dimensione="sm" icona
|
||||||
wire:click="toggleActive({{ $proclamatore->id }})"
|
wire:click="toggleActive({{ $proclamatore->id }})"
|
||||||
title="{{ $proclamatore->attivo ? 'Disattiva' : 'Attiva' }}"
|
title="{{ $proclamatore->attivo ? 'Disattiva' : 'Attiva' }}"
|
||||||
@@ -83,9 +92,10 @@
|
|||||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||||
@endif
|
@endif
|
||||||
</x-btn>
|
</x-btn>
|
||||||
|
@endif
|
||||||
<x-btn variante="danger" tono="tenue" dimensione="sm" icona
|
<x-btn variante="danger" tono="tenue" dimensione="sm" icona
|
||||||
wire:click="deleteProclamatore({{ $proclamatore->id }})"
|
wire:click="deleteProclamatore({{ $proclamatore->id }})"
|
||||||
wire:confirm="Spostare il proclamatore nel cestino?"
|
wire:confirm="{{ isset($gruppiSorvegliati[$proclamatore->id]) ? 'Spostare nel cestino ' . $proclamatore->nome_completo . '? È il sorvegliante del gruppo ' . $gruppiSorvegliati[$proclamatore->id] . ': il gruppo si scioglie, i territori non ancora dati tornano in reparto e la sua utenza viene disattivata.' : 'Spostare il proclamatore nel cestino?' }}"
|
||||||
title="Elimina" aria-label="Sposta nel cestino il proclamatore">
|
title="Elimina" aria-label="Sposta nel cestino il proclamatore">
|
||||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
|
||||||
</x-btn>
|
</x-btn>
|
||||||
|
|||||||
@@ -36,6 +36,73 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- Gruppo di servizio --}}
|
||||||
|
@if($gruppoSorvegliato || $gruppoMembro || $gruppiPassati->isNotEmpty())
|
||||||
|
<section class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6" aria-labelledby="titolo-gruppo">
|
||||||
|
<h3 id="titolo-gruppo" class="text-lg font-semibold text-gray-900 mb-3">Gruppo di servizio</h3>
|
||||||
|
|
||||||
|
@if($gruppoSorvegliato)
|
||||||
|
<p class="text-sm text-gray-700">
|
||||||
|
<span class="inline-flex px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800">Sorvegliante</span>
|
||||||
|
del gruppo
|
||||||
|
@can('settings.manage')
|
||||||
|
<a data-ritorno href="{{ route('gruppi.show', $gruppoSorvegliato) }}" class="font-semibold text-indigo-600 hover:underline">{{ $gruppoSorvegliato->nome }}</a>
|
||||||
|
@else
|
||||||
|
<strong>{{ $gruppoSorvegliato->nome }}</strong>
|
||||||
|
@endcan
|
||||||
|
dal {{ $gruppoSorvegliato->created_at->format('d/m/Y') }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<dl class="mt-4 grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||||
|
<div class="rounded-lg bg-gray-50 px-3 py-2">
|
||||||
|
<dt class="text-xs text-gray-500">Membri</dt>
|
||||||
|
<dd class="text-xl font-bold text-gray-900">{{ $riepilogoGruppo['membri'] }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg bg-gray-50 px-3 py-2">
|
||||||
|
<dt class="text-xs text-gray-500">Nel pacchetto</dt>
|
||||||
|
<dd class="text-xl font-bold text-gray-900">{{ $riepilogoGruppo['pacchetto'] }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg bg-gray-50 px-3 py-2">
|
||||||
|
<dt class="text-xs text-gray-500">Da dare</dt>
|
||||||
|
<dd class="text-xl font-bold text-gray-900">{{ $riepilogoGruppo['da_dare'] }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg bg-gray-50 px-3 py-2">
|
||||||
|
<dt class="text-xs text-gray-500">Assegnazioni dal gruppo</dt>
|
||||||
|
<dd class="text-xl font-bold text-gray-900">{{ $riepilogoGruppo['assegnazioni'] }}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
@if($utenza)
|
||||||
|
<p class="mt-4 border-t border-gray-100 pt-3 text-sm text-gray-600">
|
||||||
|
Utenza
|
||||||
|
@can('settings.manage') <code>{{ $utenza->email }}</code> @endcan
|
||||||
|
· {{ $utenza->password_change_required ? 'password temporanea, ancora da cambiare' : 'password personale impostata' }}
|
||||||
|
· {{ $ultimoAccesso ? 'ultimo accesso il ' . \Illuminate\Support\Carbon::parse($ultimoAccesso)->format('d/m/Y H:i') : 'non è ancora entrato' }}
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
@elseif($gruppoMembro)
|
||||||
|
<p class="text-sm text-gray-700">
|
||||||
|
Membro del gruppo
|
||||||
|
@can('settings.manage')
|
||||||
|
<a data-ritorno href="{{ route('gruppi.show', $gruppoMembro) }}" class="font-semibold text-indigo-600 hover:underline">{{ $gruppoMembro->nome }}</a>,
|
||||||
|
@else
|
||||||
|
<strong>{{ $gruppoMembro->nome }}</strong>,
|
||||||
|
@endcan
|
||||||
|
sorvegliante {{ $gruppoMembro->sorvegliante?->nome_completo ?? '—' }}.
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($gruppiPassati->isNotEmpty())
|
||||||
|
<p class="{{ $gruppoSorvegliato || $gruppoMembro ? 'mt-3' : '' }} text-xs text-gray-500">
|
||||||
|
In passato sorvegliante di:
|
||||||
|
@foreach($gruppiPassati as $g)
|
||||||
|
{{ $g->nome }} (sciolto il {{ $g->sciolto_at->format('d/m/Y') }}){{ $loop->last ? '.' : ',' }}
|
||||||
|
@endforeach
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
</section>
|
||||||
|
@endif
|
||||||
|
|
||||||
{{-- Assignment history --}}
|
{{-- Assignment history --}}
|
||||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Storico Assegnazioni</h3>
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Storico Assegnazioni</h3>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<a href="{{ route('dashboard') }}" class="inline-flex items-center gap-1 text-sm text-gray-500 hover:text-indigo-600 transition-colors mb-1">
|
<a href="{{ $gruppo ? route('mio-gruppo') : route('dashboard') }}" class="inline-flex items-center gap-1 text-sm text-gray-500 hover:text-indigo-600 transition-colors mb-1">
|
||||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
|
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
|
||||||
Torna alla home
|
{{ $gruppo ? 'Torna al mio gruppo' : 'Torna alla home' }}
|
||||||
</a>
|
</a>
|
||||||
<h1 class="text-2xl font-bold text-gray-900">Il mio profilo</h1>
|
<h1 class="text-2xl font-bold text-gray-900">Il mio profilo</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,6 +25,37 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- Gruppo di servizio (solo per il sorvegliante) --}}
|
||||||
|
@if($gruppo)
|
||||||
|
<section class="bg-white rounded-xl shadow-sm border border-gray-200 p-6" aria-labelledby="titolo-mio-gruppo">
|
||||||
|
<h3 id="titolo-mio-gruppo" class="text-lg font-semibold text-gray-900 mb-1">Il tuo gruppo di servizio</h3>
|
||||||
|
<p class="text-sm text-gray-500 mb-4">
|
||||||
|
Sei il sorvegliante del gruppo <strong class="text-gray-800">{{ $gruppo->nome }}</strong> dal {{ $gruppo->created_at->format('d/m/Y') }}.
|
||||||
|
</p>
|
||||||
|
<dl class="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||||
|
<div class="rounded-lg bg-gray-50 px-3 py-2">
|
||||||
|
<dt class="text-xs text-gray-500">Membri</dt>
|
||||||
|
<dd class="text-xl font-bold text-gray-900">{{ $riepilogoGruppo['membri'] }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg bg-gray-50 px-3 py-2">
|
||||||
|
<dt class="text-xs text-gray-500">Nel pacchetto</dt>
|
||||||
|
<dd class="text-xl font-bold text-gray-900">{{ $riepilogoGruppo['pacchetto'] }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg bg-gray-50 px-3 py-2">
|
||||||
|
<dt class="text-xs text-gray-500">Da dare</dt>
|
||||||
|
<dd class="text-xl font-bold text-gray-900">{{ $riepilogoGruppo['da_dare'] }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg bg-gray-50 px-3 py-2">
|
||||||
|
<dt class="text-xs text-gray-500">Dati ai membri</dt>
|
||||||
|
<dd class="text-xl font-bold text-gray-900">{{ $riepilogoGruppo['dati'] }}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
<div class="mt-4">
|
||||||
|
<x-btn variante="primary" :href="route('mio-gruppo')">Vai al mio gruppo</x-btn>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
@endif
|
||||||
|
|
||||||
{{-- Dati profilo --}}
|
{{-- Dati profilo --}}
|
||||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||||
<h3 class="text-lg font-semibold text-gray-900 mb-1">Dati profilo</h3>
|
<h3 class="text-lg font-semibold text-gray-900 mb-1">Dati profilo</h3>
|
||||||
|
|||||||
@@ -377,24 +377,120 @@ class GruppiServizioTest extends TestCase
|
|||||||
$this->assertSame($nuovo->id, $utente->gruppoSorvegliato()?->id);
|
$this->assertSame($nuovo->id, $utente->gruppoSorvegliato()?->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Proclamatori: cestino, anonimizzazione, registro ───────
|
// ─── Sorvegliante non più attivo: il gruppo si scioglie ─────
|
||||||
|
|
||||||
public function test_il_sorvegliante_di_un_gruppo_attivo_non_si_cestina_ne_si_anonimizza(): void
|
/** Il gruppo è sciolto come da "Togli il ruolo": tutto tranne l'assegnazione già fatta. */
|
||||||
|
private function assertSciolto(array $g, Assegnazione $data): void
|
||||||
{
|
{
|
||||||
['rossi' => $rossi, 'verdi' => $verdi] = $this->gruppo();
|
$this->assertNotNull($g['gruppo']->fresh()->sciolto_at, 'Gruppo sciolto');
|
||||||
|
$this->assertNull($g['territori'][1]->fresh()->gruppo_servizio_id, 'I non dati tornano in reparto');
|
||||||
|
$this->assertSame('in_reparto', $g['territori'][2]->fresh()->stato);
|
||||||
|
$this->assertNull($data->fresh()->returned_at, 'Quello già dato resta al membro');
|
||||||
|
$this->assertNull($g['bianchi']->fresh()->gruppo_servizio_id, 'Membri liberi');
|
||||||
|
$this->assertTrue($g['sorvegliante']->fresh()->isDisattivato());
|
||||||
|
$this->assertFalse($g['sorvegliante']->fresh()->hasRole('sorvegliante'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_disattivare_il_sorvegliante_dall_elenco_scioglie_il_gruppo(): void
|
||||||
|
{
|
||||||
|
$g = $this->gruppo();
|
||||||
|
$data = app(GruppiServizio::class)->assegna($g['gruppo'], $g['territori'][0], $g['verdi'], now());
|
||||||
|
|
||||||
Livewire::test(\App\Livewire\Proclamatori\ProclamatoreIndex::class)
|
Livewire::test(\App\Livewire\Proclamatori\ProclamatoreIndex::class)
|
||||||
->call('deleteProclamatore', $rossi->id)
|
->assertSeeHtml('è il sorvegliante del gruppo Zisa')
|
||||||
->call('anonimizza', $rossi->id);
|
->call('toggleActive', $g['rossi']->id)
|
||||||
$this->assertFalse($rossi->fresh()->trashed());
|
->assertDispatched('avviso', fn ($nome, $parametri) => str_contains($parametri['testo'], 'il gruppo è stato sciolto'));
|
||||||
$this->assertSame('Rossi', $rossi->fresh()->cognome);
|
|
||||||
|
|
||||||
// Un membro qualsiasi sì, e lascia il gruppo.
|
$this->assertFalse($g['rossi']->fresh()->attivo);
|
||||||
Livewire::test(\App\Livewire\Proclamatori\ProclamatoreIndex::class)->call('deleteProclamatore', $verdi->id);
|
$this->assertSciolto($g, $data);
|
||||||
$this->assertTrue($verdi->fresh()->trashed());
|
|
||||||
$this->assertNull($verdi->fresh()->gruppo_servizio_id);
|
// Riattivarlo non ricostruisce il gruppo: se ne crea uno nuovo.
|
||||||
|
Livewire::test(\App\Livewire\Proclamatori\ProclamatoreIndex::class)->call('toggleActive', $g['rossi']->id);
|
||||||
|
$this->assertNotNull($g['gruppo']->fresh()->sciolto_at);
|
||||||
|
$this->assertNull($g['rossi']->fresh()->gruppoSorvegliato());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_disattivare_il_sorvegliante_dalla_modifica_scioglie_il_gruppo(): void
|
||||||
|
{
|
||||||
|
$g = $this->gruppo();
|
||||||
|
$data = app(GruppiServizio::class)->assegna($g['gruppo'], $g['territori'][0], $g['verdi'], now());
|
||||||
|
|
||||||
|
$this->get(route('proclamatori.edit', $g['rossi']))->assertOk()->assertSee('se non è più attivo il gruppo si scioglie');
|
||||||
|
|
||||||
|
Livewire::test(\App\Livewire\Proclamatori\ProclamatoreEdit::class, ['proclamatore' => $g['rossi']])
|
||||||
|
->set('attivo', false)
|
||||||
|
->call('save')
|
||||||
|
->assertHasNoErrors();
|
||||||
|
|
||||||
|
$this->assertSciolto($g, $data);
|
||||||
|
$this->assertStringContainsString('il gruppo è stato sciolto', session('success'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_spostare_nel_cestino_il_sorvegliante_scioglie_il_gruppo(): void
|
||||||
|
{
|
||||||
|
$g = $this->gruppo();
|
||||||
|
$data = app(GruppiServizio::class)->assegna($g['gruppo'], $g['territori'][0], $g['verdi'], now());
|
||||||
|
|
||||||
|
Livewire::test(\App\Livewire\Proclamatori\ProclamatoreIndex::class)->call('deleteProclamatore', $g['rossi']->id);
|
||||||
|
|
||||||
|
$this->assertTrue($g['rossi']->fresh()->trashed());
|
||||||
|
$this->assertSciolto($g, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_disattivare_o_cestinare_un_membro_non_scioglie_il_gruppo(): void
|
||||||
|
{
|
||||||
|
['gruppo' => $gruppo, 'verdi' => $verdi, 'bianchi' => $bianchi] = $this->gruppo();
|
||||||
|
|
||||||
|
Livewire::test(\App\Livewire\Proclamatori\ProclamatoreIndex::class)
|
||||||
|
->call('toggleActive', $verdi->id)
|
||||||
|
->call('deleteProclamatore', $bianchi->id);
|
||||||
|
|
||||||
|
$this->assertNull($gruppo->fresh()->sciolto_at);
|
||||||
|
$this->assertSame($gruppo->id, $verdi->fresh()->gruppo_servizio_id, 'Non attivo resta membro, ma non riceve territori');
|
||||||
|
$this->assertNull($bianchi->fresh()->gruppo_servizio_id, 'Nel cestino lascia il gruppo');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Dettagli del gruppo nel profilo ────────────────────────
|
||||||
|
|
||||||
|
public function test_la_scheda_del_proclamatore_mostra_il_gruppo_che_guida(): void
|
||||||
|
{
|
||||||
|
$g = $this->gruppo();
|
||||||
|
app(GruppiServizio::class)->assegna($g['gruppo'], $g['territori'][0], $g['verdi'], now());
|
||||||
|
|
||||||
|
$this->get(route('proclamatori.show', $g['rossi']))->assertOk()
|
||||||
|
->assertSeeInOrder(['Gruppo di servizio', 'Sorvegliante', 'Zisa', 'Membri', '3', 'Nel pacchetto', '3', 'Da dare', '2', 'Assegnazioni dal gruppo', '1'])
|
||||||
|
->assertSee('mario@example.test')
|
||||||
|
->assertSee('non è ancora entrato');
|
||||||
|
|
||||||
|
$this->get(route('proclamatori.show', $g['verdi']))->assertOk()
|
||||||
|
->assertSee('Membro del gruppo')
|
||||||
|
->assertSee('sorvegliante Rossi Mario');
|
||||||
|
|
||||||
|
app(GruppiServizio::class)->sciogli($g['gruppo']);
|
||||||
|
$this->get(route('proclamatori.show', $g['rossi']))->assertOk()
|
||||||
|
->assertSee('In passato sorvegliante di:')
|
||||||
|
->assertDontSee('Assegnazioni dal gruppo');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_il_profilo_del_sorvegliante_mostra_il_suo_gruppo(): void
|
||||||
|
{
|
||||||
|
$g = $this->gruppo();
|
||||||
|
app(GruppiServizio::class)->assegna($g['gruppo'], $g['territori'][0], $g['verdi'], now());
|
||||||
|
$this->flushSession();
|
||||||
|
|
||||||
|
$this->actingAs($g['sorvegliante'])->get(route('profile'))->assertOk()
|
||||||
|
->assertSee('Il tuo gruppo di servizio')
|
||||||
|
->assertSeeInOrder(['Zisa', 'Membri', '3', 'Nel pacchetto', '3', 'Da dare', '2', 'Dati ai membri', '1'])
|
||||||
|
->assertSee('Torna al mio gruppo');
|
||||||
|
|
||||||
|
// L'amministratore un gruppo suo non ce l'ha.
|
||||||
|
$this->actingAs($this->admin)->get(route('profile'))->assertOk()
|
||||||
|
->assertDontSee('Il tuo gruppo di servizio')
|
||||||
|
->assertSee('Torna alla home');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Proclamatori: anonimizzazione, registro ────────────────
|
||||||
|
|
||||||
public function test_anonimizzare_un_ex_sorvegliante_anonimizza_anche_la_sua_utenza(): void
|
public function test_anonimizzare_un_ex_sorvegliante_anonimizza_anche_la_sua_utenza(): void
|
||||||
{
|
{
|
||||||
['gruppo' => $gruppo, 'rossi' => $rossi, 'sorvegliante' => $s] = $this->gruppo();
|
['gruppo' => $gruppo, 'rossi' => $rossi, 'sorvegliante' => $s] = $this->gruppo();
|
||||||
|
|||||||
Reference in New Issue
Block a user