chore: riorganizzazione e pulizia della cartella di progetto

Rimossi i doppioni e gli artefatti accumulati, senza cancellare nulla di
definitivo: tutto cio' che serviva una revisione e' parcheggiato in cestino/
(gitignored), documentato in cestino/LEGGIMI.md.

- .planning/phases/01-10: 10 cartelle identiche byte-per-byte alle copie in
  .planning/milestones/v1.0-phases e v2.0-phases. HANDOFF.md:33 documentava che
  furono copiate e non spostate, lasciando la pulizia 'facoltativa in futuro'.
  Verificata l'identita' con diff -rq prima di spostare ciascuna.
- scripts/: 13 script one-off gia' eseguiti (push-*, migrate-*, validate-*,
  verify-12-03-*) piu' reset-and-import-services.ts, che cancella dati.
  Restano i 3 riutilizzabili: seed, import-services-notion, import-service-offer-tags.
- CLAUDE-SECURITY-20260727-210226/: cartella di lavoro della run interrotta.

Cancellati subito, senza revisione: 6 .DS_Store, le due cache .impeccable/
(una era dentro src/) e tsconfig.tsbuildinfo.

.gitignore: aggiunti cestino/, .impeccable/ e CLAUDE-SECURITY-*/ per evitare
che si riformino.

