Files
simone 5bf5dfce71 infra(04-00): route /c/ → /client/, Dockerfile, Gitea deploy
- Rename src/app/c/[token] → src/app/client/[token]
- Update proxy.ts, ClientRow, admin client detail with /client/ path
- Add output: "standalone" to next.config.ts for Docker build
- Add Dockerfile (multi-stage, node:20-alpine) and .dockerignore
- Push schema to Coolify Postgres via SSH tunnel (drizzle-kit push ✓)
- Update CLAUDE.md constraint 4 to reflect /client/ route
- Add Phase 4 planning artifacts (04-00, 04-RESEARCH, 04-PATTERNS)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 16:12:05 +02:00

6.1 KiB

Architecture — ClientHub Freelancer Client Portal

Project: ClientHub (welcomeclient.iamcavalli.net) Researched: 2026-05-09 Confidence: HIGH


Component Boundaries

Single Next.js application on Vercel. No separate backend. All server logic lives in Route Handlers (/api/**). One Postgres database (Neon serverless) accessed via Drizzle ORM. Admin auth via env-var secret + cookie. Client access via UUID token in URL — no auth library needed for clients.

Component Responsibility Communicates With
Client Portal /c/[token] Read-only view: status, phases, tasks, deliverables, payments, documents API Routes (GET only)
Admin Dashboard /admin List all clients with status summary API Routes (full CRUD)
Admin Client Workspace /admin/clients/[id] Edit phases, tasks, deliverables, payments, documents API Routes (full CRUD)
Service Catalog Manager /admin/catalog CRUD on service items + unit prices API Routes (catalog entity)
Quote Builder /admin/clients/[id]/quote Compose quote from catalog items, write accepted_total to client row Catalog + API Routes
Comments System Client posts on task/deliverable; admin replies API Route POST
Approval Flow Client PATCHes a deliverable to approved API Route, validates token ownership
API Routes /api/** Validate token or admin session; query/mutate DB; return JSON Postgres only
Database Single source of truth API Routes only — never queried from browser

Data Flow

Client reading their dashboard:

Browser → GET /c/[token]
→ Next.js server component
→ DB: clients WHERE token = [token] → 404 if missing
→ JOIN: project + phases + tasks + deliverables + payments + documents
→ Omit: quote_items, service prices
→ Render read-only portal

Client posting a comment:

Browser → POST /api/comments { token, entity_type, entity_id, body }
→ Validate token → write comment { author: 'client' }
→ 201 → re-fetch thread

Client approving a deliverable:

Browser → PATCH /api/deliverables/[id]/approve { token }
→ Validate token owns deliverable → set status='approved', approved_at=now()
→ Return updated deliverable

Admin editing:

Browser (admin) → PATCH /api/admin/tasks/[id] + admin cookie
→ Validate session → update row → return updated task

Quote building:

Admin UI selects services → computes line items
→ POST /api/admin/clients/[id]/quote { line_items[], accepted_total }
→ Write quote_items rows + write clients.accepted_total (denormalized)
→ Client portal reads clients.accepted_total — never touches quote_items

Data Model

clients
  id              UUID PK
  name            TEXT
  brand_name      TEXT
  brief           TEXT
  token           UUID UNIQUE   ← the secret link key (separate from PK!)
  accepted_total  NUMERIC       ← denormalized; only price client ever sees
  created_at      TIMESTAMPTZ

phases
  id              UUID PK
  client_id       UUID → clients.id
  title           TEXT
  sort_order      INT
  status          TEXT  (upcoming | active | done)

tasks
  id              UUID PK
  phase_id        UUID → phases.id
  title           TEXT
  description     TEXT
  status          TEXT  (todo | in_progress | done)
  sort_order      INT

deliverables
  id              UUID PK
  task_id         UUID → tasks.id
  title           TEXT
  url             TEXT          ← external link (Google Drive, PDF, etc.)
  status          TEXT  (pending | submitted | approved)
  approved_at     TIMESTAMPTZ  ← immutable audit trail

comments
  id              UUID PK
  entity_type     TEXT  (task | deliverable)
  entity_id       UUID
  author          TEXT  (client | admin)
  body            TEXT
  created_at      TIMESTAMPTZ

payments
  id              UUID PK
  client_id       UUID → clients.id
  label           TEXT  ("Acconto 50%" / "Saldo 50%")
  amount          NUMERIC
  status          TEXT  (da_saldare | inviata | saldato)
  paid_at         TIMESTAMPTZ

documents
  id              UUID PK
  client_id       UUID → clients.id
  label           TEXT
  url             TEXT
  created_at      TIMESTAMPTZ

service_catalog
  id              UUID PK
  name            TEXT
  description     TEXT
  unit_price      NUMERIC
  active          BOOLEAN

quote_items
  id              UUID PK
  client_id       UUID → clients.id
  service_id      UUID → service_catalog.id
  quantity        NUMERIC
  unit_price      NUMERIC       ← snapshot at time of quote
  subtotal        NUMERIC
  -- NEVER exposed via client API

Key design decisions:

  • clients.token is the only secret. Rotation = single UPDATE. No session store needed.
  • clients.accepted_total is deliberately denormalized so client API never touches quote_items.
  • Approval approved_at stored as immutable audit trail — disputes resolved by timestamp.
  • comments use entity_type + entity_id polymorphic pair — correct at this scale.
  • payments always two rows per client (created when quote is finalized).

Suggested Build Order

1. DB schema + migrations
   └─ everything depends on this

2. API: token lookup + project read (GET only)
   └─ unblocks client portal

3. Client portal UI /c/[token]
   └─ the core deliverable; clients need this first

4. Admin auth middleware (env-var secret, cookie check)
   └─ gate before admin routes go live

5. Admin: client list + client workspace CRUD
   └─ phases, tasks, status, documents, payments

6. Comments system + deliverable approval
   └─ depends on both client portal and admin workspace

7. Service catalog CRUD  ← can run parallel with step 5
   └─ independent of client-facing features

8. Quote builder
   └─ depends on catalog + client entity

9. Claude onboarding flow (v2)
   └─ depends on all CRUD APIs being complete

Roadmap Implications

  • Phase 1: DB schema + token API + client portal (all three coupled)
  • Phase 2: Admin auth + CRUD management + comments + approvals
  • Phase 3: Service catalog + quote builder
  • Phase 4 (v2): Claude onboarding flow