Il Profilo non chiede più la foto tessera e la scheda admin non la mostra. Il completamento diventa dati personali 34%, documento 33%, certificato 33%. La migration m29 elimina la colonna profili_giocatore.foto_path; le 4 foto già caricate sono state cancellate dal bucket profili-giocatore. Migration applicata in produzione. Test: pesi e sezioni di profili-core, rendering di CampiProfilo, colonna rimossa sul database locale. Documentati DD-044, modulo profilo-giocatore, DATABASE, ROADMAP e changelog. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
18 KiB
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.
L'obiettivo è centralizzare in un'unica schermata tutti i dati necessari sia al giocatore sia agli amministratori, eliminando la gestione tramite chat, documenti cartacei e fogli Excel.
Utenti
L'allenatore ha un profilo ridotto (solo Docs con i dati personali essenziali e Opzioni): la specifica sta in allenatore.md.
Giocatore
Può:
- visualizzare il proprio profilo
- modificare i propri dati personali
- aggiornare il certificato medico
- aggiornare i documenti
- caricare le immagini richieste
Amministratore
Può:
- visualizzare il profilo di tutti i giocatori
- scaricare documenti e certificati
- esportare i dati necessari al tesseramento CSI
- verificare lo stato di completamento dei profili
- modificare i dati squadra di qualsiasi giocatore (nome, cognome, numero, ruolo, email)
- compilare e correggere i dati personali e del documento al posto di un giocatore (DD-017)
- scollegare un account da un profilo, liberando lo slot
- aggiungere un nuovo giocatore alla rosa (id, nome, cognome, numero, ruolo, email opzionale), oppure un allenatore (tipo «Allenatore»: niente numero né ruolo, DD-034)
- disattivare un giocatore che ha lasciato la squadra, e riattivarlo in caso di errore: la riga non viene eliminata, così presenze, voti, pagelle e badge della stagione restano agganciati al suo id
Non può caricare o sostituire i file altrui: documento e certificato restano responsabilità del giocatore che li fornisce.
Flusso utente
Primo accesso
- Login tramite Google oppure Email. Implementato con il solo Google: la squadra ha tutti un account Google, e un secondo metodo è additivo (un bottone in più sulla stessa schermata) il giorno che serve.
- Collegamento automatico al proprio giocatore, confrontando l'email dell'account Google
con l'email registrata in
giocatori_squadra(DD-018). Nessuna scelta manuale: se l'email non corrisponde a nessun profilo, l'accesso si ferma con un messaggio che invita a contattare un amministratore. - Accesso alla Home.
Se il profilo non è completo compare automaticamente un widget di completamento.
Home
Il giocatore visualizza un widget dedicato e, quando serve, l'avviso sul certificato medico.
Completa il tuo profilo
Viene mostrata una barra di avanzamento (esempio: Profilo completato — 85%), composta dalle seguenti sezioni.
- Dati personali
- Documento di identità
- Certificato medico
Quando tutte le sezioni sono complete il widget scompare automaticamente.
Il tap apre Profilo sulla sottosezione Documenti (/profilo?tab=documenti), non
sulla tab Stagione.
Avviso certificati
Stato: implementato (DD-035, soglia del giocatore DD-041).
Un avviso in Home segnala i certificati medici in scadenza o scaduti, così nessuno se ne accorge quando il giocatore è già fuori regola. Si calcola al volo dalla data di scadenza già salvata: niente tabella, niente migration, niente push.
Chi lo vede
| Utente | Cosa vede | Al tocco |
|---|---|---|
| Admin | l'avviso dello staff: i nomi di tutti i giocatori coinvolti | niente, non è cliccabile |
| Giocatore interessato | l'avviso personale: solo il proprio certificato | apre /profilo?tab=documenti |
| Admin che è anche giocatore | entrambi: lo staff (senza il suo nome) e il proprio personale, a 30 giorni | personale: apre /profilo?tab=documenti |
| Altri giocatori | niente | — |
| Allenatore | niente: non vede i certificati altrui e non ne ha uno (DD-034) | — |
L'avviso dello staff non è cliccabile, come quello dei palloni: l'admin non può caricare il certificato al posto del giocatore (DD-017), quindi può solo sollecitarlo, e il giocatore riceve già il proprio avviso. Quello personale porta invece dove si carica il certificato nuovo, come il widget «Completa il tuo profilo».
I certificati sono dati sanitari: fuori da /admin li legge solo il titolare (DD-016).
Nessuna policy cambia, perché useProfili() restituisce già tutti i profili a un admin e il
solo proprio a un giocatore.
Quando compare
giorni è la differenza in giorni di calendario tra la scadenza e oggi (ora locale). La
soglia del giallo dipende da chi guarda (DD-041): il giocatore è avvisato un mese prima dello
staff.
| Chi guarda | Soglia |
|---|---|
| Giocatore | GIORNI_AVVISO_CERTIFICATO_GIOCATORE = 30 |
| Admin | GIORNI_AVVISO_CERTIFICATO = 7 |
| Condizione | Stato | Avviso |
|---|---|---|
giorni > soglia |
valido | nessuno |
0 ≤ giorni ≤ soglia |
in scadenza | giallo (warning) |
giorni < 0 |
scaduto | nero (primary) |
| data o file mancanti | mancante | nessuno |
Esempio: scadenza il 5 ottobre 2026. Dal 5 settembre l'avviso giallo compare solo al giocatore; dal 28 settembre anche agli admin; dal 6 ottobre è nero per entrambi.
- Il giorno della scadenza il certificato vale ancora, coerente con
statoScadenza(): il giocatore compare nel giallo con «scade oggi» e passa al nero dal giorno dopo. - L'avviso nero resta finché il giocatore non aggiorna la data: non c'è un limite di tempo.
- Il certificato mancante non genera avviso: è un problema diverso, già visibile nella
tab Profili di
/admine nel widget di completamento del giocatore. - Sono esclusi i giocatori disattivati e gli allenatori: conta solo la rosa (
inRosa(), cioèattivoetipo = 'giocatore').
Testi
| Avviso | Titolo (staff / personale) | Riga |
|---|---|---|
| Giallo, staff | «Certificati in scadenza» | «Mario Rossi — scade tra 5 giorni», «… — scade domani», «… — scade oggi» |
| Nero, staff | «Certificati scaduti» | «Mario Rossi — scaduto il 12/09/2026» |
| Giallo, personale | «Certificato in scadenza» | «Il tuo certificato medico scade tra 5 giorni» / «domani» / «oggi» |
| Nero, personale | «Certificato scaduto» | «Il tuo certificato medico è scaduto il 12/09/2026: caricane uno nuovo» |
Il numero di giorni scende da solo ogni giorno (30… 7, 6, 5… domani, oggi): l'avviso non conserva niente, rilegge la data a ogni apertura della Home.
Più giocatori
- Più giocatori nello stesso stato stanno in un unico avviso, una riga ciascuno, e compaiono tutti: nessun limite al numero di nomi, anche a inizio stagione quando ne scadono molti insieme.
- Se ci sono sia certificati in scadenza sia scaduti compaiono due avvisi: prima quello nero, poi quello giallo.
- Ordine delle righe: dalla scadenza più vicina nel giallo, dal certificato scaduto da più tempo nel nero; a parità di data, alfabetico per cognome e poi per nome. L'ordine è stabile, quindi l'elenco non si rimescola tra un'apertura e l'altra.
Aspetto e posizione
- Stessa forma del banner palloni (
PromemoriaPalloni): card arrotondata a tutta larghezza con icona e titolo infont-displaymaiuscolo, righe in testo piccolo sotto. - Giallo: sfondo
bg-warning, testotext-warning-foreground. Nero: sfondobg-primary, testotext-primary-foreground. L'app è solo chiara (DD-022), quindi il nero non rischia di confondersi con lo sfondo. - In Home sta subito sotto il banner palloni e sopra «Completa il tuo profilo»: prima ciò che scade oggi, poi ciò che manca.
Rinnovo
Il giocatore rinnova dal proprio profilo, sottosezione Documenti: carica il file nuovo e
aggiorna la data. Il salvataggio aggiorna la cache dei profili (setQueryData), quindi il
suo avviso personale sparisce subito. Nell'avviso dello staff il nome sparisce alla lettura
successiva dei profili da parte dell'admin (cache di 30 minuti, useProfili()), senza
nessuna azione da parte sua. Quando non resta nessuno, l'avviso non compare più.
Basta aggiornare la data: l'avviso guarda solo quella (e la presenza di un file). Che il file caricato sia davvero quello nuovo resta un controllo dell'admin, come oggi. Anche l'admin può correggere la data dalla scheda del giocatore (DD-017, non il file): l'avviso segue la data, chiunque l'abbia scritta.
Caricamento
Finché ruoli e profili non sono arrivati, l'avviso non compare: così un admin che è anche giocatore non vede per un attimo l'avviso personale prima di quello dello staff. Se la lettura dei profili fallisce l'avviso semplicemente non compare, senza messaggio d'errore: la Home non deve rompersi per un dato accessorio.
Implementazione
| Pezzo | Ruolo |
|---|---|
GIORNI_AVVISO_CERTIFICATO (profili-core.ts) |
la soglia dello staff, 7 |
GIORNI_AVVISO_CERTIFICATO_GIOCATORE |
la soglia del giocatore, 30 (DD-041) |
giorniAllaScadenza(scadenza, oggi) |
differenza in giorni di calendario tra due date AAAA-MM-GG, negativa se scaduto |
avvisiCertificati(rosa, profili, oggi, soglia) |
funzione pura (soglia di default 7): restituisce { scaduti, inScadenza }, ognuno una lista ordinata di { giocatoreId, nome, cognome, scadenza, giorni } |
avvisiCertificatiUtente(rosa, profili, oggi, utente) |
funzione pura: { personale, staff } per chi guarda; l'admin giocatore ha entrambi, senza il suo nome nello staff (DD-041) |
testoScadenza(giorni) |
«scade oggi» / «scade domani» / «scade tra N giorni» |
src/components/crapp/AvvisoCertificati.tsx |
legge useGiocatoriSquadra(), useProfili(), useIsAdmin() e l'utente corrente; sceglie avviso dello staff o personale e disegna le card |
src/routes/index.tsx |
monta <AvvisoCertificati /> subito dopo <PromemoriaPalloni /> |
- "Oggi" viene da
oggiISO()(palloni-core.ts), in ora locale. Non danew Date().toISOString(), che è in UTC e tra mezzanotte e le 2 darebbe il giorno prima. - Nessuna query nuova:
useProfili()ha la stessa chiave di cache usata da Profilo e/admin, quindi al massimo una lettura diprofili_giocatoreper sessione (vedi EFFICIENZA_CLOUD.md). Per l'allenatore il componente non legge i profili. - Test in
test/unit/profili-core.test.ts: avvisi per utente (admin giocatore, admin puro, giocatore semplice), soglia dello staff (8 giorni no, 7 sì, 0 sì, −1 scaduto) e del giocatore (31 no, 30 sì), certificato senza file o senza data escluso, giocatore disattivato e allenatore esclusi, ordinamento (per data, poi alfabetico), testi al singolare e al plurale, cambio di mese e di anno nel calcolo dei giorni.
Profilo
Il profilo viene suddiviso in sette aree.
Dati Giocatore
Dati squadra — solo lettura, gestiti esclusivamente dagli amministratori.
- Nome
- Cognome
- Numero di maglia
- Ruolo
Dati personali — modificabili dal giocatore.
- Data di nascita — oltre a
profili_giocatore.data_nascita, un trigger la sincronizza ingiocatori_squadra.nascita(DD-031): a differenza degli altri campi di questa sezione, visibile a tutta la squadra, non solo al giocatore stesso o all'admin (alimenta Squadra e i compleanni nel Calendario, vedisquadra.md). Cancellare il profilo azzera anche quel valore pubblico. - Luogo di nascita
- Indirizzo di residenza
- Telefono
Documento di identità
Campi.
- Tipo documento
- Numero documento
- Rilasciato da
- Data emissione
- Data scadenza
Upload.
- Foto fronte
- Foto retro
Certificato medico
Campi.
- Data di scadenza
Upload.
- Certificato medico
Il giocatore può aggiornare liberamente sia la data sia il file.
Quando mancano 30 giorni o meno alla scadenza, o il certificato è scaduto, il giocatore riceve un avviso nella propria Home; compare in quello degli admin da 7 giorni prima (vedi Avviso certificati).
Lo storico non viene mantenuto nella prima versione.
Foto tessera
Rimossa (DD-044, migration m29): il profilo non chiede più la foto tessera e la colonna
foto_path non esiste più. I file già caricati sono stati cancellati dal bucket.
Statistiche
Sezione già presente. Contiene.
- Presenze
- Voto medio
- MVP
- Serie
- Altre statistiche disponibili
Badge
Sezione già presente.
Contiene tutti i badge ottenuti e quelli ancora da sbloccare.
Opzioni
Contiene.
- Logout
- Preferenze notifiche: un solo interruttore che iscrive il dispositivo a tutte le push (turno palloni, solleciti presenze) e abilita le notifiche smart in app — non è limitato ai soli palloni (vedi Notifiche)
- Impostazioni applicazione
- Segnala un bug e Suggerisci una nuova funzionalità: due link che aprono una issue GitHub
già impostata sul template giusto (
.github/ISSUE_TEMPLATE/bug_report.ymlefeature_request.yml). Nessun dato passa dall'app — la segnalazione vive interamente su GitHub, così non servono né una tabella né una schermata di gestione.
Dashboard amministratore
Gli amministratori dispongono di una schermata dedicata (/admin, raggiungibile da
Profilo → Opzioni), organizzata in tab scorrevoli a pillole (BarraSottosezioni, stesso
componente di Squadra e Campionato): Squadra, Profili, Disattivati (solo se
c'è almeno un giocatore disattivato) e Notifiche.
La tab Notifiche mostra quanti giocatori attivi hanno almeno un dispositivo iscritto
alle notifiche push e i loro nomi, leggendo GET /api/public/notifiche-attive (vedi
Notifiche). È solo consultiva: l'attivazione resta un gesto che ogni
giocatore deve fare dal proprio dispositivo (Profilo), l'admin non può attivarla per conto
di altri.
Gli allenatori stanno nella stessa tab in un gruppo «Allenatori» a parte, senza documenti né tesseramento, e non entrano nei conteggi della tab Squadra né nell'export CSI (DD-034).
Per ogni giocatore, nella tab Profili, vengono mostrati.
- Stato del profilo
- Certificato medico
- Documento di identità
- Stato tesseramento CSI (tesserato / da tesserare)
Azioni disponibili.
- Visualizza profilo (la scheda si apre in linea nell'elenco: nessuna schermata separata)
- Scarica certificato
- Scarica documento
- Modifica dati squadra e dati personali del giocatore (DD-017)
- Registra numero e data della tessera CSI, una volta arrivata dal comitato
- Scollega account, per liberare uno slot assegnato per errore
- Aggiungi giocatore, per inserire un nuovo membro della squadra
- Disattiva/Riattiva giocatore, per chi lascia la squadra (o rientra)
Esportazione CSI
Gli amministratori possono esportare un file CSV contenente esclusivamente i dati richiesti per il tesseramento.
Campi esportati.
- Nome
- Cognome
- Data di nascita
- Luogo di nascita
- Indirizzo
- Telefono
- Tipo documento
- Numero documento
- Rilasciato da
- Data emissione
- Data scadenza
Tracciamento tesseramento
Numero e data della tessera CSI non sono dati che il giocatore conosce in anticipo: arrivano dal comitato dopo l'iscrizione effettiva. Per questo, a differenza dei dati personali del profilo, li scrive solo un amministratore — come nome, cognome, numero di maglia e ruolo (DD-017), il trigger sulla tabella li rende non modificabili dal giocatore stesso. La dashboard mostra un badge "Tesserato"/"Da tesserare" su ogni scheda e il conteggio complessivo della squadra.
Completamento profilo
Ogni sezione contribuisce alla percentuale di completamento.
| Sezione | Peso |
|---|---|
| Dati personali | 34% |
| Documento di identità | 33% |
| Certificato medico | 33% |
Quando tutte le sezioni risultano complete il profilo raggiunge il 100%.
Permessi
Giocatore — può modificare esclusivamente il proprio profilo.
Amministratore — può visualizzare tutti i profili, scaricare tutti i documenti, esportare i dati, modificare dati squadra e dati personali di chiunque e scollegare un account (DD-017). Non carica file al posto di altri.
Avviso certificati — il giocatore vede solo il proprio, l'admin i nomi di tutta la rosa, nessun altro ne vede (vedi Avviso certificati).
Versione 1
- Profilo giocatore
- Completamento profilo
- Gestione dati personali
- Documento di identità
- Certificato medico
- Dashboard amministratore
- Esportazione CSV CSI
Versioni future
- Storico certificati medici
- Avviso certificati anche via push, o con una soglia regolabile dall'admin (DD-035, Riesame)
- Gestione documenti aggiuntivi
- Consensi privacy
- Firma digitale
- Verifica automatica documenti