2026-08-07 15:36:02 +02:00
# Architettura del progetto
2026-08-30 16:26:14 +02:00
Come è fatta CrAPP: stack, organizzazione del codice, flusso di sviluppo. È il documento di
riferimento tecnico — `CLAUDE.md` non ripete questi contenuti, li richiama.
2026-08-07 15:36:02 +02:00
2026-08-30 16:26:14 +02:00
## Stack
2026-08-07 15:36:02 +02:00
2026-09-05 12:42:55 +02:00
| 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 |
2026-08-07 15:36:02 +02:00
2026-08-30 16:26:14 +02:00
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.
2026-08-07 15:36:02 +02:00
## Struttura del progetto
2026-08-30 16:26:14 +02:00
```
2026-08-07 15:36:02 +02:00
src/
2026-08-30 16:26:14 +02:00
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
```
2026-08-07 15:36:02 +02:00
2026-08-30 16:26:14 +02:00
## Punti fermi
2026-08-07 15:36:02 +02:00
2026-08-30 16:26:14 +02:00
- **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
2026-08-30 17:58:24 +02:00
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/` .
2026-08-31 18:21:34 +02:00
- **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` ).
2026-08-07 15:36:02 +02:00
2026-08-30 16:26:14 +02:00
## Livello dati
2026-08-07 15:36:02 +02:00
2026-08-30 16:26:14 +02:00
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:
2026-08-07 15:36:02 +02:00
2026-08-30 16:26:14 +02:00
- 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
2026-09-06 19:30:49 +02:00
`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;
2026-08-30 16:26:14 +02:00
- 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.
2026-08-07 15:36:02 +02:00
2026-08-30 16:26:14 +02:00
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 )).
2026-08-07 15:36:02 +02:00
2026-08-30 16:26:14 +02:00
Vincoli di efficienza cloud — niente polling, cache lunga, `setQueryData` invece di
`invalidateQueries` — in [EFFICIENZA_CLOUD.md ](EFFICIENZA_CLOUD.md ).
2026-08-07 15:36:02 +02:00
2026-08-30 16:26:14 +02:00
Badge e statistiche sono calcolati a runtime dai dati, non persistiti (DD-007). La
gamification deve restare equa tra ruoli (DD-008).
2026-08-07 15:36:02 +02:00
2026-09-03 10:17:13 +02:00
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.
2026-08-07 15:36:02 +02:00
2026-08-30 16:26:14 +02:00
## UI
2026-08-07 15:36:02 +02:00
2026-09-05 12:42:55 +02:00
Componenti condivisi in `src/components/crapp/` (`ui-bits.tsx` per `Card` , `PageHeader` ,
2026-10-06 11:44:58 +02:00
`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
2026-09-05 12:42:55 +02:00
schermate, pochi click.
2026-10-06 11:50:28 +02:00
**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` .
2026-09-05 12:42:55 +02:00
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.
2026-08-07 15:36:02 +02:00
2026-10-01 08:53:38 +02:00
## 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 ).
2026-08-30 16:26:14 +02:00
## 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)
2026-09-24 12:25:35 +02:00
npm run typecheck # controllo dei tipi TypeScript (tsc, senza generare file)
2026-08-30 16:26:14 +02:00
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
```
2026-08-30 17:58:24 +02:00
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.
2026-08-30 16:26:14 +02:00
## Branch e flusso di sviluppo
2026-09-04 23:14:55 +02:00
- `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.
2026-08-30 16:26:14 +02:00
2026-09-04 23:14:55 +02:00
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 )).