diff --git a/HANDOFF.md b/HANDOFF.md index bfcb03a..50a8349 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -181,6 +181,13 @@ Decisioni successive dell'utente (20 ottobre 2026): 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". +- **Anche i membri si segnalano**: `Proclamatore::gruppo_etichetta` + ("Gruppo Zisa" / "Sorvegliante del gruppo Zisa") come badge nell'elenco + (più il filtro *In un gruppo di servizio*, `filtroStato=gruppo`) e in testa + alla scheda; nelle opzioni di `` come `dettaglio` (Assegna, + Registro), non come `motivo`: assegnare direttamente a un membro resta + possibile. In "Il mio gruppo" la nota non c'è (`opzioniScelta(..., + conGruppo: false)`). - L'utenza del sorvegliante porta nome ed email del proclamatore: nel registro delle attività `User::tapActivity()` li sostituisce con "proclamatore #id", come vale per tutti i proclamatori. Restano invece in chiaro nella tabella diff --git a/README.md b/README.md index 8addf69..ac1d706 100644 --- a/README.md +++ b/README.md @@ -766,6 +766,11 @@ docker compose --profile dev up -d --build 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 +- **Chi fa parte di un gruppo è segnalato**: badge "Gruppo …" o + "Sorvegliante del gruppo …" nell'elenco dei proclamatori (con il filtro + *In un gruppo di servizio*) e in testa alla scheda; nella scelta del + proclamatore (Assegna, Registro) la nota "gruppo …" accanto al nome, che + resta comunque sceglibile - **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 diff --git a/app/Livewire/Gruppi/MioGruppo.php b/app/Livewire/Gruppi/MioGruppo.php index 3b654c5..99280a2 100644 --- a/app/Livewire/Gruppi/MioGruppo.php +++ b/app/Livewire/Gruppi/MioGruppo.php @@ -184,7 +184,7 @@ class MioGruppo extends Component 'gruppo' => $gruppo, 'territori' => $territori, 'membri' => $membri, - 'opzioniMembri' => Proclamatore::opzioniScelta($membri), + 'opzioniMembri' => Proclamatore::opzioniScelta($membri, conGruppo: false), 'invio' => $this->invioCorrente(), ]); } diff --git a/app/Livewire/Proclamatori/ProclamatoreIndex.php b/app/Livewire/Proclamatori/ProclamatoreIndex.php index 469344a..60f47fa 100644 --- a/app/Livewire/Proclamatori/ProclamatoreIndex.php +++ b/app/Livewire/Proclamatori/ProclamatoreIndex.php @@ -106,7 +106,7 @@ class ProclamatoreIndex extends Component // withCount rather than letting the view call // ->assegnazioni()->aperte()->count() per row, which issued one // COUNT(*) per displayed proclamatore on every render. - $query = Proclamatore::query()->withCount([ + $query = Proclamatore::query()->with('gruppo')->withCount([ 'assegnazioni as assegnazioni_aperte_count' => fn ($q) => $q->whereNull('returned_at'), ]); @@ -124,6 +124,8 @@ class ProclamatoreIndex extends Component $filtered = $filtered->where('attivo', true); } elseif ($this->filtroStato === 'inattivo') { $filtered = $filtered->where('attivo', false); + } elseif ($this->filtroStato === 'gruppo') { + $filtered = $filtered->whereNotNull('gruppo_servizio_id'); } // Sort in-memory @@ -148,6 +150,8 @@ class ProclamatoreIndex extends Component $query->where('attivo', true); } elseif ($this->filtroStato === 'inattivo') { $query->where('attivo', false); + } elseif ($this->filtroStato === 'gruppo') { + $query->whereNotNull('gruppo_servizio_id'); } // cognome/nome encrypted, so sort in-memory diff --git a/app/Models/Proclamatore.php b/app/Models/Proclamatore.php index a81fbd2..64c88c8 100644 --- a/app/Models/Proclamatore.php +++ b/app/Models/Proclamatore.php @@ -103,9 +103,13 @@ class Proclamatore extends Model */ public function opzioneScelta(?string $motivo = null): array { + // Il gruppo di servizio come nota, solo se già caricato (opzioniScelta). + $gruppo = $this->relationLoaded('gruppo') ? $this->gruppo_etichetta : null; + return array_filter([ 'valore' => (string) $this->id, 'etichetta' => $this->nome_completo, + 'dettaglio' => $gruppo !== null ? lcfirst($gruppo) : null, 'chiavi' => [(string) $this->cognome, (string) $this->nome], 'motivo' => $motivo, ], fn ($v) => $v !== null); @@ -113,10 +117,16 @@ class Proclamatore extends Model /** * Le opzioni di un elenco di proclamatori, in ordine di cognome e nome. - * L'ordine si fa qui: in SQL ordinerebbe i testi cifrati. + * L'ordine si fa qui: in SQL ordinerebbe i testi cifrati. Con $conGruppo + * chi fa parte di un gruppo di servizio lo dice accanto al nome (non serve + * dove sono tutti dello stesso gruppo, come in "Il mio gruppo"). */ - public static function opzioniScelta(iterable $proclamatori): array + public static function opzioniScelta(iterable $proclamatori, bool $conGruppo = true): array { + if ($conGruppo && $proclamatori instanceof \Illuminate\Database\Eloquent\Collection) { + $proclamatori->loadMissing('gruppo'); + } + return collect($proclamatori) ->sortBy(fn (self $p) => mb_strtolower($p->cognome . ' ' . $p->nome)) ->map(fn (self $p) => $p->opzioneScelta($p->attivo ? null : 'non attivo')) @@ -124,6 +134,22 @@ class Proclamatore extends Model ->all(); } + /** + * "Sorvegliante del gruppo Zisa" o "Gruppo Zisa"; null fuori dai gruppi. + * Senza gruppo_servizio_id non fa query. + */ + public function getGruppoEtichettaAttribute(): ?string + { + $gruppo = $this->gruppo; + if (! $gruppo) { + return null; + } + + return $gruppo->sorvegliante_id === $this->id + ? "Sorvegliante del gruppo {$gruppo->nome}" + : "Gruppo {$gruppo->nome}"; + } + public function assegnazioni() { return $this->hasMany(Assegnazione::class, 'proclamatore_id'); diff --git a/resources/views/livewire/proclamatori/proclamatore-index.blade.php b/resources/views/livewire/proclamatori/proclamatore-index.blade.php index f990ec9..67cd283 100644 --- a/resources/views/livewire/proclamatori/proclamatore-index.blade.php +++ b/resources/views/livewire/proclamatori/proclamatore-index.blade.php @@ -26,6 +26,7 @@ + @@ -53,11 +54,16 @@ {{ $proclamatore->cognome }} {{ $proclamatore->nome }} +
@if($proclamatore->attivo) Attivo @else Inattivo @endif + @if($proclamatore->gruppo_etichetta) + {{ $proclamatore->gruppo_etichetta }} + @endif +
{{ $proclamatore->assegnazioni_aperte_count ?? $proclamatore->assegnazioni()->aperte()->count() }} diff --git a/resources/views/livewire/proclamatori/proclamatore-show.blade.php b/resources/views/livewire/proclamatori/proclamatore-show.blade.php index 509dce9..ee9f1bb 100644 --- a/resources/views/livewire/proclamatori/proclamatore-show.blade.php +++ b/resources/views/livewire/proclamatori/proclamatore-show.blade.php @@ -6,6 +6,9 @@ {{ $ritorno ? 'Torna indietro' : 'Torna alla lista' }}

