Toglie numero e data tessera dalla dashboard (DD-048); docs allineate
Spariscono dalla scheda del giocatore in /admin il blocco «Tesseramento CSI» e il salvataggio, il badge «Tesserato/Da tesserare» e il riquadro «Tesserati» della tab Squadra. L'export CSV non cambia. Le colonne numero_tessera e data_tessera restano a database, non più usate; nessuna migration. Aggiornati PROJECT_STATE, CHANGELOG, ARCHITECTURE, DATABASE, ROADMAP, i moduli profilo-giocatore, squadra e obiettivi-squadra, test/README e l'indice delle decisioni. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c2c3b249f2
commit
e7ae2c1884
13 files changed
+65
-128
No files matched your search
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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. |
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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 |
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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<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](../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<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](../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
|
||||
|
||||
@@ -222,40 +222,6 @@ export function useSalvaDatiSquadra() {
|
||||
});
|
||||
}
|
||||
|
||||
/** Numero e data della tessera CSI, note solo dopo il tesseramento effettivo. */
|
||||
export type DatiTesseramento = Pick<GiocatoreSquadra, "numeroTessera" | "dataTessera">;
|
||||
|
||||
/**
|
||||
* 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<GiocatoreSquadra[]>(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<N>`) non è generato dal database: va calcolato con `prossimoIdGiocatore`
|
||||
|
||||
+1
-72
@@ -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<DatiSquadra | null>(null);
|
||||
const [bozza, setBozza] = useState<Profilo | null>(null);
|
||||
const [tesseramento, setTesseramento] = useState<DatiTesseramento | null>(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
|
||||
</button>
|
||||
|
||||
{allenatore ? null : (
|
||||
<>
|
||||
<h3 className="font-display text-sm uppercase tracking-wide">Tesseramento CSI</h3>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<Campo label="Numero tessera">
|
||||
<input
|
||||
value={tesseramentoCorrente.numeroTessera ?? ""}
|
||||
maxLength={40}
|
||||
placeholder="Non ancora tesserato"
|
||||
onChange={(e) =>
|
||||
setTesseramento({
|
||||
...tesseramentoCorrente,
|
||||
numeroTessera: e.target.value || null,
|
||||
})
|
||||
}
|
||||
className={classiInput}
|
||||
/>
|
||||
</Campo>
|
||||
<Campo label="Data tessera">
|
||||
<input
|
||||
type="date"
|
||||
value={tesseramentoCorrente.dataTessera ?? ""}
|
||||
onChange={(e) =>
|
||||
setTesseramento({ ...tesseramentoCorrente, dataTessera: e.target.value || null })
|
||||
}
|
||||
className={classiInput}
|
||||
/>
|
||||
</Campo>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={confermaTesseramento}
|
||||
disabled={!tesseramento || salvaTesseramento.isPending}
|
||||
className="premi w-full rounded-2xl bg-primary py-2.5 text-xs font-bold uppercase text-primary-foreground disabled:opacity-50"
|
||||
>
|
||||
Salva tesseramento
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{allenatore ? (
|
||||
<>
|
||||
<h3 className="font-display text-sm uppercase tracking-wide">Dati personali</h3>
|
||||
@@ -486,15 +426,6 @@ function SchedaGiocatore({
|
||||
stato={certificato}
|
||||
path={profilo?.certificatoPath ?? null}
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-bold uppercase",
|
||||
statoClasse[g.numeroTessera ? "presente" : "assente"],
|
||||
)}
|
||||
>
|
||||
<BadgeCheck className="h-3.5 w-3.5" />
|
||||
{g.numeroTessera ? "Tesserato" : "Da tesserare"}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
<StatTile valore={`${tesserati}/${attivi.length}`} label="Tesserati" hint="CSI" />
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ la consegna effettiva a schermo bloccato richiede un telefono e il servizio push
|
||||
|
||||
| Cartella | Cosa verifica | Serve rete? |
|
||||
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
|
||||
| `unit/` | Logica di dominio pura: badge, serie, palloni, pagelle, MVP, cacche, scout, obiettivi, notifiche, parsing CSI, dati della rosa. Più le funzioni pure isolabili nei moduli con hook/rete (validazione upload, guardie push, JWT VAPID, cattura errori, avatar, guardia admin delle route, lettura del `.env` ed elenco dei bucket dello script di backup, logica del worker in `mailer/` (email e push dei promemoria), preferenza «Email» e avviso del sondaggio) | No |
|
||||
| `unit/` | Logica di dominio pura: badge, serie, palloni, pagelle, MVP, cacche, scout, obiettivi (calcolo e card di dettaglio), notifiche, parsing CSI, dati della rosa. Più le funzioni pure isolabili nei moduli con hook/rete (validazione upload, guardie push, JWT VAPID, cattura errori, avatar, guardia admin delle route, lettura del `.env` ed elenco dei bucket dello script di backup, logica del worker in `mailer/` (email e push dei promemoria), preferenza «Email» e avviso del sondaggio) | No |
|
||||
| `integration/` | Le route `/api/public/*` sul server di sviluppo: risposte, cache, validazione degli input. Più schema e permessi del Profilo Giocatore (`schema-profili`) contro il database configurato; permessi per ruolo, incluse le policy di M11, le deroghe dell'amministratore e le tabelle lasciate aperte di proposito (`permessi`), accesso alle route di notifica (`permessi-route`), semantica degli upsert e vincoli (`scritture`), le letture lato server delle route push (`lettori-server`) e la pulizia a cascata dei dati collegati alla cancellazione di un evento (`pulizia-evento`, M14) la funzione di bonifica delle righe orfane (`bonifica-evento`, M16), il centro notifiche in-app (`notifiche-utente`, M17), la coda delle email e le sue funzioni RPC (`notifiche-email`, M22), i promemoria evento che non ricompaiono se eliminati (`promemoria-eventi`, M23), la coda delle push dei promemoria (`push-promemoria`, M24), i job automatici di turno palloni e sollecito presenze con revoca, finestre e testi identici al codice (`notifiche-automatiche`, M26-M28 e M30, DD-040, DD-042, DD-043 e DD-045: palloni un solo avviso a 3 ore, sollecito uno solo a 24 ore, nessun promemoria evento) la colonna `foto_path` eliminata (`foto-tessera-rimossa`, M29, DD-044) e il sondaggio in-app/email (`sondaggio-notifiche`, M24), i destinatari delle notifiche per ruolo (`destinatari-notifiche`: promemoria, messaggio dello staff, sondaggio, sollecito e turno palloni con allenatore e giocatore), gli avvisi di compleanno con ora, più compleanni lo stesso giorno, registro e 29 febbraio (`notifiche-compleanno`, M31, DD-047), la nascita pubblica sincronizzata dal profilo (`nascita-pubblica`, M18) e il suo backfill per i profili preesistenti (`backfill-nascita-esistenti`, M19 — unico test che esegue SQL diretto via `docker exec` nel container Postgres, perché PostgREST non espone quell'update multi-tabella come RPC) sul database locale | Sì |
|
||||
| `e2e/` | Percorsi completi sull'app servita: schermate, dati CSI fino alla pagina, file PWA, 404 | Sì |
|
||||
| `helpers/` | Avvio del server di test e mini-harness condiviso | — |
|
||||
|
||||
Reference in new issue
Block a user