diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index 643c901..7fce601 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -1,6 +1,6 @@ # Project State -Ultimo aggiornamento: 06/09/2026 +Ultimo aggiornamento: 09/09/2026 ## Stato generale @@ -10,7 +10,9 @@ Backend migrato al nuovo Supabase proprietario. Autenticazione Google, dashboard amministratore e Profilo Giocatore (lato giocatore e lato admin) sono in produzione su `main`. Foto profilo (M6) e Scout Live (M7) non dipendono più da `localStorage`: entrambi ora sincronizzano tra dispositivi tramite Supabase. Le serie di presenze sono calcolate sui dati -reali (M9). +reali (M9). Prima versione pre-release rilasciata (0.9.0, vedi `docs/CHANGELOG.md`). Cancellare +un evento pulisce ora a cascata tutte le tabelle collegate (M14) e le righe orfane da +cancellazioni precedenti a M14 sono state bonificate una tantum (M15/M16). --- @@ -21,7 +23,8 @@ reali (M9). - Cursor e Claude Code come ambienti di sviluppo - Vercel configurato; Environment Variables aggiornate al nuovo Supabase (Preview e Production) - Supabase proprietario attivo — Project Ref: `kfkcldwncxqaixetsjes` -- 23 migration in `supabase/migrations/`, fino a `m12_niente_autovoto` +- 27 migration in `supabase/migrations/`, fino a `m16_funzione_bonifica_dati_evento_orfani` + (09/09/2026) - Sviluppo locale verificato con il nuovo Supabase --- @@ -36,8 +39,9 @@ reali (M9). ## Database -- Schema v1.0 e migration da M1 a M12 applicate al nuovo Supabase (`m12_niente_autovoto` - in produzione dal 06/09/2026, verificata con `npx supabase migration list`) +- Schema v1.0 e migration da M1 a M16 applicate al nuovo Supabase + (`m16_funzione_bonifica_dati_evento_orfani` in produzione dal 09/09/2026, verificata con + `npx supabase migration list`) - `public.giocatori_squadra`: rosa iniziale di 17 giocatori (migration `m5_email_giocatori_squadra`) più quelli aggiunti da `/admin` a stagione in corso; da settembre 2026 tutti i giocatori attivi hanno l'email registrata (colonna `email`, DD-018), impostabile da `/admin` senza @@ -61,6 +65,19 @@ reali (M9). scoutate concluse, e collegamento della tabella `scout_sessioni` (già presente nello schema ma mai usata) al blocco condiviso dello Scout Live — prima entrambi vivevano solo in `localStorage`, quindi visibili a un solo dispositivo +- Migration `m13_convocati_e_pagelle_chiuse`: le RLS di `mvp_voti`/`pagelle_voti`/ + `badge_social_voti` richiedono che votante e votato siano convocati all'evento (e per le + pagelle anche `pagelle_chiuse = false`) +- Migration `m14_pulizia_dati_evento_cancellato` (DD-029): cancellare un evento pulisce a + cascata, tramite trigger, tutte le tabelle collegate (`risposte_presenze`, + `cacche_partita`, `mvp_voti`, `pagelle_voti`, `badge_social_voti`, `turni_palloni`, + `scout_sessioni`, `scout_live`, `scout_partite`) +- Migration `m15_bonifica_dati_evento_orfani`: bonifica una tantum delle righe orfane da + cancellazioni di eventi precedenti a M14, senza toccare i vecchi voti MVP/pagelle/badge + social legati a id Scout o CSI +- Migration `m16_funzione_bonifica_dati_evento_orfani`: la stessa logica di bonifica di M15 + resta richiamabile come funzione `bonifica_dati_evento_orfani()` (riservata al service + role), se mai servisse di nuovo --- @@ -68,13 +85,18 @@ reali (M9). - Squadra - Presenze -- Badge +- Badge (incluso badge social) - Scout Live (blocco e archivio partite sincronizzati tra dispositivi, migration `m7_scout_partite`) - Pagelle - MVP +- Obiettivi di squadra +- Turno palloni (specifica in `docs/modules/palloni.md`) +- Infortuni, in forma minima (specifica in `docs/modules/infortuni.md`) - Notifiche - Profilo Giocatore (specifica in `docs/modules/profilo-giocatore.md`) - Serie di presenze (specifica in `docs/modules/serie-presenze.md`) +- Collegamento CSI: classifica campionato e Coppa, storico e dettaglio partita — formazioni, + scontri diretti (specifica in `docs/modules/collegamento-csi.md`) --- diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 47e04d1..c3b03b1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,128 +1,58 @@ # Changelog -Tutte le modifiche significative del progetto, dalla più recente. Il formato segue -[Keep a Changelog](https://keepachangelog.com/it/1.1.0/) e la numerazione -[Semantic Versioning](https://semver.org/lang/it/): il numero di versione è quello di -`package.json`. +Le modifiche rilevanti di CrAPP sono documentate qui, in ordine di rilascio. Il formato +segue [Keep a Changelog](https://keepachangelog.com/it/1.1.0/): ogni versione ha una data +e le voci sono divise per categoria (Aggiunto, Modificato, Sicurezza...). L'elenco +completo delle funzionalità, fatte e previste, sta in [ROADMAP.md](ROADMAP.md); qui si +registra solo _quando_ una voce è stata rilasciata e con quale versione. -L'elenco delle funzionalità disponibili e previste non si ripete qui: sta in -[ROADMAP.md](ROADMAP.md), che elenca il _cosa_ senza numeri di versione — quelli stanno solo -qui. +Le versioni sono sempre a tre cifre (`x.y.z`, mai `x.y`). Il progetto è pre-1.0 (`0.y.z`): +finché resta sotto `1.0.0` un aumento di `y` può includere anche cambi non compatibili +all'indietro. -## [Non rilasciato] — 0.9.0 +## [Non rilasciato] -Prima versione, pre-release. +## [0.9.0] - 2026-09-09 + +Prima versione pre-release: lo sviluppo precedente non era versionato a parte, quindi +questa release riunisce tutto ciò che l'app fa oggi in produzione. ### Aggiunto -- Login con Google tramite Supabase Auth e collegamento automatico dell'account al proprio - giocatore confrontando l'email (DD-011, DD-018; migration `m5_email_giocatori_squadra`): - senza sessione non si entra in nessuna schermata. -- Dashboard amministratore `/admin`: stato dei profili, download di documento, certificato e - foto tessera, export CSV per il tesseramento, aggiunta e disattivazione dei giocatori - (la riga non viene mai eliminata, così presenze, voti e badge restano agganciati al suo id). -- Profilo giocatore: dati anagrafici, documento, certificato medico e foto tessera con le - relative scadenze (migration `m2_profili_giocatore`, bucket privato), divisi nelle tab - Stagione, Documenti e Opzioni — [modules/profilo-giocatore.md](modules/profilo-giocatore.md). -- Tracciamento del tesseramento CSI: numero e data di tessera in `giocatori_squadra`, badge e - contatore in dashboard (migration `m8_tesseramento_csi`). -- Foto profilo condivise tra dispositivi tramite il bucket pubblico `avatar-giocatori` - (migration `m6_avatar_giocatori`). -- Serie di presenze calcolate sui dati reali (`serieConsecutiva()`), con la colonna - `risposto_il` che congela l'istante della **prima** risposta tramite trigger (migration - `m9_risposte_presenze_risposto_il`): sblocca la serie "Conferme 24h" e i badge "Risposta - lampo" e "Mai un forfait" — [modules/serie-presenze.md](modules/serie-presenze.md). -- Scout Live sincronizzato tra dispositivi: il blocco "chi sta scoutando" passa dalla tabella - `scout_sessioni` e le partite concluse vengono archiviate in `scout_partite` (migration - `m7_scout_partite`). Si apre dalla sezione «Scout live» di `/partita/$id`, solo il giorno - della partita, e può usarlo chiunque sia autenticato: uno per volta, grazie al lock. -- Votazione MVP legata all'evento CrAPP e non al referto CSI o allo Scout: si apre due ore - dopo `data`+`ora` della partita, anche senza risultato caricato — [modules/mvp.md](modules/mvp.md). -- Badge Pagellone: la media pagelle conta per il badge solo con almeno `VOTI_MINIMI_PAGELLA` - (5) voti ricevuti — prima un singolo voto poteva sbloccarlo o farlo sparire senza nessuna - significatività statistica — [modules/badge.md](modules/badge.md). -- Sondaggio pre-partita aperto dalle 8:00 del giorno della partita fino al fischio d'inizio - (poi resta chiuso, anche nei giorni successivi) e pulsante «Avvisa tutti del sondaggio» per - gli amministratori (`POST /api/public/apri-sondaggio`); nessun cron, l'invio è manuale. -- Turni palloni con rotazione automatica sulle partite e assegnazione manuale per gli - allenamenti, che restano «da assegnare» finché non si sceglie (migration M10). -- Notifiche push con il testo cifrato **dentro** la push (`aes128gcm`, RFC 8291), così - arrivano anche ad app chiusa e a schermo bloccato (DD-026). -- Collegamento CSI: classifica e risultati ufficiali letti dal portale Livescore CSI Bologna - (stagione 2025/26, Open Misto Eccellenza, Girone B) — - [modules/collegamento-csi.md](modules/collegamento-csi.md). -- Note dell'evento visibili in `/allenamento/$id` e `/partita/$id`, con gli a capo mantenuti. -- «Segnala un bug» e «Suggerisci una nuova funzionalità» in `/profilo`: due link che aprono - una issue GitHub sul template giusto, senza tabelle né schermate di gestione. -- Interfaccia accessibile: contrasto dei token colore sopra 4.5:1, `viewport-fit=cover` e - `theme-color` coerenti con un'app chiara, `lang="it"`, `:focus-visible` globale, tocchi da - 44px, `aria-current`/`aria-pressed`/`aria-controls`/`aria-busy`, niente testo sotto i 12px. -- Movimento con molle interrompibili di `motion` al posto delle `@keyframes` a durata fissa, - swipe fra i mesi del calendario e barre di progresso che misurano l'avanzamento tra un - traguardo e il successivo (DD-021). -- Suite di test in `test/` (unit, integration, end-to-end) eseguita con bun e senza nuove - dipendenze: `npm run test` e `npm run test:all`. -- Convenzioni interne: primitive condivise (`Card`, `Campo`, `classiInput` in `ui-bits`), - cache aggiornata con `setQueryData` invece di rileggere il database dopo ogni scrittura, e - logica pura estratta in `src/lib/` con i suoi test. -- Infrastruttura di sviluppo: migrazione da Lovable a sviluppo locale, repository GitHub - indipendente, deploy automatico su Vercel. - -### Modificato - -- In Squadra la classifica interna (filtro «Classifica per» + elenco) è sotto i 6 riquadri - di Statistiche; la tab «Classifica» è stata rimossa dalla barra delle sottosezioni. -- Tolto l'hint statico "+2 questo mese" dalla StatTile Presenze in home (sezione «Colpo - d'occhio»): mostrava un testo fisso, non un dato calcolato. -- La StatTile Media voto in home applica ora la stessa soglia minima di voti del badge - Pagellone (`VOTI_MINIMI_PAGELLA`): sotto soglia mostra `—` invece di una media poco - significativa (DD-028). -- L'MVP di una partita richiede ora un quorum minimo di 2 voti totali (`VOTI_MINIMI_MVP`) - oltre al margine netto già richiesto: un solo voto non assegna più la vittoria (DD-028). - Alcuni conteggi `mvp` già mostrati possono scendere per effetto della nuova regola. -- Cancellare un evento pulisce ora a cascata, tramite trigger database, tutte le tabelle - collegate (presenze, pagelle, MVP, badge social, turni palloni, scout) invece di lasciarle - come righe orfane (migration `m14_pulizia_dati_evento_cancellato`, DD-029). -- Bonificate una tantum le righe orfane lasciate da cancellazioni precedenti a M14 (migration - `m15_bonifica_dati_evento_orfani`), senza toccare i vecchi voti MVP/pagelle/badge social - legati a id Scout o CSI, che restano dati storici legittimi (DD-029). -- La bonifica sopra è ora anche una funzione richiamabile, `bonifica_dati_evento_orfani()` - (migration `m16_funzione_bonifica_dati_evento_orfani`, riservata al service role), coperta - da test di integrazione invece che verificata solo a mano (DD-029). - -### Corretto - -- Classifica interna di Squadra: a parità di valore (es. stesse presenze) i giocatori - condividono ora la stessa posizione invece di essere numerati in sequenza (`classificaRank` - in `src/lib/rosa.ts`); la corona di primo posto va a tutti i pari merito in testa, non solo - al primo dell'elenco. -- Il sottotitolo di ogni riga della classifica interna di Squadra mostrava sempre le - "presenze consecutive" anche ordinando per Media voto, MVP, Palloni o Cacche, un dato - scollegato dal criterio scelto: ora segue il criterio selezionato (`dettaglioClassifica` in - `src/lib/rosa.ts`). Per Palloni mostra le volte consecutive in cui il giocatore li ha - portati (nuovo campo `Giocatore.seriePalloni`, calcolato da `serieConsecutivaPalloni` in - `src/lib/palloni-core.ts`), non più le presenze. Per MVP mostra le partite giocate — solo - partite, non più allenamenti compresi (nuovo campo `Giocatore.partiteGiocate`, da - `contaPartiteGiocate()` in `src/lib/presenze.ts`). +- **Gestione squadra** — rosa dei giocatori con ruoli e dati anagrafici di base. +- **Profilo Giocatore** — dati personali e amministrativi, documento d'identità, + certificato medico (caricamento, scadenza, stato, download) e foto tessera in + un'unica schermata, sia lato giocatore sia lato amministratore; lo storico dei + certificati resta un'estensione futura. +- **Gestione tesseramenti CSI** — raccolta dei dati richiesti dal CSI, tracciamento di chi + è già tesserato (numero e data tessera) ed export CSV per il tesseramento. +- **Calendario** — eventi di allenamento e partita, con schermata di dettaglio dedicata. +- **Presenze** — conferma o rifiuto della partecipazione a un evento, visibile a tutta la + squadra al posto di chat e fogli condivisi. +- **Serie di presenze** — tre serie (presenze, conferme, allenamenti) calcolate sui dati + reali della rosa. +- **Scout Live** — un solo referente alla volta registra in tempo reale le azioni di gioco + durante la partita. +- **Badge** — gamification con gradi bronzo/argento/oro, badge segreti e badge social + votati tra compagni. +- **Pagelle** — voto tra compagni (1-10) a fine partita, con media personale e di squadra. +- **Votazione MVP** — elezione del migliore in campo della partita tramite voto tra + compagni, un voto a testa. +- **Obiettivi di squadra** — traguardi collettivi che avanzano con presenze, risposte alle + convocazioni, pagelle e risultati di campionato. +- **Turno palloni** — rotazione condivisa e promemoria di chi porta e riporta i palloni ad + allenamenti e partite. +- **Notifiche push** — promemoria intelligenti su un unico opt-in per dispositivo. +- **Dashboard amministratore** — vista aggregata su tesseramenti, certificati, presenze e + dati della rosa, con download CSV. +- **Collegamento CSI** — classifica di campionato e Coppa, storico partite e dettaglio di + ogni gara (formazioni, storico scontri diretti, probabilità di vittoria calcolata dal + CSI) letti in tempo reale dal portale ufficiale Livescore CSI Bologna, senza inserimento + manuale da parte degli amministratori. +- **Infortuni** — conteggio degli eventi saltati per infortunio, riusando lo stato di + presenza già registrato per le convocazioni. ### Sicurezza -- Migration `m4_solo_autenticati`: tolto al ruolo `anon` l'accesso alle tabelle dell'app - (applicata in produzione il 03/09/2026). -- I permessi di amministrazione arrivano solo da `user_roles` (`src/lib/ruoli.ts`): senza, - basterebbe scegliere il nome giusto per amministrare. -- Migration `m11_scritture_per_ruolo`: ogni voto è firmato con lo slot collegato all'account - (DD-023); il collegamento account → giocatore non è modificabile dal giocatore stesso - (DD-016). -- Migration `m12_niente_autovoto`: i vincoli `mvp_no_autovoto` e `badge_social_no_autovoto` - rifiutano l'auto-voto anche a chi scrive direttamente su PostgREST, come già faceva - `pagelle_voti`. -- Al voto MVP partecipano solo i presenti (o in ritardo) di quell'evento; il filtro è - applicativo, non RLS ([modules/mvp.md](modules/mvp.md)). -- Migration `m13_convocati_e_pagelle_chiuse` (DD-027): la policy di M11 su `pagelle_voti`, - `mvp_voti` e `badge_social_voti` verifica ora anche che votante e votato siano tra i - convocati dell'evento, e per le sole pagelle che `pagelle_chiuse` sia falso — prima erano - filtri solo applicativi, aggirabili scrivendo direttamente su PostgREST. -- La suite copre i rifiuti `401` di `richiediAdmin` (DD-024), i permessi di - `badge_social_voti` e le deroghe admin di M11, e verifica che un ripensamento non riscriva - `risposto_il` (trigger di `m9`). +- Autenticazione tramite Google via Supabase Auth, unico metodo di accesso; permessi + differenziati per ruolo (giocatore/amministratore) su tabelle e route. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 9fff202..39cceef 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -6,7 +6,7 @@ il _cosa_: `CHANGELOG.md` registra _quando_ una voce è stata rilasciata e con q ## Fatto -Tutto quello che è in `main` e finirà nella prima release. +Tutto quello che è in `main`, rilasciato in versione 0.9.0 (vedi `CHANGELOG.md`). - [x] Gestione squadra - [x] Calendario @@ -16,17 +16,23 @@ Tutto quello che è in `main` e finirà nella prima release. - [x] Badge - [x] Badge social - [x] Pagelle +- [x] Votazione MVP - [x] Obiettivi di squadra +- [x] Turno palloni +- [x] Infortuni — conteggio eventi saltati, in forma minima - [x] Notifiche Push (promemoria intelligenti) - [x] Dashboard amministratore - [x] Download CSV dati -- [x] Certificati medici — caricamento, scadenza, stato e download; lo storico dei - certificati resta un'estensione futura +- [x] Profilo Giocatore — dati personali, documento d'identità, certificato medico + (caricamento, scadenza, stato, download) e foto tessera; lo storico dei certificati + resta un'estensione futura - [x] Gestione tesseramenti CSI — raccolta dati, export CSV e tracciamento di chi è già tesserato (numero e data di tessera) - [x] Collegamento CSI (stagione 2025/26) -- [x] Classifica automatica +- [x] Classifica automatica (campionato e Coppa) - [x] Risultati campionato +- [x] Dettaglio partita — formazioni, storico scontri diretti e probabilità di vittoria + calcolata dal CSI ## Prossimo diff --git a/docs/modules/calendario.md b/docs/modules/calendario.md new file mode 100644 index 0000000..dc23963 --- /dev/null +++ b/docs/modules/calendario.md @@ -0,0 +1,60 @@ +# Modulo — Calendario ed Eventi + +**Stato:** implementato +**File principali:** `src/lib/eventi.ts`, `src/lib/eventi.server.ts`, `src/routes/calendario.tsx` +(vista mensile, tutti), `src/routes/eventi.tsx` (creazione/modifica, solo admin), +`src/components/crapp/EventoCard.tsx` (card condivisa) +**Test:** `test/unit/eventi.test.ts` + +--- + +## Obiettivo + +Un unico calendario condiviso per allenamenti, partite, amichevoli ed eventi extra +(riunioni, cene di squadra...), al posto di messaggi sparsi in chat. Ogni evento in +`eventi_app` diventa il punto a cui si agganciano presenze, convocazioni, MVP, pagelle, +scout e turno palloni — la maggior parte degli altri moduli dipende da un `evento.id`. + +## Due schermate, due pubblici + +- **`/calendario`** — vista mensile per tutta la squadra, sola lettura. Mostra allenamenti, + partite, eventi ed **eventi virtuali** per i compleanni della rosa (`compleanniEventi()` + in `eventi.ts`, generati a runtime dall'anagrafica di `useAnagraficaRosa()`, non righe + vere di `eventi_app`): la spunta della vista `giorniIT`/`mesiIT` colora la cella per tipo + di evento, i giorni con più eventi si dividono lo spazio. +- **`/eventi`** — "Gestione eventi", riservata agli amministratori (`useIsAdmin()`): crea, + modifica ed elimina un evento, sceglie i convocati (`convocatiEvento()`, vuoto = tutta la + rosa). Da qui si distingue "partita" da "amichevole" tramite il flag `campionato` + (`categoriaEvento()`/`daCategoria()` in `eventi.ts` convertono tra la categoria mostrata + in interfaccia e la coppia `{ tipo, campionato }` salvata nel database). + +Entrambe leggono la stessa cache (`useEventi()`, `EVENTI_KEY`, `staleTime` 10 minuti: il +calendario cambia raramente). `EventoCard.tsx` è la card riusata da entrambe le schermate; +`linkPerEvento()` decide dove porta il click — `/partita/$id` per una partita (con +`/partita-csi/$id` come alternativa "solo CSI" quando non c'è un evento collegato, vedi +`collegamento-csi.md`), `/allenamento/$id` per un allenamento, nessun link per eventi ed +eventi virtuali (compleanni). + +## Lettura lato server + +`src/lib/eventi.server.ts` (`leggiEventi()`) è la stessa conversione riga→modello di +`eventi.ts`, ma con `supabaseAdmin` per le route API che girano senza sessione utente (es. +`sollecita-presenze.ts`, `promemoria-palloni.ts` — vedi `presenze.md` e `palloni.md`) e per +`notifiche-smart.ts`, che decide i promemoria da mandare in base agli eventi del giorno. + +--- + +## Limiti noti + +1. **Cancellare un evento è distruttivo per tutto ciò che vi era agganciato.** Un trigger + (`m14_pulizia_dati_evento_cancellato`, + [DD-029](../DESIGN_DECISIONS.md#dd-029--cancellare-un-evento-pulisce-a-cascata-i-dati-collegati)) + pulisce a cascata presenze, cacche, voti MVP/pagelle/badge social, turni palloni e scout + di quell'evento: non è recuperabile con un annulla, e prima di M14 quelle righe restavano + orfane nel database (bonificate una tantum da M15/M16, vedi `PROJECT_STATE.md`). +2. **Nessuna creazione automatica degli eventi partita dal calendario CSI.** Le gare + ufficiali arrivano già come dati (`getEventsByTeamId.php`, vedi `collegamento-csi.md`), + ma un amministratore deve comunque creare a mano l'evento corrispondente in `/eventi` + perché esistano convocazioni, presenze, MVP e pagelle per quella partita — altrimenti la + gara resta visibile solo nello storico CSI, con un dettaglio "solo CSI" più povero + (`/partita-csi/$id` invece di `/partita/$id`). In `docs/ROADMAP.md` sotto "Prossimo". diff --git a/docs/modules/profilo-giocatore.md b/docs/modules/profilo-giocatore.md index 12ef0bc..83d19e3 100644 --- a/docs/modules/profilo-giocatore.md +++ b/docs/modules/profilo-giocatore.md @@ -1,5 +1,11 @@ # Modulo — Profilo Giocatore +**Stato:** implementato +**File principali:** `src/lib/profili.ts`, `src/lib/profili-core.ts`, `src/routes/profilo.tsx`, +`src/routes/admin.tsx` + +--- + ## Obiettivo Il modulo "Profilo Giocatore" raccoglie tutte le informazioni personali, amministrative e documentali di ciascun membro della squadra. diff --git a/docs/modules/squadra.md b/docs/modules/squadra.md new file mode 100644 index 0000000..517482b --- /dev/null +++ b/docs/modules/squadra.md @@ -0,0 +1,89 @@ +# 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](../DESIGN_DECISIONS.md#dd-015--rosa-anagrafica-da-codice-hardcoded-a-database) +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 + giocatori `attivo`. Serve dove basta sapere chi c'è, es. i compleanni nel Calendario o le + liste presenze: non monta gli hook di MVP/pagelle/palloni/infortuni. +- **`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 un `useMemo` le cache già in memoria di + `mvp-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 di `useIo()` (il giocatore sul dispositivo corrente) e `useObiettivi()` + (`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. + +## Gestione dati squadra (solo amministratore) + +Da `/admin` un amministratore può ([DD-017](../DESIGN_DECISIONS.md#dd-017--lamministratore-può-compilare-i-dati-al-posto-del-giocatore)): + +| 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` (`prossimoIdGiocatore()`), non generato dal database | +| 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](../DESIGN_DECISIONS.md#dd-016--schema-dati-profilo-giocatore-f0) 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](../DESIGN_DECISIONS.md#dd-018--collegamento-automatico-giocatoreaccount-per-email)). +`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 + +1. **`giocatori_squadra` non ha ancora una colonna per la data di nascita.** Per i + giocatori storici (seed iniziale) la nascita viene letta da `crapp-data.ts` + (`nascitaPerId`, lookup per id); un giocatore aggiunto dopo la migrazione non ha nascita + nota finché la colonna non esiste (DD-015). Effetto visibile: niente compleanno nel + Calendario per quei giocatori. +2. **`src/lib/crapp-data.ts` resta 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 in `giocatori_squadra` mostra quindi + comunque una squadra, non una schermata vuota — ma è la rosa 2025/26 hardcoded, non + quella reale.