{{ $proclamatore->nome_completo }}

+ @if($proclamatore->gruppo_etichetta) + {{ $proclamatore->gruppo_etichetta }} + @endif @can('settings.manage') diff --git a/tests/Feature/GruppiServizioTest.php b/tests/Feature/GruppiServizioTest.php index 2f184c1..34f4afe 100644 --- a/tests/Feature/GruppiServizioTest.php +++ b/tests/Feature/GruppiServizioTest.php @@ -472,6 +472,33 @@ class GruppiServizioTest extends TestCase ->assertDontSee('Assegnazioni dal gruppo'); } + public function test_chi_fa_parte_di_un_gruppo_e_segnalato_in_elenco_scheda_e_scelta(): void + { + $g = $this->gruppo(); + $this->proclamatore('Neri'); + + Livewire::test(\App\Livewire\Proclamatori\ProclamatoreIndex::class) + ->assertSee('Sorvegliante del gruppo Zisa') + ->assertSee('Gruppo Zisa') + ->set('filtroStato', 'gruppo') + ->assertSee('Verdi')->assertSee('Bianchi')->assertSee('Rossi') + ->assertDontSee('Neri'); + + $this->get(route('proclamatori.show', $g['verdi']))->assertOk()->assertSee('Gruppo Zisa'); + + // Nella tendina di Assegna la nota accanto al nome... + $opzioni = collect(Livewire::test(Assegna::class)->viewData('opzioniProclamatori'))->keyBy('etichetta'); + $this->assertSame('gruppo Zisa', $opzioni['Verdi Anna']['dettaglio']); + $this->assertSame('sorvegliante del gruppo Zisa', $opzioni['Rossi Mario']['dettaglio']); + $this->assertArrayNotHasKey('dettaglio', $opzioni['Neri Test']); + $this->assertArrayNotHasKey('motivo', $opzioni['Verdi Anna'], 'Si può comunque scegliere'); + + // ...che in "Il mio gruppo", dove sono tutti dello stesso gruppo, non serve. + $this->flushSession(); + $membri = Livewire::actingAs($g['sorvegliante'])->test(MioGruppo::class)->viewData('opzioniMembri'); + $this->assertSame([], collect($membri)->pluck('dettaglio')->filter()->all()); + } + public function test_il_profilo_del_sorvegliante_mostra_il_suo_gruppo(): void { $g = $this->gruppo();