- Modello: tipo sugli slot, inRosa() al posto del filtro su attivo in tutte le letture della rosa di gioco (presenze, convocati, voti, palloni, classifiche, badge, sollecito); gli allenatori restano solo nei compleanni e in Squadra. - Permessi: user_roles letto una volta per tutti i ruoli; usePuoGestireEventi() apre /eventi e il sollecito presenze anche all'allenatore, lato server con richiediGestoreEventi(). Palloni, sondaggio e messaggi restano all'admin. - Identità: useGiocatoreInCampo() è null per l'allenatore, così non risponde alle presenze e non vota; useSonoAllenatore() decide cosa si vede. - Profilo: per l'allenatore solo Docs (nome, cognome, nascita, telefono, email) e Opzioni; card avatar e tab Opzioni estratte per riusarle. - Home, Squadra e partita: niente statistiche personali, badge, cacche e obiettivi per l'allenatore; in Rosa compare con la dicitura «Allenatore». - Admin: tipo in «Aggiungi», allenatori a parte in Profili, fuori da conteggi ed export CSI. Test unit su rosa di gioco e completamento, route test sull'allenatore. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
6.7 KiB
Modulo — Squadra
Stato: implementato
File principali: src/lib/giocatori-squadra.ts, src/lib/giocatori-squadra.server.ts,
src/lib/rosa.ts, src/routes/squadra.tsx, src/routes/admin.tsx (sezione rosa)
Test: test/unit/giocatori-squadra.test.ts, test/unit/rosa.test.ts
Obiettivo
Tenere l'anagrafica della rosa (nome, numero di maglia, ruolo, chi è collegato a quale
account) in un unico posto — giocatori_squadra — e farla usare a tutte le schermate che
hanno bisogno di sapere "chi c'è in squadra", invece di ciascuna avere la propria copia.
Prima di DD-015
la lista viveva hardcoded in src/lib/crapp-data.ts: aggiungere o disattivare un
giocatore dalla dashboard admin non aveva alcun effetto sul resto dell'app.
Due letture diverse, per non pagare due volte lo stesso costo
useAnagraficaRosa()(rosa.ts) — solo id, nome, ruolo, numero, data di nascita dei giocatoriattivo. Serve dove basta sapere chi c'è, es. i compleanni nel Calendario o le liste presenze: non monta gli hook di MVP/pagelle/palloni/infortuni. La data di nascita viene dagiocatori_squadra.nascita(migrationm18_nascita_pubblica_giocatori_squadra, DD-031): pubblica a tutta la rosa, sincronizzata automaticamente da un trigger quando il giocatore (o un admin per lui) la salva dal Profilo — vediprofilo-giocatore.md.nullfinché non è stata inserita da nessuno.useRosa()(rosa.ts) — la stessa anagrafica arricchita con tutte le statistiche personali calcolate a runtime: presenze, partite giocate, serie (presenze, allenamenti, partite, conferme, palloni), MVP vinti, media voto pagelle, palloni, cacche, infortuni, ritardi. Non fa query aggiuntive: combina in unuseMemole cache già in memoria dimvp-voti.ts,pagelle.ts,cacche.ts,palloni.ts,infortuni.ts,presenze.ts,eventi.ts— la spec di ciascuna di queste statistiche sta nel modulo relativo (mvp.md,pagelle.md,palloni.md,infortuni.md,presenze.md).useRosa()è anche la base diuseIo()(il giocatore sul dispositivo corrente) euseObiettivi()(obiettivi-squadra.md).
Entrambe filtrano solo i giocatori attivo: chi ha lasciato la squadra resta nel database
(presenze, voti, pagelle e badge della stagione restano agganciati al suo id) ma sparisce
dagli elenchi correnti. Il filtro è inRosa() (giocatori-squadra.ts), che esclude anche
gli allenatori (tipo = 'allenatore', DD-034): hanno uno slot ma non giocano.
useAnagraficaRosa({ conAllenatori: true }) li include, per i compleanni.
Allenatori in Squadra
Nella tab Rosa gli allenatori attivi compaiono in cima, con foto, nome e la dicitura
«Allenatore» dove i giocatori hanno il ruolo in campo (ruoloVisibile()); niente numero,
statistiche né badge, e niente posto nella tab Stats. Chi usa l'app da allenatore vede solo
Rosa e Stats, senza badge e senza cacche (criterio «Cacche» e riga «Cacche/partita»). La
specifica completa sta in allenatore.md.
Gestione dati squadra (solo amministratore)
Da /admin un amministratore può (DD-017):
| Azione | Hook | Effetto |
|---|---|---|
| Modificare dati squadra | useSalvaDatiSquadra() |
Nome, cognome, numero, ruolo, email (usata per il collegamento automatico, non il dato personale del profilo) |
| Aggiungere un giocatore | useAggiungiGiocatore() |
Nuova riga con id progressivo g<N> (prossimoIdGiocatore()), non generato dal database; con tipo «Allenatore» senza numero né ruolo (DD-034) |
| Attivare/disattivare | useImpostaAttivo() |
Non elimina la riga: la storia della stagione resta intatta |
| Scollegare un account | useScollegaAccount() |
Libera uno slot collegato per errore (DD-016 regola 2); il giocatore si ricollega al primo accesso successivo |
| Registrare il tesseramento CSI | useSalvaTesseramento() |
Numero e data tessera, note solo dopo il tesseramento effettivo (vedi profilo-giocatore.md) |
Il collegamento giocatore↔account, invece, non è manuale: avviene in automatico al primo
accesso con Google, per corrispondenza email
(DD-018).
useCollegaGiocatore() esiste per completare quel flusso, non per una scelta libera
dell'admin.
Le regole di validazione (validaDatiSquadra(), numeroGiaUsato()) rispecchiano i vincoli
della tabella (numero maglia univoco tra gli attivi, campi obbligatori): l'obiettivo è
mostrare un messaggio leggibile invece di far arrivare un errore Postgres grezzo
all'amministratore.
Classifica interna di Squadra
La tab "Stats" di /squadra mostra una classifica interna ordinabile per 5 criteri
(CriterioClassifica in rosa.ts): presenze, media voto, MVP, palloni, cacche/partita.
classificaRank() calcola un "dense rank" (a parità di valore stessa posizione, il
successivo non salta — 1, 1, 2, non 1, 1, 3); dettaglioClassifica() sceglie quale
sottostatistica mostrare sotto il nome, coerente col criterio selezionato (es. "voti
pagella" per il criterio media voto, non sempre "presenze consecutive").
Le altre tab di /squadra (Rosa, Obiettivi, Badge) sono viste diverse sugli stessi dati di
useRosa()/useObiettivi()/badges.ts: non introducono altra logica di dominio, solo
presentazione — le rispettive specifiche stanno in badge.md e obiettivi-squadra.md.
Limiti noti
src/lib/crapp-data.tsresta come fallback, non più come fonte viva: se il database non risponde o non è ancora popolato,rosaFallback()genera una rosa di riserva dai dati statici storici. Un ambiente nuovo senza dati ingiocatori_squadramostra quindi comunque una squadra, non una schermata vuota — ma è la rosa 2025/26 hardcoded, non quella reale.