Ogni testo sotto i 16 px sale di un passo in tutte le pagine (12→13, 13→14, 14→15, 15→16); restano invariate le barre a pillola dei menu (navigazione in basso e sottosezioni) e i badge da 10-11 px. Nella scheda giocatore di Squadra l'etichetta «Cacche/partita» diventa «Cacche» (il valore resta la media). Documentazione: DD-049, CHANGELOG, ARCHITECTURE, PROJECT_STATE, moduli squadra e allenatore. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
182 lines
9.9 KiB
Markdown
182 lines
9.9 KiB
Markdown
# Architettura del progetto
|
|
|
|
Come è fatta CrAPP: stack, organizzazione del codice, flusso di sviluppo. È il documento di
|
|
riferimento tecnico — `CLAUDE.md` non ripete questi contenuti, li richiama.
|
|
|
|
## Stack
|
|
|
|
| Livello | Tecnologie |
|
|
| ------------- | -------------------------------------------------------------------------------- |
|
|
| Frontend | React 19, TypeScript, TanStack Start (SSR), Vite 8, Tailwind CSS 4, motion, vaul |
|
|
| Backend | Supabase (PostgreSQL, Auth, Storage) |
|
|
| Hosting | Vercel |
|
|
| Versionamento | Git, GitHub |
|
|
|
|
Le dipendenze sono installate con **bun** (`bun.lock`, `bunfig.toml`). `bunfig.toml` impone
|
|
`minimumReleaseAge = 24h` come guardia supply-chain: aggiungere un pacchetto a
|
|
`minimumReleaseAgeExcludes` richiede conferma esplicita.
|
|
|
|
## Struttura del progetto
|
|
|
|
```
|
|
src/
|
|
components/ componenti condivisi (crapp/, ui/, motion/)
|
|
routes/ routing file-based
|
|
lib/ logica di dominio, un file per modulo
|
|
integrations/ client Supabase e integrazioni esterne
|
|
assets/
|
|
supabase/ migration SQL
|
|
test/ suite di test (unit, integration, end-to-end)
|
|
docs/ documentazione ufficiale
|
|
```
|
|
|
|
## Punti fermi
|
|
|
|
- **Routing**: file-based in `src/routes/`. `src/routeTree.gen.ts` è **generato**, non si
|
|
modifica a mano.
|
|
- **Configurazione Vite**: `vite.config.ts` usa `@lovable.dev/vite-tanstack-config`, che
|
|
include già devtools, tanstackStart, viteReact, tailwind, tsconfig-paths, nitro e l'alias
|
|
`@` → `src/`. **Non ri-aggiungere questi plugin**: l'app si rompe.
|
|
- **Entry point server**: `src/server.ts` avvolge l'entry di TanStack Start per intercettare
|
|
gli errori SSR che h3 trasformerebbe in un 500 JSON silenzioso, e renderizza
|
|
`renderErrorPage()`. `src/start.ts` registra i middleware globali (error handler, CSRF sui
|
|
server functions, `attachSupabaseAuth`).
|
|
- **Supabase**: `src/integrations/supabase/client.ts` (browser/SSR, chiave publishable — file
|
|
generato) e `client.server.ts` (`supabaseAdmin`, solo server). `types.ts` è generato dallo
|
|
schema; finché non viene rigenerato, le tabelle introdotte da M1/M2 si usano tramite
|
|
`client-nuove-tabelle.ts`, con i tipi di riga dichiarati nei moduli di `src/lib/`.
|
|
- **Autenticazione**: login Google via Supabase Auth (`src/lib/auth.ts`, DD-011). È l'unica
|
|
strada di accesso: `__root.tsx` rimanda a `/benvenuto` chi non ha sessione, e l'identità
|
|
del giocatore è lo slot di `giocatori_squadra` collegato all'account. I permessi di
|
|
amministrazione arrivano solo da `user_roles` (`src/lib/ruoli.ts`).
|
|
|
|
## Livello dati
|
|
|
|
Tutta la logica di dominio sta in `src/lib/`, un file per modulo (`presenze`, `eventi`,
|
|
`pagelle`, `mvp-voti`, `palloni`, `cacche`, `badges`, `scout-*`, `infortuni`, …). Il pattern
|
|
ricorrente:
|
|
|
|
- ogni modulo esporta hook TanStack Query (`useX`); i default globali stanno in
|
|
`src/router.tsx` (`staleTime` 5 min, `gcTime` 30 min, `refetchOnWindowFocus/Mount/Reconnect`
|
|
disattivati, `retry: 1`);
|
|
- dopo una mutazione la cache si aggiorna con `setQueryData`, **non** con
|
|
`invalidateQueries`: invalidare provoca una rilettura e costa una query in più. Unica
|
|
eccezione: `scout-live.ts`, dove il lock può essere stato preso da un altro dispositivo,
|
|
quindi quello che abbiamo scritto non è detto sia quello che vale;
|
|
- le funzioni pure di calcolo sono separate dagli hook (es. `palloni-core.ts` vs
|
|
`palloni.ts`, `mediePagelle()` vs `usePagelle()`);
|
|
- `src/lib/rosa.ts` è l'aggregatore: compone tutti gli hook e restituisce la rosa completa
|
|
con le statistiche derivate, **senza query aggiuntive** rispetto a quelle già in cache. Le
|
|
route consumano `useRosa()`, non i singoli moduli.
|
|
|
|
Nessun accesso al database dai componenti: solo attraverso i moduli in `src/lib/`, così il
|
|
backend resta sostituibile in un solo punto (DD-013, [PORTABILITA.md](PORTABILITA.md)).
|
|
|
|
Vincoli di efficienza cloud — niente polling, cache lunga, `setQueryData` invece di
|
|
`invalidateQueries` — in [EFFICIENZA_CLOUD.md](EFFICIENZA_CLOUD.md).
|
|
|
|
Badge e statistiche sono calcolati a runtime dai dati, non persistiti (DD-007). La
|
|
gamification deve restare equa tra ruoli (DD-008).
|
|
|
|
La rosa vive nella tabella `giocatori_squadra`, letta tramite `useRosa()`/`useGiocatoriSquadra()`
|
|
(DD-015, DD-016). `src/lib/crapp-data.ts` (`rosaCSI`) resta solo come seed storico e fallback
|
|
quando il database non risponde.
|
|
|
|
## UI
|
|
|
|
Componenti condivisi in `src/components/crapp/` (`ui-bits.tsx` per `Card`, `PageHeader`,
|
|
`Section`, `StatTile`; `Avviso.tsx` per gli avvisi colorati in Home; i drawer di dettaglio `BadgeDrawer` e `ObiettivoDrawer`), animazioni in `src/components/motion/`. Mobile-first (DD-005): poche
|
|
schermate, pochi click.
|
|
|
|
**Caratteri** (DD-049): niente testo sotto i 13 px, tranne la navigazione a pillola e i badge
|
|
da 10-11 px. Scala in uso: 13 px note, 15 px corpo, 16 px titoli di card; si usano le misure
|
|
fisse `text-[13px]`…`text-[16px]`, non `text-xs` e `text-sm`.
|
|
|
|
In `src/components/ui/` restano solo le due primitive shadcn davvero usate, `drawer` (vaul) e
|
|
`sonner`: le altre 43 non erano importate da nessuna parte (DD-021). Il resto dell'interfaccia
|
|
è composto con Tailwind e la primitiva `Card`, che è l'unica definizione di raggio, sfondo e
|
|
ombra delle superfici.
|
|
|
|
L'app è **solo chiara** (DD-022): non esiste un tema scuro e `:root` dichiara
|
|
`color-scheme: light`.
|
|
|
|
Il movimento usa molle interrompibili di `motion` con i preset in `src/lib/molla.ts`
|
|
(DD-021): `molla.ui` di default, `molla.slancio` solo dopo un gesto con inerzia,
|
|
`molla.foglio` per drawer e cambi di vista. `proietta()` calcola dove finirebbe un elemento
|
|
lanciato, così swipe come quello del calendario atterrano dove il gesto stava andando.
|
|
`src/lib/motion.ts` conserva solo il rilevamento del movimento ridotto e i coriandoli.
|
|
|
|
## Trappole note
|
|
|
|
Cose che si scoprono solo rompendole; ognuna ha la sua decisione o il suo documento.
|
|
|
|
- **Non invalidare la cache**: dopo una mutazione si usa `setQueryData`, non `invalidateQueries`
|
|
([EFFICIENZA_CLOUD.md](EFFICIENZA_CLOUD.md)).
|
|
- **Niente database dai componenti**: solo tramite `src/lib/` (DD-013).
|
|
- **Tabelle presenti ma non usate**: `giocatori`, `eventi`, `presenze` (DD-014) e `promemoria_push`.
|
|
Il modello in uso è `giocatori_squadra`, `eventi_app`, `risposte_presenze`.
|
|
- **Route in `src/routes/api/public/`**: usano la service role e saltano la RLS, quindi chiedono
|
|
le credenziali (DD-024). Una nuova route che avvisa la squadra deve farlo.
|
|
- **`convocati` vuoto significa tutta la rosa**, non nessuno (DD-027).
|
|
- **L'allenatore non è un giocatore**: è fuori da `inRosa()`, non vota e non riceve i promemoria
|
|
(DD-034, DD-039).
|
|
- **Cancellare un evento** pulisce a cascata le tabelle collegate; una tabella nuova legata
|
|
agli eventi va aggiunta al trigger (DD-029).
|
|
- **Dev e produzione condividono lo stesso Supabase**: le prove distruttive si fanno sullo stack
|
|
locale ([OPERATIONS.md](OPERATIONS.md)). Il seed gira solo in locale.
|
|
- **Migration**: mai riscrivere una già applicata; un `CHECK` sul tipo di `notifiche_utente` va
|
|
esteso con una migration nuova quando nasce un tipo nuovo.
|
|
- **Dipendenze**: `bunfig.toml` impone `minimumReleaseAge = 24h`; escluderne un pacchetto richiede
|
|
conferma dell'utente.
|
|
|
|
## Aggiungere un modulo o una funzione
|
|
|
|
Nell'ordine, perché il progetto è document-first (DD-002):
|
|
|
|
1. scheda in `docs/modules/<nome>.md` (stato, file principali, obiettivo, dati, permessi) e riga in
|
|
[ROADMAP.md](ROADMAP.md); se la scelta è non ovvia, una `DD-XXX` in [decisions/](decisions/README.md);
|
|
2. se serve lo schema: tabella documentata in [DATABASE.md](DATABASE.md), **nuova** migration,
|
|
permessi di scrittura nella sua tabella e test in `test/integration/permessi.test.ts`;
|
|
3. logica pura in `src/lib/<nome>-core.ts` con test in `test/unit/`; hook e accesso ai dati in
|
|
`src/lib/<nome>.ts`; componenti in `src/components/crapp/`, riusando quelli esistenti;
|
|
4. se tocca le notifiche: il catalogo in [modules/notifiche.md](modules/notifiche.md);
|
|
5. voce in [CHANGELOG.md](CHANGELOG.md) sotto `[Non rilasciato]` e checklist «Fine lavoro» di
|
|
[AGENTS.md](../AGENTS.md).
|
|
|
|
## Comandi
|
|
|
|
```bash
|
|
npm run dev # vite dev su http://localhost:8080
|
|
npm run build # build di produzione (nitro)
|
|
npm run lint # eslint (include prettier come regola)
|
|
npm run typecheck # controllo dei tipi TypeScript (tsc, senza generare file)
|
|
npm run format # prettier --write .
|
|
npm run test # test unit (veloci, senza rete né database)
|
|
npm run test:integration # route server vere
|
|
npm run test:e2e # percorsi sull'app servita
|
|
npm run test:all # tutto
|
|
```
|
|
|
|
Database di sviluppo in locale (Docker), alternativo al progetto Supabase cloud:
|
|
|
|
```bash
|
|
npx supabase start # avvia lo stack locale e applica tutte le migration
|
|
npx supabase stop # spegne i container
|
|
npx supabase db reset # ricrea il database da zero: migration + supabase/seed.sql
|
|
npx supabase db push # applica le migration al progetto cloud
|
|
```
|
|
|
|
`supabase/seed.sql` popola qualche profilo di prova e gira **solo in locale**. Serve perché
|
|
il progetto cloud è uno solo, condiviso tra sviluppo e produzione: lo stack locale è il posto
|
|
dove provare le migration distruttive senza toccare i dati veri.
|
|
|
|
## Branch e flusso di sviluppo
|
|
|
|
- `main` → produzione, deploy automatico su Vercel. È anche il branch di lavoro corrente.
|
|
- `develop` → preview Vercel; oggi indietro rispetto a `main`, non rappresenta lo stato attuale.
|
|
- `feature/…`, `fix/…`, `refactor/…` → lavori rischiosi o paralleli.
|
|
|
|
Su quale branch va un commit lo decide l'utente (DD-019): un assistente AI può consigliare un
|
|
branch dedicato, non sceglierlo. Poiché si lavora su `main`, la rete di sicurezza sono i test,
|
|
che vanno scritti insieme al codice e devono essere verdi (DD-020, [test/README.md](../test/README.md)).
|