src/ non e' stato toccato: la struttura e' dettata dall'App Router di Next.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 14:13:32 +02:00
parent fb6ab92fd0
commit 94b3b2f766
98 changed files with 432 additions and 30516 deletions
+38
View File
@@ -1,3 +1,7 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# ClientHub
Portale clienti per consulente di personal branding. Admin area + dashboard cliente via link segreto.
@@ -5,6 +9,22 @@ Portale clienti per consulente di personal branding. Admin area + dashboard clie
## Stack
Next.js 16 App Router · Neon Postgres · Drizzle ORM · Auth.js v4 · Tailwind v4 · shadcn/ui · Zod · nanoid
## Commands
- `npm run dev` · `npm run build` · `npm run lint` (lint script is bare `eslint`, not `next lint`)
- **There is no test suite** — no vitest/jest/playwright, no `test` script. Don't go looking for one and don't invent test commands. `npm run build` is the verification of record (it typechecks).
- One-off scripts: `npx tsx scripts/<name>.ts` with `DATABASE_URL` in the env (`tsx` is not a devDependency — it must go through `npx`). `scripts/` holds historical import/seed/verify utilities; nothing there is part of the runtime.
## Architecture
- **`src/proxy.ts` is the middleware.** Next 16 names it `proxy`, not `middleware` — searching for `middleware.ts` finds nothing. Matcher: `/admin/*`, `/client/*`, `/quote/*`.
- **Admin auth is a double gate.** `proxy.ts` redirects unauthenticated `/admin` traffic *and* stamps two headers (`x-admin-pathname` + `x-admin-gate`, a digest derived from `NEXTAUTH_SECRET`). `src/app/admin/layout.tsx` is a second, independent gate: it verifies that digest with `safeEqual` and **fails closed by rendering** (not redirecting — that would loop) when the proxy never ran. Shared helper: `src/lib/admin-gate.ts`, on Web Crypto so it works in both the edge and node runtimes. Reuse those helpers; don't bypass or reimplement either gate.
- **Client access resolution.** `/client/<x>`: per-IP rate limit (`src/lib/rate-limit.ts`, 20/min), then an HTTP fetch **to `localhost:$PORT`** against `/api/internal/validate-slug`, falling back to `/api/internal/validate-token`. Those internal routes are guarded by `INTERNAL_SECRET`. The localhost base URL is deliberate (hairpin NAT inside Docker), not a leftover.
- **The query layers are split, and that split is what enforces LOCKED constraint #2.** `src/lib/client-view.ts` exposes only client-safe projections — it deliberately omits `quote_items`, service prices, and payment amounts. `src/lib/admin-queries.ts` and the other `*-queries.ts` are admin-only. New client-facing queries go in `client-view.ts`; never import `admin-queries` from a `/client/*` route.
- **Two distinct commercial artifacts, easy to confuse:**
- `/quote/[token]``quotes` table, single tier, 21-char nanoid token, served by `src/lib/quote-service.ts`
- `/preventivo/[slug]``proposals` table, an A/B/C tier deck generated by the AI agent in `src/lib/proposal/` (`agent.ts` calls the Anthropic SDK, output is Zod-validated by `schema.ts`, then `assembleProposal` in `assemble.ts` merges it with offer data). States: `draft|published|accepted|rejected`.
- **Offer model:** `offer_macros``offer_micros` (tiers A/B/C) → `services`, wired through join tables (`offer_tier_services`, `offer_phase_services`, …). `importOfferIntoProject` in `src/app/admin/projects/project-actions.ts` turns an offer into phases/tasks by grouping services on `services.fase`.
- **Auth:** a single admin credential from env (`ADMIN_EMAIL`/`ADMIN_PASSWORD`) — no users table — with a stateless JWT session (`src/lib/auth.ts`).
## Architecture Constraints (LOCKED)
1. `clients.token` = campo separato rotatable, MAI primary key
2. `quote_items` MAI esposti via client API — solo `accepted_total` al cliente
@@ -12,6 +32,24 @@ Next.js 16 App Router · Neon Postgres · Drizzle ORM · Auth.js v4 · Tailwind
4. Auth: `/client/[token]/*` → middleware token check | `/admin/*` → Auth.js session
5. No file hosting v1 — documenti come URL esterni
## Conventions
- **Mutations are Server Actions**, colocated as `actions.ts` (or `*-actions.ts`) inside the route folder. There is no REST API for admin: `src/app/api/` holds only NextAuth, the two internal validation routes, and two client endpoints.
- **Migrations** are hand-written SQL in `src/db/migrations/NNNN_name.sql`, with gaps in the numbering (0002 doesn't exist — that's expected). `drizzle.config.ts` is present but `drizzle-kit generate` is broken: edit `src/db/schema.ts` **and** write the SQL by hand, keeping the two in sync. The `scripts/push-*.ts` files are the old way of applying migrations — don't use them, the SSH/docker-exec procedure below is authoritative.
- **Slugs and tokens are bearer credentials.** Never commit their values (migration files included). They're generated with `customAlphabet` (CSPRNG) in `src/app/admin/clients/new/actions.ts` — never `Math.random()`.
- **Language:** code and comments mix English and Italian; all user-facing UI and error messages are **Italian**.
- **AI-generated HTML:** never `dangerouslySetInnerHTML` on model output — use `src/components/public/proposal/RichText.tsx`, which whitelists bold/emphasis only.
## Design System
Single source of truth: **`design-reference/DESIGN-SYSTEM.md`** ("Quiet Luxury" v1.0).
- Cardinal rule: **semantic tokens only** (`bg-card`, `text-muted-foreground`, `border-border`) — never raw Tailwind palette classes or hex literals. That's what makes dual light/dark work off the single `.dark` class toggle (FOUC guard in `src/app/layout.tsx`, tokens in `src/app/globals.css`).
- Fonts: Plus Jakarta Sans for UI, Geist Mono for numeric/tabular cells (prices, counts, dates).
- Per-page HTML mocks live in `design-reference/pagina-*/` — replicate them faithfully.
- Reuse the existing primitives before building new ones: `StatusBadge`, `SearchInput`, `SegmentedToggle`, `editable-cell`, `option-select`/`option-multi-select` in `src/components/ui/`, and the shell in `src/components/admin/AdminShell.tsx`.
- **`.planning/UI-RULES.md` and `.planning/DESIGN-SYSTEM.md` are SUPERSEDED** — they mandate hex literals and forbid semantic tokens, the exact inverse of the current rule. Don't follow them.
Other docs: `STATUS.md` (current project status + backlog) · `.planning/STATE.md` (GSD state, milestone v2.3) · `.planning/SECURITY-*.md` (2026-07 audit).
## GSD Workflow
Planning in `.planning/`. Use `/gsd-plan-phase N``/gsd-execute-phase N`. State in `.planning/STATE.md`.