diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index bb56ea7..b94e869 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -18,6 +18,10 @@ schema in [docs/DATABASE.md](docs/DATABASE.md), le procedure in `m30_sollecito_solo_24h`) sono applicate in produzione (verificato con `npx supabase migration list` il 01/10/2026); la `m31` no, ancora da applicare (DD-047). La `m29` toglie `foto_path`: il codice che non la legge più (1.2.2) va rilasciato. +- **Su `develop`, non ancora rilasciato:** obiettivi cliccabili con card di dettaglio, presenze del mese + calcolate sui convocati, obiettivi «Presenze collettive» e «evento di squadra al mese» (ora dell'evento), + tessera CSI tolta dalla dashboard (DD-048), notifica di compleanno (DD-047, migration `m31`). Dettaglio in + [docs/CHANGELOG.md](docs/CHANGELOG.md). - **Worker delle notifiche** (`mailer/`): in funzione su un Raspberry Pi con Docker dal 29/09/2026 ([docs/WORKER_EMAIL.md](docs/WORKER_EMAIL.md)). Il codice con l'interruttore «Email» in Profilo è su `develop`, non ancora su `main`. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 1a74348..fcffcc2 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -85,7 +85,7 @@ quando il database non risponde. ## UI Componenti condivisi in `src/components/crapp/` (`ui-bits.tsx` per `Card`, `PageHeader`, -`Section`, `StatTile`), animazioni in `src/components/motion/`. Mobile-first (DD-005): poche +`Section`, `StatTile`; i drawer di dettaglio `BadgeDrawer` e `ObiettivoDrawer`), animazioni in `src/components/motion/`. Mobile-first (DD-005): poche schermate, pochi click. In `src/components/ui/` restano solo le due primitive shadcn davvero usate, `drawer` (vaul) e diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 958d218..1adedfa 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -25,6 +25,9 @@ versioni `0.y.z` erano pre-release. ### Modificato +- **Tessera CSI tolta dalla dashboard** — in Profili spariscono il blocco «Tesseramento CSI» (numero e data + tessera), il badge «Tesserato»/«Da tesserare» e il riquadro «Tesserati» della tab Squadra. L'export CSV non + cambia. Le colonne a database restano, non più usate (DD-048). Nessuna migration. - **Obiettivo «90% di presenze del mese»** — i posti sono i convocati di ogni partita e allenamento (tutta la rosa se non ce ne sono), come nelle presenze collettive: chi non è convocato non abbassa più la percentuale. Nessuna migration. - **Obiettivo «1 evento di squadra al mese»** — si completa quando arriva l'ora dell'evento, non appena viene diff --git a/docs/DATABASE.md b/docs/DATABASE.md index 444af8a..cd6de2f 100644 --- a/docs/DATABASE.md +++ b/docs/DATABASE.md @@ -59,7 +59,7 @@ Colonne di `giocatori_squadra` che meritano una nota: | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `email` | Chiave del collegamento automatico account↔giocatore al primo accesso (DD-018). Impostabile da `/admin`. | | `auth_user_id` | Collega lo slot all'account; NULL finché nessuno lo reclama. | -| `numero_tessera`, `data_tessera` | Chi è già tesserato al CSI. Come `numero` e `ruolo` le scrive solo un admin: il trigger le include tra i campi bloccati per chi reclama il proprio slot. | +| `numero_tessera`, `data_tessera` | Non più usate dall'interfaccia (DD-048): i valori già inseriti restano, nessuna schermata li mostra o li scrive. Il trigger le include ancora tra i campi bloccati per chi reclama il proprio slot. | | `nascita` | Pubblica a tutta la rosa (DD-031). Non si scrive mai da qui: il trigger `sincronizza_nascita_pubblica` su `profili_giocatore` la tiene allineata a `data_nascita` (impostata, aggiornata o azzerata). | | `tipo` | `giocatore` o `allenatore` (DD-034). L'allenatore ha uno slot per collegarsi e per i dati personali ma è fuori dalla rosa di gioco (`inRosa()`), con `numero` NULL (vincolo `giocatori_squadra_numero_giocatori`) e `ruolo` vuoto. Lo scrive solo un admin. | diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 462f19e..eacf5f4 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -29,8 +29,8 @@ Tutto quello che è rilasciato, fino alla versione 1.0.0 (vedi `CHANGELOG.md`). - [x] Profilo Giocatore — dati personali, documento d'identità, certificato medico (caricamento, scadenza, stato, download); la foto tessera è stata tolta (DD-044); 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] Gestione tesseramenti CSI — raccolta dati ed export CSV; il tracciamento di numero e data + di tessera è stato tolto dalla dashboard (DD-048) - [x] Collegamento CSI (stagione 2025/26) - [x] Classifica automatica (campionato e Coppa) - [x] Risultati campionato diff --git a/docs/decisions/DD-048.md b/docs/decisions/DD-048.md new file mode 100644 index 0000000..1d3948c --- /dev/null +++ b/docs/decisions/DD-048.md @@ -0,0 +1,37 @@ +# DD-048 — Via numero e data tessera dalla dashboard + +**Data:** 6 ottobre 2026 +**Stato:** Accettata (nessuna migration) + +**Contesto** +La scheda di ogni giocatore in `/admin` aveva un blocco «Tesseramento CSI» con numero e data +tessera, scritti solo dall'admin (roadmap v1.1). Non serve più tenerli nell'app. + +**Decisione** + +1. Sparisce il blocco «Tesseramento CSI» dalla scheda del giocatore, con il pulsante «Salva + tesseramento» e il salvataggio (`useSalvaTesseramento()`). +2. Spariscono anche le due viste che ne dipendevano e senza il campo non avrebbero più senso: + il badge «Tesserato»/«Da tesserare» sulla scheda e il riquadro «Tesserati» della tab Squadra. +3. **Restano** l'export CSV per il tesseramento (non conteneva la tessera) e il resto del profilo + amministrativo: documento, certificato, dati personali. +4. **Dati:** le colonne `giocatori_squadra.numero_tessera` e `data_tessera` non vengono toccate + e continuano a essere lette dal codice, ma nessuna schermata le mostra né le scrive. I valori + già inseriti restano a database. + +**Alternative scartate** + +- Eliminare anche le colonne → scartata per ora: serve una migration e una decisione sui valori + già inseriti; si può fare dopo senza toccare l'interfaccia. +- Lasciare badge e riquadro → scartata: senza il campo resterebbero fermi su «Da tesserare» e + `0/N`, un'informazione falsa. + +**Conseguenze** + +- L'admin non può più registrare né correggere la tessera dall'app. +- Chi lavora sul codice trova `numeroTessera` e `dataTessera` nel tipo `GiocatoreSquadra` senza + uso in interfaccia: sono volutamente lasciati finché le colonne esistono. + +**Riesame** +Se tra qualche mese nessuno ha bisogno dei valori già inseriti, eliminare le colonne con una +migration e togliere i due campi dal tipo e dalla lettura. diff --git a/docs/decisions/README.md b/docs/decisions/README.md index 71c98c0..5fa5128 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -86,6 +86,7 @@ Una decisione sostituita non si cancella: si cambia il suo **Stato** e si rimand | [DD-038](DD-038.md) | Tre canali per ogni notifica | | [DD-039](DD-039.md) | Niente promemoria all'allenatore | | [DD-040](DD-040.md) | Palloni e solleciti automatici | +| [DD-048](DD-048.md) | Via la tessera dalla dashboard | | [DD-047](DD-047.md) | Notifica di compleanno | | [DD-046](DD-046.md) | Avviso certificati mancanti | | [DD-045](DD-045.md) | Sollecito presenze: un solo avviso a 24 ore | diff --git a/docs/modules/obiettivi-squadra.md b/docs/modules/obiettivi-squadra.md index a84df8c..3c4c0a5 100644 --- a/docs/modules/obiettivi-squadra.md +++ b/docs/modules/obiettivi-squadra.md @@ -2,7 +2,8 @@ **Stato:** implementato — mesi/scadenze dinamici, target stagionali fissi da rivedere a mano, copertura test completa (unit + integration) su tutti e 10 gli obiettivi. -**File principali:** `src/lib/obiettivi.ts`, `src/lib/rosa.ts` (`useObiettivi()`) +**File principali:** `src/lib/obiettivi.ts`, `src/lib/rosa.ts` (`useObiettivi()`), +`src/components/crapp/ObiettivoDrawer.tsx` (card di dettaglio) --- @@ -70,7 +71,9 @@ UI per farlo, si cambia il numero in `obiettivi.ts`. Fa eccezione "Continuità d automaticamente a ogni cambio mese: il mese di riferimento è calcolato dalla data corrente (fuso Europe/Rome, `meseCorrente(oggi)`), non più una costante fissa. Per `o1`, titolo ("90% di presenze ad agosto" / "a settembre" / ...) e scadenza (ultimo giorno del mese) -seguono di conseguenza. +seguono di conseguenza. I posti di ogni partita e allenamento sono i convocati (tutta la rosa se non ce +ne sono): chi non è convocato non pesa, mentre assente, forse, infortunato e nessuna risposta sono +presenze perse. `o6` si completa quando **arriva l'ora dell'evento**, non quando lo si definisce: conta solo un evento di tipo "evento" del mese corrente il cui `data` + `ora` (fuso Europe/Rome, diff --git a/docs/modules/profilo-giocatore.md b/docs/modules/profilo-giocatore.md index d6299ec..b9256c2 100644 --- a/docs/modules/profilo-giocatore.md +++ b/docs/modules/profilo-giocatore.md @@ -328,7 +328,6 @@ Per ogni giocatore, nella tab Profili, vengono mostrati. - Stato del profilo - Certificato medico - Documento di identità -- Stato tesseramento CSI (tesserato / da tesserare) Azioni disponibili. @@ -336,7 +335,6 @@ Azioni disponibili. - 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) @@ -362,12 +360,9 @@ Campi esportati. ## 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. +Tolto dalla dashboard (DD-048): numero e data della tessera CSI non si registrano più dall'app e non +ci sono più il badge «Tesserato»/«Da tesserare» né il conteggio dei tesserati. Le colonne +`numero_tessera` e `data_tessera` esistono ancora a database, senza uso. ## Completamento profilo diff --git a/docs/modules/squadra.md b/docs/modules/squadra.md index 481f87f..65cf7c6 100644 --- a/docs/modules/squadra.md +++ b/docs/modules/squadra.md @@ -55,13 +55,12 @@ specifica completa sta in [allenatore.md](allenatore.md). Da `/admin` un amministratore può ([DD-017](../decisions/DD-017.md)): -| 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; 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](../decisions/DD-016.md) 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`) | +| 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; 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](../decisions/DD-016.md) regola 2); il giocatore si ricollega al primo accesso successivo | Il collegamento giocatore↔account, invece, non è manuale: avviene in automatico al primo accesso con Google, per corrispondenza email diff --git a/src/lib/giocatori-squadra.ts b/src/lib/giocatori-squadra.ts index 7df6258..99d5448 100644 --- a/src/lib/giocatori-squadra.ts +++ b/src/lib/giocatori-squadra.ts @@ -222,40 +222,6 @@ export function useSalvaDatiSquadra() { }); } -/** Numero e data della tessera CSI, note solo dopo il tesseramento effettivo. */ -export type DatiTesseramento = Pick; - -/** - * Registra numero e data della tessera CSI (roadmap v1.1). Campo puramente amministrativo: - * il trigger di M8 lo rende scrivibile solo da un admin, il giocatore non può autodichiararsi - * tesserato. - */ -export function useSalvaTesseramento() { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async (input: { giocatoreId: string; dati: DatiTesseramento }) => { - const dati = { - numero_tessera: input.dati.numeroTessera?.trim() || null, - data_tessera: input.dati.dataTessera || null, - }; - const { error } = await supabaseNuoveTabelle - .from("giocatori_squadra") - .update(dati) - .eq("id", input.giocatoreId); - if (error) throw error; - return { - giocatoreId: input.giocatoreId, - dati: { numeroTessera: dati.numero_tessera, dataTessera: dati.data_tessera }, - }; - }, - onSuccess: (input) => { - queryClient.setQueryData(SQUADRA_KEY, (prec) => - (prec ?? []).map((g) => (g.id === input.giocatoreId ? { ...g, ...input.dati } : g)), - ); - }, - }); -} - /** * Aggiunge un giocatore alla rosa (DD-017). Solo un admin passa le policy di M1. * L'id (`g`) non è generato dal database: va calcolato con `prossimoIdGiocatore` diff --git a/src/routes/admin.tsx b/src/routes/admin.tsx index b61ba01..038a0ee 100644 --- a/src/routes/admin.tsx +++ b/src/routes/admin.tsx @@ -1,7 +1,6 @@ import { useState } from "react"; import { createFileRoute } from "@tanstack/react-router"; import { - BadgeCheck, Bell, BellOff, ChevronDown, @@ -33,11 +32,9 @@ import { useGiocatoriSquadra, useImpostaAttivo, useSalvaDatiSquadra, - useSalvaTesseramento, useScollegaAccount, validaDatiSquadra, type DatiSquadra, - type DatiTesseramento, type GiocatoreSquadra, } from "@/lib/giocatori-squadra"; import { scaricaFile, useProfili, useSalvaProfilo } from "@/lib/profili"; @@ -110,18 +107,11 @@ function ModificaGiocatore({ g, profilo }: { g: GiocatoreSquadra; profilo: Profi const { righe } = useGiocatoriSquadra(); const salvaSquadra = useSalvaDatiSquadra(); const salvaProfilo = useSalvaProfilo(); - const salvaTesseramento = useSalvaTesseramento(); const scollega = useScollegaAccount(); const impostaAttivo = useImpostaAttivo(); const [datiSquadra, setDatiSquadra] = useState(null); const [bozza, setBozza] = useState(null); - const [tesseramento, setTesseramento] = useState(null); - - const tesseramentoCorrente: DatiTesseramento = tesseramento ?? { - numeroTessera: g.numeroTessera, - dataTessera: g.dataTessera, - }; const squadraCorrente: DatiSquadra = datiSquadra ?? { nome: g.nome, @@ -131,7 +121,7 @@ function ModificaGiocatore({ g, profilo }: { g: GiocatoreSquadra; profilo: Profi email: g.email, tipo: g.tipo, }; - // L'allenatore non ha numero, ruolo in campo né tesseramento (DD-034). + // L'allenatore non ha numero né ruolo in campo (DD-034). const allenatore = isAllenatore(g); const profiloCorrente = bozza ?? profilo ?? profiloVuoto(g.id); @@ -167,16 +157,6 @@ function ModificaGiocatore({ g, profilo }: { g: GiocatoreSquadra; profilo: Profi } } - async function confermaTesseramento() { - try { - await salvaTesseramento.mutateAsync({ giocatoreId: g.id, dati: tesseramentoCorrente }); - setTesseramento(null); - toast.success("Tesseramento aggiornato"); - } catch (e) { - toast.error(messaggioErrore(e, "Salvataggio non riuscito")); - } - } - async function confermaProfilo() { try { await salvaProfilo.mutateAsync(profiloCorrente); @@ -269,46 +249,6 @@ function ModificaGiocatore({ g, profilo }: { g: GiocatoreSquadra; profilo: Profi Salva dati squadra - {allenatore ? null : ( - <> -

Tesseramento CSI

-
- - - setTesseramento({ - ...tesseramentoCorrente, - numeroTessera: e.target.value || null, - }) - } - className={classiInput} - /> - - - - setTesseramento({ ...tesseramentoCorrente, dataTessera: e.target.value || null }) - } - className={classiInput} - /> - -
- - - )} - {allenatore ? ( <>

Dati personali

@@ -486,15 +426,6 @@ function SchedaGiocatore({ stato={certificato} path={profilo?.certificatoPath ?? null} /> - - - {g.numeroTessera ? "Tesserato" : "Da tesserare"} - )} @@ -743,7 +674,6 @@ function Dashboard() { statoScadenza(profili[g.id]?.certificatoScadenza, profili[g.id]?.certificatoPath, oggi) === "valido", ).length; - const tesserati = attivi.filter((g) => g.numeroTessera).length; const contenutoSquadra = ( <> @@ -755,7 +685,6 @@ function Dashboard() { label="Certificati validi" hint="non scaduti" /> -