Files
CRAPP/CLAUDE.md
T
davideandClaude Sonnet 5 142a7fbaa9 Aggiungo a CLAUDE.md gli apprendimenti di sessione su tooling e ambiente locale
bun non è nel PATH in ambienti sandbox (usare npx/bunx), il repo non è
prettier-clean ovunque (filtrare i falsi positivi preesistenti), e la porta
Postgres locale può differire dal default 5432 nei .env di sviluppo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 15:41:14 +02:00

152 lines
8.7 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What this is
CrAPP — a mobile-first web app for managing an amateur volleyball team (CRAP Volley): attendance,
matches/trainings/events, live match scouting, player stats/badges, CSI league standings, push
notifications. Italian-language codebase (routes, variables, comments). Built with Lovable
(lovable.dev); pushes to `main` sync back into the Lovable editor — avoid rewriting published git
history (force-push, rebase/amend/squash of pushed commits).
## Commands
Package manager is **bun** (`bun.lock` is the lockfile; `package-lock.json` also exists but bun is
primary — see `bunfig.toml`).
```sh
bun run dev # vite dev server
bun run build # production build (nitro/vite)
bun run build:dev # development-mode build
bun run preview # preview a build
bun run lint # eslint .
bun run format # prettier --write .
```
If `bun` isn't on PATH (sandboxed/CI environments), use the `npx`/`bunx` equivalents:
`npx tsc --noEmit -p tsconfig.json` (typecheck, no npm script exists for this),
`npx eslint src` (not `npx eslint .` — a full-repo scan chokes on the root-owned
`infra/supabase/docker/volumes/db/data` directory), `npx prettier --write <files>`.
This repo is not fully `prettier`-clean — a fresh `eslint`/`prettier --check` run surfaces
hundreds of pre-existing formatting errors unrelated to any given change. Filter with
`grep -v "prettier/prettier"` to see real lint issues, and run `prettier --write` only on the
files you touched, not the whole repo.
There is no test runner configured in this repo.
Adding a dependency: `bunfig.toml` enforces a 24h supply-chain guard (`minimumReleaseAge`) on new
packages; only pre-listed `@lovable.dev/*` packages bypass it. Confirm with the user before adding
new bypass entries.
## Architecture
**Stack**: TanStack Start (React 19, file-based router) + Vite, styled with Tailwind v4 +
shadcn/radix components, data via `@supabase/supabase-js`, TanStack Query for client cache.
### Routing
File-based routing under `src/routes/` (see `src/routes/README.md`). One root layout,
`src/routes/__root.tsx`, wraps every page — preserve its `<Outlet />`. `$id.tsx` = dynamic segment,
`{-$category}.tsx` = optional segment, `$.tsx` = splat (`_splat` param). Do not hand-create
`src/pages/` or Next/Remix-style layout files. `src/routeTree.gen.ts` is auto-generated — never
edit it directly.
Server-only HTTP endpoints live under `src/routes/api/public/*.ts` (e.g. push subscription,
palloni/presenze reminders) — plain HTTP handlers, not proprietary edge functions, so they can run
under any Node host or scheduler (system cron, pg_cron, etc).
### Server entry / SSR error handling
`src/start.ts` registers global middleware: `attachSupabaseAuth` (client-side function middleware
that attaches the Supabase bearer token to every server-fn RPC — see
`src/integrations/supabase/auth-attacher.ts`, which is **auto-generated**, do not hand-edit) and an
error middleware + explicit CSRF middleware (`createCsrfMiddleware`) for server functions. Defining
`src/start.ts` opts out of Start's automatic CSRF middleware, so this file must keep re-adding it.
`src/server.ts` wraps the generated TanStack Start server entry and normalizes a specific h3
failure mode: h3 swallows in-handler throws into a 500 JSON body
(`{"unhandled":true,"message":"HTTPError"}`) that a plain try/catch never sees, so it's detected by
inspecting the response body and converted into `renderErrorPage()`'s HTML error page.
### Data layer — portability rule
The project must remain deployable on plain Node.js + PostgreSQL, not locked into Lovable Cloud
(see `docs/PORTABILITA.md`). Concretely:
- **Never query the database (or the Strapi CMS) from components.** All data access goes through
modules in `src/lib/*.ts` (e.g. `palloni.ts`, `mvp-voti.ts`, `eventi.ts`, `presenze.ts`, `rosa.ts`,
`scout-*.ts`) — each exports TanStack Query hooks (`useX`) wrapping `supabase.from(...)` or, for
Strapi-backed data, `strapiFetch(...)` from `src/lib/strapi-client.ts` (see `rosa-base.ts`,
`classifica-csi.ts`, `storico-match.ts`). This keeps the backend swappable in one place.
- `src/integrations/lovable/*` (social login) is optional and unused by any screen — safe to
remove without impact.
- No provider-exclusive features: no edge functions, no Lovable-only auth as the sole login method,
no proprietary storage. Config only via standard env vars (`DATABASE_URL` / `SUPABASE_URL` +
keys, `VAPID_*`), never hardcoded.
- SQL migrations in `supabase/migrations/` must use standard PostgreSQL, no provider-exclusive
extensions.
### Cloud-efficiency rules (small paid-tier budget)
The Supabase/Lovable Cloud plan is metered (~20 credits/month, ~17 users), so these constraints are
load-bearing, not style preferences:
- No polling (`refetchInterval`) against the database; prefer local sync via
BroadcastChannel/storage.
- Global QueryClient (`src/router.tsx`) is deliberately configured for infrequent refetching:
`staleTime` 5 min, `gcTime` 30 min, `refetchOnWindowFocus/Mount/Reconnect` all off, `retry: 1`.
Don't override this per-query without a reason.
- After a mutation, update the query cache with `setQueryData` (see `useAssegnaTurno` in
`src/lib/palloni.ts` for the pattern) rather than `invalidateQueries`, to avoid an extra read.
- Stats/badges/standings are "write once, read many": computed and persisted once (e.g. at match
end), never recomputed on every page open.
- Live match scouting: only the scoring user writes; everyone else reads already-saved data.
- Player roster, CSI league standings and match history come from a self-hosted **Strapi CMS**
(`infra/strapi/`, its own Postgres database on the same cluster as Supabase) — editing happens
only in Strapi's own admin panel (`/cms/admin`), never in app UI. The app reads them read-only via
`src/lib/rosa-base.ts` / `classifica-csi.ts` / `storico-match.ts` (long `staleTime`, no polling —
same cloud-efficiency rules apply). Player identity is the stable `codice` field (`g1`, `g2`, …)
on the Strapi `giocatore` content type, **not** Strapi's own numeric id — every Supabase table
that references a player by id (`pagelle_voti`, `cacche_partita`, MVP votes, ball-duty turns,
presenze, scout actions) keys on that string, so it must never change for an existing player.
- A scouted match result (`ScoutMatch`, from `scout.tsx` / `scout-store.ts`) is saved to the
scoring device's `localStorage` (key `crapp-scout-v1`, still the source `useRosa`/
`giocatoriConScout` read for stats) **and** pushed best-effort to Strapi's `scout-match-finale`
content type via `/api/public/scout-finale` at match end, so it becomes visible cross-device in
the admin panel — no retry if that POST fails, the local save already succeeded either way. The
in-progress resume state still goes only to the `scout_live` Supabase table, unrelated to Strapi.
- Local dev quirk: this machine's `infra/supabase/docker/.env` may customize `POSTGRES_PORT`
away from the documented default `5432` (e.g. to avoid colliding with another project's
Postgres) — check that file before assuming the template's port when wiring up a new service
to the same database.
- Push notifications only for high-value events (convocations, training/match reminders, ball-duty
turn, final result) — no chat/photo/video features.
### Auth
Supabase Auth (GoTrue), attached via middleware rather than per-request boilerplate — see
`auth-attacher.ts` / `auth-middleware.ts` above. Self-hostable; not tied to Lovable-proprietary
auth.
## Project memory (`mem/`)
`mem/index.md` indexes feature-level design notes (`mem/features/*.md`) — currently the portability
rule and the cloud-efficiency rules summarized above. Check there before adding a feature that
might conflict with either constraint.
## Conventions
- **Language: Italian.** Code comments and git commit messages must be written in Italian, matching
the rest of the codebase (routes, identifiers, existing comments/commits are already Italian).
- Path alias `@/*``src/*` (see `tsconfig.json`).
- Prettier: 100-char width, double quotes (`singleQuote: false`), trailing commas everywhere.
- TypeScript strict mode plus extra strictness: `noUncheckedIndexedAccess`,
`exactOptionalPropertyTypes`, `noImplicitReturns`, `noImplicitOverride`,
`noPropertyAccessFromIndexSignature`.
- `vite.config.ts` is intentionally minimal: `@lovable.dev/vite-tanstack-config` already bundles
TanStack devtools, `tanstackStart`, `viteReact`, `tailwindcss`, `tsConfigPaths`, nitro, env
injection, and the `@` alias — do not re-add any of those plugins manually or the app breaks with
duplicate plugins.