Documenta il ruolo allenatore (specifica, DD-034, roadmap)

L'allenatore è uno slot di giocatori_squadra con tipo diverso: registrato dall'admin
per email, gestisce gli eventi e sollecita le presenze, ha un profilo ridotto
(dati personali e foto, senza stagione e badge), compare in Rosa come «Allenatore»
e non vede badge né cacche. Solo documentazione, nessuna implementazione.

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:23:12 +02:00
1 parent e1e4ca0df6
commit dda4aff746
3 files changed
+278 -38

No files matched your search

+85 -38
View File
@@ -16,45 +16,46 @@ Serve a rispondere a domande del tipo:
**Accettate** **Accettate**
| ID | Titolo | | ID | Titolo |
| --------------------------------------------------------------------------------- | ------------------------------------- | | ------------------------------------------------------------------------------------------------ | ------------------------------------- |
| [DD-001](#dd-001--crapp-deve-restare-indipendente-da-lovable) | Indipendenza da Lovable | | [DD-001](#dd-001--crapp-deve-restare-indipendente-da-lovable) | Indipendenza da Lovable |
| [DD-002](#dd-002--sviluppo-document-first) | Sviluppo document-first | | [DD-002](#dd-002--sviluppo-document-first) | Sviluppo document-first |
| [DD-004](#dd-004--ogni-versione-aggiunge-non-riscrive) | Ogni versione aggiunge, non riscrive | | [DD-004](#dd-004--ogni-versione-aggiunge-non-riscrive) | Ogni versione aggiunge, non riscrive |
| [DD-005](#dd-005--mobile-first-pochi-click-pochi-schermi) | Mobile-first | | [DD-005](#dd-005--mobile-first-pochi-click-pochi-schermi) | Mobile-first |
| [DD-006](#dd-006--intelligenza-artificiale-solo-se-porta-beneficio-reale) | AI solo se utile | | [DD-006](#dd-006--intelligenza-artificiale-solo-se-porta-beneficio-reale) | AI solo se utile |
| [DD-007](#dd-007--badge-calcolati-dallapp-non-salvati-nel-database) | Badge calcolati, non in DB | | [DD-007](#dd-007--badge-calcolati-dallapp-non-salvati-nel-database) | Badge calcolati, non in DB |
| [DD-008](#dd-008--gamification-equa-tra-ruoli) | Gamification equa tra ruoli | | [DD-008](#dd-008--gamification-equa-tra-ruoli) | Gamification equa tra ruoli |
| [DD-009](#dd-009--tesseramento-csi-manuale-integrazione-api-in-una-fase-successiva) | CSI manuale, poi integrazione API | | [DD-009](#dd-009--tesseramento-csi-manuale-integrazione-api-in-una-fase-successiva) | CSI manuale, poi integrazione API |
| [DD-010](#dd-010--profilo-giocatore-niente-storico-certificati) | Niente storico certificati | | [DD-010](#dd-010--profilo-giocatore-niente-storico-certificati) | Niente storico certificati |
| [DD-011](#dd-011--autenticazione-reale-prima-del-profilo-amministrativo-completo) | Auth reale prima del profilo | | [DD-011](#dd-011--autenticazione-reale-prima-del-profilo-amministrativo-completo) | Auth reale prima del profilo |
| [DD-012](#dd-012--rimandare-la-migrazione-degli-id-giocatore) | Rimandare la migrazione ID | | [DD-012](#dd-012--rimandare-la-migrazione-degli-id-giocatore) | Rimandare la migrazione ID |
| [DD-013](#dd-013--portabilità-lapp-non-deve-dipendere-da-servizi-esclusivi) | Portabilità dello stack | | [DD-013](#dd-013--portabilità-lapp-non-deve-dipendere-da-servizi-esclusivi) | Portabilità dello stack |
| [DD-015](#dd-015--rosa-anagrafica-da-codice-hardcoded-a-database) | Rosa da hardcoded a DB | | [DD-015](#dd-015--rosa-anagrafica-da-codice-hardcoded-a-database) | Rosa da hardcoded a DB |
| [DD-016](#dd-016--schema-dati-profilo-giocatore-f0) | Schema dati Profilo Giocatore | | [DD-016](#dd-016--schema-dati-profilo-giocatore-f0) | Schema dati Profilo Giocatore |
| [DD-017](#dd-017--lamministratore-può-compilare-i-dati-al-posto-del-giocatore) | L'admin scrive al posto del giocatore | | [DD-017](#dd-017--lamministratore-può-compilare-i-dati-al-posto-del-giocatore) | L'admin scrive al posto del giocatore |
| [DD-018](#dd-018--collegamento-automatico-giocatoreaccount-per-email) | Collegamento automatico per email | | [DD-018](#dd-018--collegamento-automatico-giocatoreaccount-per-email) | Collegamento automatico per email |
| [DD-019](#dd-019--il-branch-dei-commit-lo-decide-lutente) | Il branch lo decide l'utente | | [DD-019](#dd-019--il-branch-dei-commit-lo-decide-lutente) | Il branch lo decide l'utente |
| [DD-020](#dd-020--una-funzione-modificata-senza-test-non-è-finita) | Test obbligatori e verdi | | [DD-020](#dd-020--una-funzione-modificata-senza-test-non-è-finita) | Test obbligatori e verdi |
| [DD-021](#dd-021--molle-interrompibili-al-posto-delle-animazioni-a-durata-fissa) | Molle interrompibili con motion | | [DD-021](#dd-021--molle-interrompibili-al-posto-delle-animazioni-a-durata-fissa) | Molle interrompibili con motion |
| [DD-022](#dd-022--lapp-è-solo-chiara) | App solo chiara | | [DD-022](#dd-022--lapp-è-solo-chiara) | App solo chiara |
| [DD-023](#dd-023--ogni-scrittura-è-limitata-a-chi-la-fa) | Scritture limitate per ruolo | | [DD-023](#dd-023--ogni-scrittura-è-limitata-a-chi-la-fa) | Scritture limitate per ruolo |
| [DD-024](#dd-024--le-route-che-avvisano-la-squadra-chiedono-le-credenziali) | Route di notifica autenticate | | [DD-024](#dd-024--le-route-che-avvisano-la-squadra-chiedono-le-credenziali) | Route di notifica autenticate |
| [DD-025](#dd-025--il-promemoria-palloni-lo-manda-ladmin-per-un-evento) | Promemoria palloni manuale | | [DD-025](#dd-025--il-promemoria-palloni-lo-manda-ladmin-per-un-evento) | Promemoria palloni manuale |
| [DD-026](#dd-026--il-testo-della-notifica-viaggia-dentro-la-push) | Payload push cifrato | | [DD-026](#dd-026--il-testo-della-notifica-viaggia-dentro-la-push) | Payload push cifrato |
| [DD-027](#dd-027--chi-vota-deve-essere-convocato-non-solo-autenticato-come-sé-stesso) | Voto limitato ai convocati | | [DD-027](#dd-027--chi-vota-deve-essere-convocato-non-solo-autenticato-come-sé-stesso) | Voto limitato ai convocati |
| [DD-028](#dd-028--soglia-minima-di-campione-per-media-voto-e-mvp-in-home) | Soglia minima Media voto e MVP | | [DD-028](#dd-028--soglia-minima-di-campione-per-media-voto-e-mvp-in-home) | Soglia minima Media voto e MVP |
| [DD-029](#dd-029--cancellare-un-evento-pulisce-a-cascata-i-dati-collegati) | Pulizia a cascata evento cancellato | | [DD-029](#dd-029--cancellare-un-evento-pulisce-a-cascata-i-dati-collegati) | Pulizia a cascata evento cancellato |
| [DD-030](#dd-030--centro-notifiche-in-app-storico-separato-dalla-push-letto-solo-con-rls) | Centro notifiche in-app | | [DD-030](#dd-030--centro-notifiche-in-app-storico-separato-dalla-push-letto-solo-con-rls) | Centro notifiche in-app |
| [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 |
**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**
@@ -1200,7 +1201,7 @@ modo per un giocatore di toglierle di torno per sempre.
- Notificare anche sulla sola creazione di un evento, non solo sull'avvicinarsi della data → - Notificare anche sulla sola creazione di un evento, non solo sull'avvicinarsi della data →
scartata su richiesta esplicita: conta quando l'evento sta per succedere, non il momento in scartata su richiesta esplicita: conta quando l'evento sta per succedere, non il momento in
cui è stato messo in calendario. La prima versione aveva un trigger `AFTER INSERT ON cui è stato messo in calendario. La prima versione aveva un trigger `AFTER INSERT ON
eventi_app` per questo, rimosso prima del rilascio. eventi_app` per questo, rimosso prima del rilascio.
- Una route API dedicata per leggere/segnare come lette (come le route push esistenti) → - Una route API dedicata per leggere/segnare come lette (come le route push esistenti) →
scartata: qui non serve la service role (niente da nascondere al giocatore stesso), quindi scartata: qui non serve la service role (niente da nascondere al giocatore stesso), quindi
la RLS basta ed evita di duplicare la logica di lettura già scritta lato client per gli la RLS basta ed evita di duplicare la logica di lettura già scritta lato client per gli
@@ -1242,7 +1243,7 @@ In "Squadra" la data di nascita mostrava **"Invalid Date"** per chi l'aveva inse
solo dal proprio Profilo. Causa: due fonti mai collegate. I 17 giocatori del seed storico solo dal proprio Profilo. Causa: due fonti mai collegate. I 17 giocatori del seed storico
hanno la nascita in `nascitaPerId`, una mappa statica pubblica bundlata nel client hanno la nascita in `nascitaPerId`, una mappa statica pubblica bundlata nel client
(`src/lib/crapp-data.ts`) — il commento sulla riga la descriveva esplicitamente come (`src/lib/crapp-data.ts`) — il commento sulla riga la descriveva esplicitamente come
stopgap: *"`giocatori_squadra` non ha ancora questa colonna (DD-015 follow-up)"*. Un stopgap: _"`giocatori_squadra` non ha ancora questa colonna (DD-015 follow-up)"_. Un
giocatore aggiunto dopo la migrazione DD-015 inserisce invece la nascita dal form Profilo, giocatore aggiunto dopo la migrazione DD-015 inserisce invece la nascita dal form Profilo,
che scrive su `profili_giocatore.data_nascita` — tabella con RLS "solo il proprio profilo o che scrive su `profili_giocatore.data_nascita` — tabella con RLS "solo il proprio profilo o
admin" (M2), mai letta da `useRosa()`/`useAnagraficaRosa()` (`src/lib/rosa.ts`), e comunque admin" (M2), mai letta da `useRosa()`/`useAnagraficaRosa()` (`src/lib/rosa.ts`), e comunque
@@ -1309,7 +1310,7 @@ per questa colonna.
**Aggiornamento (15 settembre 2026, stesso giorno)** — dopo aver applicato M18 in produzione **Aggiornamento (15 settembre 2026, stesso giorno)** — dopo aver applicato M18 in produzione
(`supabase db push`), la nascita in Squadra compariva solo per i 17 giocatori storici, non (`supabase db push`), la nascita in Squadra compariva solo per i 17 giocatori storici, non
per chi aveva già compilato il proprio profilo prima della migration: il trigger di sync per chi aveva già compilato il proprio profilo prima della migration: il trigger di sync
copre solo le scritture *future* su `profili_giocatore`, non retroattivamente, e M18 copre solo le scritture _future_ su `profili_giocatore`, non retroattivamente, e M18
backfillava a mano solo il seed storico. Migration `m19_backfill_nascita_da_profili_esistenti` backfillava a mano solo il seed storico. Migration `m19_backfill_nascita_da_profili_esistenti`
allinea una tantum `giocatori_squadra.nascita` a `profili_giocatore.data_nascita` per tutti i allinea una tantum `giocatori_squadra.nascita` a `profili_giocatore.data_nascita` per tutti i
profili già esistenti, con la stessa disattivazione temporanea del trigger di sicurezza usata profili già esistenti, con la stessa disattivazione temporanea del trigger di sicurezza usata
@@ -1399,3 +1400,49 @@ toccando «Cambia», per spostare un evento in un altro giorno.
**Riesame** **Riesame**
Se gli eventi crescono tanto da rendere scomodo lo scorrere dei mesi, valutare una ricerca Se gli eventi crescono tanto da rendere scomodo lo scorrere dei mesi, valutare una ricerca
per titolo invece di reintrodurre la lista completa. per titolo invece di reintrodurre la lista completa.
### DD-034 — L'allenatore è uno slot della squadra con tipo diverso, non un giocatore
**Data:** 24 settembre 2026
**Stato:** In valutazione
**Contesto**
Serve un account per l'allenatore: deve gestire gli eventi (oggi solo admin, `m11`) ma non è
un giocatore. Non ha statistiche, badge né stagione, non vota e non viene votato. L'admin
vuole registrarlo come i giocatori, inserendo la sua email Gmail. Specifica completa in
[modules/allenatore.md](modules/allenatore.md).
**Decisione**
L'allenatore è una riga di `giocatori_squadra` con una nuova colonna `tipo = 'allenatore'`
(default `'giocatore'`). Così riusa senza cambiarli il collegamento per email (DD-018),
`profili_giocatore` per i dati personali, il bucket avatar e il compleanno sincronizzato
(DD-031). Il permesso di gestire gli eventi arriva da un nuovo valore `allenatore` di
`app_role` in `user_roles` (DD-011), scritto da una funzione `SECURITY DEFINER` quando si
collega uno slot di tipo allenatore: è quindi l'admin a concederlo, registrando lo slot.
"Rosa" diventa `attivo e tipo = 'giocatore'` ovunque.
**Alternative scartate**
- Una tabella `staff` separata → isola l'allenatore da voti e rosa senza toccare i filtri, ma
duplica collegamento per email, profilo, avatar e form admin. Resta l'alternativa se i
filtri sulla rosa si rivelano troppi o fragili.
- Registrare l'allenatore come admin → gli darebbe dati personali e documenti di tutta la
squadra (DD-017), molto più del necessario.
- Distinguere l'allenatore dal campo `ruolo` (ruolo in campo) → mescola due concetti e il
campo è testo libero.
**Conseguenze**
- Ogni lettura della rosa (client, server, SQL di voto e promemoria) va filtrata per `tipo`:
un punto dimenticato rende l'allenatore votabile o lo conta tra i convocati. L'elenco sta
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
l'allenatore sul proprio slot.
- 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`;
riceve i promemoria degli eventi e può sollecitare le presenze, non il turno palloni.
- Non vede badge né classifiche o sondaggi sulle cacche, in nessuna schermata.
**Riesame**
Se arriva un secondo tipo di staff (dirigente, segreteria) o se i filtri sulla rosa diventano
fonte di bug, passare alla tabella `staff` separata.
+2
View File
@@ -44,6 +44,8 @@ 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
+191
View File
@@ -0,0 +1,191 @@
# Modulo — Ruolo Allenatore
**Stato:** da implementare (specifica)
**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`,
`src/routes/profilo.tsx`, `src/routes/eventi.tsx`, `src/routes/admin.tsx`,
`src/lib/profili-core.ts`, i punti che leggono la rosa (vedi [Esclusione dalla rosa](#esclusione-dalla-rosa))
## Obiettivo
Dare accesso all'app all'allenatore, con permessi diversi da quelli di un giocatore: gestisce
gli eventi (allenamenti, partite, amichevoli) e sollecita le presenze, ma non gioca: compare
in Squadra come membro con la dicitura «Allenatore», però non è convocabile, non vota, non
riceve voti e non ha statistiche, badge o stagione.
## Registrazione
Stesso flusso dei giocatori (DD-018), senza passaggi in più:
1. L'admin, da `/admin` → **Aggiungi**, sceglie il tipo **Allenatore** invece di Giocatore e
inserisce nome, cognome ed email Gmail. Numero di maglia e ruolo in campo non si chiedono.
2. L'allenatore accede con Google; l'email corrisponde allo slot e il collegamento è
automatico. Se l'email non corrisponde, stesso messaggio di errore dei giocatori.
3. Entra in Home.
Il permesso nasce dalla registrazione fatta dall'admin: l'allenatore non può
auto-promuoversi, perché il tipo dello slot lo scrive solo un admin (come oggi numero e ruolo).
## Profilo
Il profilo dell'allenatore ha solo due tab (in `TAB_PROFILO` oggi sono quattro):
| Tab | Giocatore | Allenatore |
| ------------ | --------- | ---------------------------------------------- |
| Stagione | sì | **no** |
| Badge | sì | **no** |
| Documenti | sì | sì, ridotta (vedi sotto); tab iniziale |
| Impostazioni | sì | sì, identica (logout, notifiche, segnalazioni) |
Un link diretto a `/profilo?tab=stagione` o `?tab=badge` apre Documenti.
### Dati modificabili dall'allenatore
| Campo | Dove sta oggi | Nota |
| ---------------- | --------------------------------- | ---------------------------------------------------------------------------------------------- |
| Nome, cognome | `giocatori_squadra` | per un giocatore li scrive solo l'admin; per l'allenatore anche lui sul proprio slot |
| Email (contatto) | `profili_giocatore.email` | non è l'email di login, che resta quella registrata dall'admin |
| Data di nascita | `profili_giocatore.data_nascita` | sincronizzata in `giocatori_squadra.nascita` come per tutti (DD-031): compleanno in Calendario |
| Luogo di nascita | `profili_giocatore.luogo_nascita` | |
| Telefono | `profili_giocatore.telefono` | |
| Foto profilo | bucket `avatar-giocatori` | stesso cerchio avatar e stesso upload dei giocatori |
**Non** si mostrano: indirizzo di residenza, documento d'identità, certificato medico, foto
tessera, tesseramento CSI.
### Completamento profilo
Per l'allenatore il completamento conta solo i dati personali della tabella sopra (100%).
Il widget in Home segue la stessa regola: sparisce quando quei campi sono compilati.
## Squadra
L'allenatore compare nella tab **Rosa** insieme ai giocatori, con foto, nome e cognome. Dove
per un giocatore c'è il ruolo in campo (palleggiatore, schiacciatore…) per lui c'è scritto
**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.
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.
| Tab | Giocatore | Allenatore |
| --------- | --------- | ---------- |
| Rosa | sì | sì |
| Stats | sì | sì |
| Obiettivi | sì | **no** |
| Badge | sì | **no** |
## Badge e cacche nascosti
L'allenatore non vede **nessun badge** e **nessuna classifica o dato sulle cacche**, in
nessuna schermata:
| Dove | Cosa si nasconde all'allenatore |
| ----------------------------------- | -------------------------------------------------------------------------------- |
| `/squadra` → Stats | il criterio di ordinamento «Cacche» (`cacchePartita`) |
| `/squadra` → Rosa, scheda giocatore | la riga «Cacche/partita 💩» |
| `/partita/$id` | la sezione «Badge votati dai compagni» e il sondaggio cacche (`SondaggioCacche`) |
| `/profilo`, `/squadra` | le tab Badge (già escluse sopra) |
Presenze, media voto, MVP e palloni restano visibili.
## Gestione eventi
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
"solo admin" a "admin o allenatore" (oggi `m11_scritture_per_ruolo`); la cancellazione a
cascata (DD-029) resta invariata.
Il link a `/eventi` compare dove oggi compare per l'admin.
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
arriva solo ai giocatori che non hanno risposto, mai all'allenatore stesso.
**Restano solo admin:** dashboard `/admin`, dati e documenti dei giocatori, export CSI,
notifica personalizzata, promemoria palloni, apertura sondaggio cacche, correzione delle
risposte presenze altrui, gestione dei ruoli.
## Notifiche
L'allenatore riceve i promemoria degli eventi (24h e 3h prima, push e centro notifiche
in-app) come i giocatori, anche se non è convocato, e può attivare le push dal proprio
Profilo. Non riceve il sollecito presenze né il turno palloni, che riguardano chi gioca.
## Esclusione dalla rosa
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
eccezione la tab Rosa di Squadra, i compleanni del Calendario e i destinatari dei promemoria
evento, che lo includono. Punti da coprire, da verificare uno per uno in implementazione:
- client: `useRosa()`, `useGiocatoriSquadra()` (dove usata come rosa), `useAnagraficaRosa()`
— Squadra (Stats, Obiettivi, Badge), Presenze, Palloni, Pagelle, MVP, Badge, Classifica,
Scout, form convocati di `/eventi`;
- server e condivisi: `leggiGiocatoriSquadra()`, `convocatiEvento()` (`eventi.ts`, riceve la
rosa già filtrata), route API che notificano "tutta la rosa";
- database: `evento_permette_voto()` (M13), che con `convocati` vuoto intende "tutta la rosa
attiva"; i cron dei promemoria evento (M17) invece aggiungono sempre l'allenatore ai
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
giocatore).
## Permessi (riepilogo)
| Azione | Giocatore | Allenatore | Admin |
| ----------------------------------------------- | --------- | ------------------ | ------------------ |
| Modificare nome e cognome | no | sì, i propri | tutti |
| Dati personali e foto profilo | i propri | i propri (ridotti) | tutti (non i file) |
| Documento, certificato, foto tessera | i propri | — | lettura/download |
| Creare/modificare/eliminare eventi, convocati | no | sì | sì |
| Sollecitare le presenze | no | sì | sì |
| Promemoria palloni, notifica personalizzata | no | no | sì |
| Rispondere alle presenze, votare, essere votato | sì | no | sì (se giocatore) |
| Tab Stagione e Badge del profilo | sì | no | sì |
| Tab Obiettivi e Badge di Squadra | sì | no | sì |
| Badge e classifiche/dati sulle cacche | sì | no | sì |
| Dashboard `/admin` | no | no | sì |
## Cosa implementare
1. **Migration** (nuova, non si riscrivono le esistenti):
- `giocatori_squadra.tipo` (`giocatore` | `allenatore`, default `giocatore`, non nullo);
- valore `allenatore` nell'enum `app_role`, assegnato in `user_roles` da una funzione
`SECURITY DEFINER` al momento del collegamento di uno slot con `tipo = allenatore`
(i permessi restano letti da `user_roles`, DD-011);
- trigger `enforce_giocatori_squadra_update`: `tipo` scrivibile solo dall'admin; `nome` e
`cognome` scrivibili dall'allenatore sul proprio slot;
- policy di scrittura `eventi_app`: admin o allenatore;
- `evento_permette_voto()`: escludere `tipo = allenatore`;
- cron promemoria evento: includere sempre gli allenatori attivi tra i destinatari.
2. **`src/lib/ruoli.ts`**: `useIsAllenatore()` e `usePuoGestireEventi()` (admin o allenatore),
stessa lettura di `user_roles` e stessa cache.
3. **`/eventi`**: gate `usePuoGestireEventi()` al posto di `useIsAdmin()`, anche per il link di accesso.
Il bottone del sollecito presenze usa lo stesso gate; lato server `sollecita-presenze.ts`
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
- Riceve i promemoria degli eventi: sì.
- Sollecita le presenze: sì. Promemoria palloni: no.
- Badge e cacche: mai visibili all'allenatore, né classifiche né sondaggio.
- In Squadra: nessuna sezione Staff; compare in Rosa come gli altri, con «Allenatore» al
posto del ruolo.
- Vede le risposte presenze e le pagelle dei giocatori: sì, la lettura è già aperta a tutti
gli autenticati.