Aggiorna la documentazione del ruolo allenatore a implementato

La specifica in docs/modules/allenatore.md passa da «da implementare» a
«implementato», con Home, notifiche, dove si esclude l'allenatore e le scelte di
implementazione; DD-034 diventa Accettata. Voce in CHANGELOG (Non rilasciato) e
in ROADMAP tra le cose fatte.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
davideandClaude Opus 5.5 committed 2026-09-24 14:54:43 +02:00
1 parent 2ec72fd08f
commit 15a23dfaf4
4 files changed
+79 -66

No files matched your search

+8
View File
@@ -12,6 +12,14 @@ all'indietro.
## [Non rilasciato] ## [Non rilasciato]
### Aggiunto
- **Ruolo allenatore** — l'admin registra un allenatore da `/admin` (tipo «Allenatore», con
l'email Gmail) e lui si collega al primo accesso come i giocatori. Gestisce gli eventi e
sollecita le presenze; ha un profilo ridotto (dati personali e foto, senza stagione e
badge), compare in Squadra con la dicitura «Allenatore» e non vede badge né cacche.
Migration `m20_ruolo_allenatore_enum` e `m21_ruolo_allenatore` (DD-034).
### Modificato ### Modificato
- **Notifiche in-app** — tolta la campanella in alto a destra: il numero delle notifiche è ora - **Notifiche in-app** — tolta la campanella in alto a destra: il numero delle notifiche è ora
+8 -3
View File
@@ -49,13 +49,13 @@ Serve a rispondere a domande del tipo:
| [DD-031](#dd-031--data-di-nascita-pubblica-una-colonna-sincronizzata-non-una-seconda-rls-aperta) | Nascita pubblica sincronizzata | | [DD-031](#dd-031--data-di-nascita-pubblica-una-colonna-sincronizzata-non-una-seconda-rls-aperta) | Nascita pubblica sincronizzata |
| [DD-032](#dd-032--la-stagione-csi-si-ricava-dalle-date-delle-gare) | Stagione CSI dalle date delle gare | | [DD-032](#dd-032--la-stagione-csi-si-ricava-dalle-date-delle-gare) | Stagione CSI dalle date delle gare |
| [DD-033](#dd-033--gestione-eventi-solo-calendario-giorno-fissato-dal-tocco) | Gestione eventi solo da calendario | | [DD-033](#dd-033--gestione-eventi-solo-calendario-giorno-fissato-dal-tocco) | Gestione eventi solo da calendario |
| [DD-034](#dd-034--lallenatore-è-uno-slot-della-squadra-con-tipo-diverso-non-un-giocatore) | Ruolo allenatore |
**In valutazione** **In valutazione**
| ID | Titolo | | ID | Titolo |
| ----------------------------------------------------------------------------------------- | --------------------- | | ---------------------------------------------------------------- | --------------------- |
| [DD-014](#dd-014--convergenza-schema-database-eventi-e-presenze) | Convergenza schema DB | | [DD-014](#dd-014--convergenza-schema-database-eventi-e-presenze) | Convergenza schema DB |
| [DD-034](#dd-034--lallenatore-è-uno-slot-della-squadra-con-tipo-diverso-non-un-giocatore) | Ruolo allenatore |
**Sostituite** **Sostituite**
@@ -1404,7 +1404,7 @@ per titolo invece di reintrodurre la lista completa.
### DD-034 — L'allenatore è uno slot della squadra con tipo diverso, non un giocatore ### DD-034 — L'allenatore è uno slot della squadra con tipo diverso, non un giocatore
**Data:** 24 settembre 2026 **Data:** 24 settembre 2026
**Stato:** In valutazione **Stato:** Accettata (implementata con le migration M20 e M21)
**Contesto** **Contesto**
Serve un account per l'allenatore: deve gestire gli eventi (oggi solo admin, `m11`) ma non è Serve un account per l'allenatore: deve gestire gli eventi (oggi solo admin, `m11`) ma non è
@@ -1438,6 +1438,11 @@ collega uno slot di tipo allenatore: è quindi l'admin a concederlo, registrando
nella specifica e va coperto dai test di `permessi.test.ts`. nella specifica e va coperto dai test di `permessi.test.ts`.
- Il trigger sugli slot cambia: `tipo` lo scrive solo l'admin; nome e cognome li scrive anche - Il trigger sugli slot cambia: `tipo` lo scrive solo l'admin; nome e cognome li scrive anche
l'allenatore sul proprio slot. l'allenatore sul proprio slot.
- `numero` diventa nullabile a database, ma solo per gli allenatori (vincolo di M21); nell'app
resta un numero, 0 per l'allenatore, per non toccare i tipi di tutta la rosa.
- Due fonti distinte, ciascuna per il suo scopo: i permessi (eventi, sollecito) da
`user_roles`, la visibilità (badge, cacche, stagione) dal tipo del proprio slot, che è
immediato e non aspetta la query dei ruoli.
- L'allenatore non vede le tab Stagione e Badge del Profilo, né Obiettivi e Badge di Squadra. - L'allenatore non vede le tab Stagione e Badge del Profilo, né Obiettivi e Badge di Squadra.
- In Squadra compare nella tab Rosa con «Allenatore» al posto del ruolo, ricavato da `tipo`; - In Squadra compare nella tab Rosa con «Allenatore» al posto del ruolo, ricavato da `tipo`;
riceve i promemoria degli eventi e può sollecitare le presenze, non il turno palloni. riceve i promemoria degli eventi e può sollecitare le presenze, non il turno palloni.
+3 -2
View File
@@ -36,6 +36,9 @@ Tutto quello che è in `main`, rilasciato in versione 0.9.0 (vedi `CHANGELOG.md`
- [x] Risultati campionato - [x] Risultati campionato
- [x] Dettaglio partita — formazioni, storico scontri diretti e probabilità di vittoria - [x] Dettaglio partita — formazioni, storico scontri diretti e probabilità di vittoria
calcolata dal CSI calcolata dal CSI
- [x] Ruolo allenatore — gestisce gli eventi e sollecita le presenze, profilo ridotto senza
stagione e badge ([specifica](modules/allenatore.md), DD-034); in `develop`, non ancora
rilasciato
## Prossimo ## Prossimo
@@ -44,8 +47,6 @@ Tutto quello che è in `main`, rilasciato in versione 0.9.0 (vedi `CHANGELOG.md`
- [ ] Database esercizi - [ ] Database esercizi
- [ ] AI Allenamenti - [ ] AI Allenamenti
- [ ] Archivio allenamenti - [ ] Archivio allenamenti
- [ ] Ruolo allenatore — gestisce gli eventi, profilo ridotto senza stagione e badge
([specifica](modules/allenatore.md), DD-034)
## Idee future ## Idee future
+60 -61
View File
@@ -1,10 +1,15 @@
# Modulo — Ruolo Allenatore # Modulo — Ruolo Allenatore
**Stato:** da implementare (specifica) **Stato:** implementato
**Decisione:** [DD-034](../DESIGN_DECISIONS.md#dd-034--lallenatore-è-uno-slot-della-squadra-con-tipo-diverso-non-un-giocatore) **Decisione:** [DD-034](../DESIGN_DECISIONS.md#dd-034--lallenatore-è-uno-slot-della-squadra-con-tipo-diverso-non-un-giocatore)
**File coinvolti (previsti):** `src/lib/ruoli.ts`, `src/lib/auth-route.server.ts`, **Migration:** `m20_ruolo_allenatore_enum`, `m21_ruolo_allenatore`
`src/routes/profilo.tsx`, `src/routes/eventi.tsx`, `src/routes/admin.tsx`, **File principali:** `src/lib/giocatori-squadra.ts` (`tipo`, `inRosa`, `isAllenatore`,
`src/lib/profili-core.ts`, i punti che leggono la rosa (vedi [Esclusione dalla rosa](#esclusione-dalla-rosa)) `ruoloVisibile`), `src/lib/ruoli.ts`, `src/lib/user-store.ts` (`useSonoAllenatore`,
`useGiocatoreInCampo`), `src/lib/auth-route.server.ts`, `src/routes/profilo.tsx`,
`src/routes/admin.tsx`, `src/routes/squadra.tsx`, `src/routes/index.tsx`
**Test:** `test/unit/giocatori-squadra.test.ts`, `test/unit/profili-core.test.ts`,
`test/unit/presenze.test.ts`, `test/integration/permessi-allenatore.test.ts`,
`test/integration/permessi-route.test.ts`
## Obiettivo ## Obiettivo
@@ -28,7 +33,7 @@ auto-promuoversi, perché il tipo dello slot lo scrive solo un admin (come oggi
## Profilo ## Profilo
Il profilo dell'allenatore ha solo due tab (in `TAB_PROFILO` oggi sono quattro): Il profilo dell'allenatore ha solo due tab (quelle del giocatore sono quattro):
| Tab | Giocatore | Allenatore | | Tab | Giocatore | Allenatore |
| ------------ | --------- | ---------------------------------------------- | | ------------ | --------- | ---------------------------------------------- |
@@ -55,8 +60,16 @@ tessera, tesseramento CSI.
### Completamento profilo ### Completamento profilo
Per l'allenatore il completamento conta solo i dati personali della tabella sopra (100%). Per l'allenatore il completamento conta solo data e luogo di nascita, telefono ed email, 25%
Il widget in Home segue la stessa regola: sparisce quando quei campi sono compilati. ciascuno (`completamentoAllenatore()`). Il widget in Home segue la stessa regola: sparisce
quando quei campi sono compilati.
## Home
La Home dell'allenatore tiene ciò che riguarda la squadra e toglie ciò che riguarda il
giocatore: restano classifica e bilancio, prossimo impegno e ultima partita; spariscono lo
streak personale, il promemoria palloni, «Da confermare», l'obiettivo di squadra e il
«Colpo d'occhio» con le statistiche personali.
## Squadra ## Squadra
@@ -65,8 +78,8 @@ per un giocatore c'è il ruolo in campo (palleggiatore, schiacciatore…) per lu
**Allenatore**, ricavato da `tipo` e non dal campo `ruolo`. Non ha numero di maglia né **Allenatore**, ricavato da `tipo` e non dal campo `ruolo`. Non ha numero di maglia né
statistiche, e non compare nella tab Stats né nelle classifiche. statistiche, e non compare nella tab Stats né nelle classifiche.
Da allenatore, in `/squadra` vede solo **Rosa** e **Stats**. Le tab **Obiettivi** e **Badge** Da allenatore, in `/squadra` vede solo **Rosa** e **Stats**: le tab **Obiettivi** e **Badge**
non compaiono, e un link diretto a una delle due apre Rosa. non compaiono.
| Tab | Giocatore | Allenatore | | Tab | Giocatore | Allenatore |
| --------- | --------- | ---------- | | --------- | --------- | ---------- |
@@ -85,6 +98,7 @@ nessuna schermata:
| `/squadra` → Stats | il criterio di ordinamento «Cacche» (`cacchePartita`) | | `/squadra` → Stats | il criterio di ordinamento «Cacche» (`cacchePartita`) |
| `/squadra` → Rosa, scheda giocatore | la riga «Cacche/partita 💩» | | `/squadra` → Rosa, scheda giocatore | la riga «Cacche/partita 💩» |
| `/partita/$id` | la sezione «Badge votati dai compagni» e il sondaggio cacche (`SondaggioCacche`) | | `/partita/$id` | la sezione «Badge votati dai compagni» e il sondaggio cacche (`SondaggioCacche`) |
| push «Sondaggio pre-partita aperto» | non parte verso i dispositivi degli allenatori (`apri-sondaggio.ts`) |
| `/profilo`, `/squadra` | le tab Badge (già escluse sopra) | | `/profilo`, `/squadra` | le tab Badge (già escluse sopra) |
Presenze, media voto, MVP e palloni restano visibili. Presenze, media voto, MVP e palloni restano visibili.
@@ -92,15 +106,14 @@ Presenze, media voto, MVP e palloni restano visibili.
## Gestione eventi ## Gestione eventi
L'allenatore ha su `/eventi` gli stessi poteri dell'admin: crea, modifica, sposta ed elimina L'allenatore ha su `/eventi` gli stessi poteri dell'admin: crea, modifica, sposta ed elimina
eventi e sceglie i convocati. Nel database la policy di scrittura su `eventi_app` passa da eventi e sceglie i convocati (l'elenco dei convocati contiene solo giocatori). Nel database
"solo admin" a "admin o allenatore" (oggi `m11_scritture_per_ruolo`); la cancellazione a la policy di scrittura su `eventi_app` è "admin o allenatore" (M21); la cancellazione a
cascata (DD-029) resta invariata. cascata (DD-029) resta invariata. Il bottone «Gestisci eventi» del Calendario compare anche
a lui (`usePuoGestireEventi()`).
Il link a `/eventi` compare dove oggi compare per l'admin.
Può anche **sollecitare le presenze** di un evento, come l'admin: la route Può anche **sollecitare le presenze** di un evento, come l'admin: la route
`sollecita-presenze.ts` accetta admin o allenatore (oggi `richiediAdmin()`), e il sollecito `sollecita-presenze.ts` accetta admin o allenatore (`richiediGestoreEventi()`), e il
arriva solo ai giocatori che non hanno risposto, mai all'allenatore stesso. sollecito arriva solo ai giocatori che non hanno risposto, mai all'allenatore stesso.
**Restano solo admin:** dashboard `/admin`, dati e documenti dei giocatori, export CSI, **Restano solo admin:** dashboard `/admin`, dati e documenti dei giocatori, export CSI,
notifica personalizzata, promemoria palloni, apertura sondaggio cacche, correzione delle notifica personalizzata, promemoria palloni, apertura sondaggio cacche, correzione delle
@@ -108,26 +121,27 @@ risposte presenze altrui, gestione dei ruoli.
## Notifiche ## Notifiche
L'allenatore riceve i promemoria degli eventi (24h e 3h prima, push e centro notifiche L'allenatore riceve i promemoria automatici degli eventi (24h e 3h prima, centro notifiche
in-app) come i giocatori, anche se non è convocato, e può attivare le push dal proprio in-app di M17) come i giocatori, anche se non è convocato: `giocatori_destinatari_evento()`
Profilo. Non riceve il sollecito presenze né il turno palloni, che riguardano chi gioca. lo include sempre. Riceve anche i messaggi dell'admin, e può attivare le push dal proprio
Profilo. Non riceve il sollecito presenze, il turno palloni né il sondaggio cacche, che
riguardano chi gioca.
## Esclusione dalla rosa ## Esclusione dalla rosa
L'allenatore ha uno slot in `giocatori_squadra` (DD-034), quindi ogni lettura della "rosa di L'allenatore ha uno slot in `giocatori_squadra` (DD-034), quindi ogni lettura della "rosa di
gioco" deve escluderlo: oggi il filtro è `attivo`, diventa `attivo e tipo = giocatore`. Fanno gioco" lo esclude con `inRosa()` (`attivo` e `tipo = giocatore`) al posto del vecchio filtro
eccezione la tab Rosa di Squadra, i compleanni del Calendario e i destinatari dei promemoria su `attivo`. Fanno eccezione la tab Rosa di Squadra, i compleanni del Calendario
evento, che lo includono. Punti da coprire, da verificare uno per uno in implementazione: (`useAnagraficaRosa({ conAllenatori: true })`), i destinatari dei promemoria evento e dei
messaggi admin, che lo includono.
- client: `useRosa()`, `useGiocatoriSquadra()` (dove usata come rosa), `useAnagraficaRosa()` | Livello | Dove si esclude l'allenatore |
— Squadra (Stats, Obiettivi, Badge), Presenze, Palloni, Pagelle, MVP, Badge, Classifica, | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Scout, form convocati di `/eventi`; | client | `useRosa()`, `useAnagraficaRosa()`, `useTurniPalloni()`, `usePresenzeUltimoMeseTutti()`, form convocati di `/eventi`, `EventoCard`, `TurnoPalloni`, `VotazioneMvp`, `VotoSocial` |
- server e condivisi: `leggiGiocatoriSquadra()`, `convocatiEvento()` (`eventi.ts`, riceve la | identità | `useGiocatoreInCampo()` è `null` per l'allenatore: niente risposta presenze, voti, pagelle, cacche, promemoria palloni |
rosa già filtrata), route API che notificano "tutta la rosa"; | server | `destinatariSollecito()`, `promemoria-palloni.ts`, `apri-sondaggio.ts` |
- database: `evento_permette_voto()` (M13), che con `convocati` vuoto intende "tutta la rosa | database | policy presenze e cacche «la propria riga» solo per `tipo = giocatore`; `evento_permette_voto()` rifiuta votante o votato allenatore |
attiva"; i cron dei promemoria evento (M17) invece aggiungono sempre l'allenatore ai | admin | tab Squadra (conteggi, export CSI) e Profili (allenatori in un gruppo a parte, senza documenti né tesseramento) |
destinatari, convocato o no;
- admin: tab Profili (conteggi di completamento e tesseramento), export CSI.
Il compleanno dell'allenatore resta visibile in Calendario (è un membro della squadra, non un Il compleanno dell'allenatore resta visibile in Calendario (è un membro della squadra, non un
giocatore). giocatore).
@@ -148,37 +162,22 @@ giocatore).
| Badge e classifiche/dati sulle cacche | sì | no | sì | | Badge e classifiche/dati sulle cacche | sì | no | sì |
| Dashboard `/admin` | no | no | sì | | Dashboard `/admin` | no | no | sì |
## Cosa implementare ## Implementazione
1. **Migration** (nuova, non si riscrivono le esistenti): - **Migration M20** aggiunge `allenatore` all'enum `app_role`: sta da sola perché Postgres non
- `giocatori_squadra.tipo` (`giocatore` | `allenatore`, default `giocatore`, non nullo); lascia usare un valore di enum nella transazione che lo crea.
- valore `allenatore` nell'enum `app_role`, assegnato in `user_roles` da una funzione - **Migration M21**: colonna `giocatori_squadra.tipo`; `numero` nullabile solo per gli
`SECURITY DEFINER` al momento del collegamento di uno slot con `tipo = allenatore` allenatori (vincolo `giocatori_squadra_numero_giocatori`); trigger
(i permessi restano letti da `user_roles`, DD-011); `enforce_giocatori_squadra_update` con `tipo` bloccato e nome/cognome liberi solo sullo slot
- trigger `enforce_giocatori_squadra_update`: `tipo` scrivibile solo dall'admin; `nome` e del proprio allenatore; policy «L'allenatore aggiorna il proprio slot»; trigger
`cognome` scrivibili dall'allenatore sul proprio slot; `sincronizza_ruolo_allenatore` che aggiunge o toglie la riga `allenatore` in `user_roles` al
- policy di scrittura `eventi_app`: admin o allenatore; collegamento, scollegamento, disattivazione o cambio di tipo; policy `eventi_app` admin o
- `evento_permette_voto()`: escludere `tipo = allenatore`; allenatore; presenze, cacche e voti chiusi all'allenatore; promemoria evento aperti.
- cron promemoria evento: includere sempre gli allenatori attivi tra i destinatari. - **Client**: nell'app `numero` resta un `number` (0 per l'allenatore, NULL a database). I
2. **`src/lib/ruoli.ts`**: `useIsAllenatore()` e `usePuoGestireEventi()` (admin o allenatore), permessi (eventi, sollecito) leggono `user_roles` con `usePuoGestireEventi()`; cosa si vede
stessa lettura di `user_roles` e stessa cache. dipende dal tipo del proprio slot (`useSonoAllenatore()`), che è immediato.
3. **`/eventi`**: gate `usePuoGestireEventi()` al posto di `useIsAdmin()`, anche per il link di accesso. - **Admin**: «Aggiungi giocatore o allenatore» con il campo Tipo; per l'allenatore il form non
Il bottone del sollecito presenze usa lo stesso gate; lato server `sollecita-presenze.ts` chiede numero e ruolo. Il tipo si sceglie solo alla creazione.
passa da `richiediAdmin()` a un controllo "admin o allenatore" in `auth-route.server.ts`.
4. **`/profilo`**: tab e campi in base al tipo; nome e cognome modificabili per l'allenatore.
5. **`/squadra`**: l'allenatore compare in Rosa con la dicitura «Allenatore», senza numero né
statistiche; da allenatore non si vedono le tab Obiettivi e Badge, né il criterio e la riga
delle cacche.
**`/partita/$id`**: niente sezione badge né sondaggio cacche per l'allenatore.
6. **`profili-core.ts`**: completamento dedicato all'allenatore (solo dati personali).
7. **`/admin`**: scelta del tipo in "Aggiungi"; allenatori mostrati a parte, fuori dai conteggi
di completamento e tesseramento; nessun export CSI per loro.
8. **Filtro rosa** in tutti i punti elencati sopra.
9. **Test**: unit sul completamento e sul filtro rosa; integration in
`test/integration/permessi.test.ts` (l'allenatore scrive `eventi_app`, non vota, non cambia
il proprio `tipo`, non scrive slot altrui).
10. **Documentazione** alla consegna: `DATABASE.md` (colonna, enum, tabella permessi),
`profilo-giocatore.md`, `squadra.md`, `calendario.md`, `CHANGELOG.md`, `ROADMAP.md`, DD-034 → Accettata.
## Scelte confermate ## Scelte confermate