Files
Simone Cavalli 81c667838f docs(01-foundation-client-dashboard): complete phase 1 planning with 5-plan structure
Create comprehensive phase plans for Foundation & Client Dashboard:
- 01-01-PLAN.md: Walking Skeleton (Next.js 15 bootstrap + DB connection)
- 01-02-PLAN.md: Database schema (11 tables, Drizzle ORM, drizzle-kit push)
- 01-03-PLAN.md: Middleware token validation + ClientView type + data fetching
- 01-04-PLAN.md: Client dashboard UI (header, timeline, progress, payments, docs, notes)
- 01-05-PLAN.md: Seed script + DNS CNAME configuration

Also create SKELETON.md documenting locked architectural decisions for all future phases:
- Next.js 15 + Drizzle + postgres-js driver (Coolify Postgres)
- Token as separate rotatable field (not PK)
- ClientView enforcement (no quote_items exposed to client API)
- Approved_at immutable audit trail
- Two independent auth systems (client token + admin session)
- Vercel deployment with custom domain

Update ROADMAP.md to mark Phase 1 as planned (5 plans created) and ready for execution.

All plans follow MVP vertical-slice structure with 2-3 tasks per plan.
Walking Skeleton proves the entire stack works end-to-end.
Requirements mapping: DASH-01 through DASH-04, DASH-07 through DASH-10 covered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:27:19 +02:00

303 lines
11 KiB
Markdown

# ClientHub — Walking Skeleton (Phase 1)
**Project:** ClientHub — Freelancer Client Portal
**Phase:** 01 — Foundation & Client Dashboard
**Date:** 2026-05-13
**Status:** Blueprint (decisions below are LOCKED for all subsequent phases)
---
## Project Architecture — Locked Decisions
This Walking Skeleton establishes the architectural foundation for all future phases. These decisions are **immutable** without explicit user approval.
### Core Stack
| Layer | Technology | Why | Locked? |
|-------|-----------|-----|---------|
| **Framework** | Next.js 15 (App Router, TypeScript, src/) | Server Components + Edge Middleware for performance; Vercel-native | ✅ YES |
| **Database** | Postgres on Coolify (Hetzner), via `postgres-js` driver | Self-hosted (no Neon/Supabase cost); persistent via external DB | ✅ YES |
| **ORM** | Drizzle ORM with postgres-js | Zero-cost serverless driver; schema-as-code migrations | ✅ YES |
| **UI** | Tailwind CSS v4 + shadcn/ui components | Utility-first, copied components, mobile-first | ✅ YES |
| **Auth (Admin)** | Auth.js v4 Credentials provider (Phase 2) | Single admin account, JWT cookie | ✅ YES |
| **Auth (Client)** | Custom Next.js Middleware + token validation | No session store needed; token in URL | ✅ YES |
| **Token Generation** | nanoid (21 chars) | Cryptographically secure, URL-safe, non-enumerable | ✅ YES |
| **Deployment** | Vercel (Hobby plan) + custom subdomain | Native Next.js; auto-SSL; single deploy command | ✅ YES |
### Data Model — Locked Entities
All tables below **must** exist and maintain these field definitions. Modifications require explicit approval.
```
clients
id UUID PK (stable, never changes)
name TEXT
brand_name TEXT
brief TEXT
token UUID UNIQUE ← SEPARATE from PK, rotatable
accepted_total NUMERIC ← denormalized, only price client sees
created_at TIMESTAMPTZ
phases
id UUID PK
client_id UUID FK → clients.id
title TEXT
sort_order INT
status TEXT (upcoming | active | done)
tasks
id UUID PK
phase_id UUID FK → phases.id
title TEXT
description TEXT
status TEXT (todo | in_progress | done)
sort_order INT
deliverables
id UUID PK
task_id UUID FK → tasks.id
title TEXT
url TEXT
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 FK → 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 FK → clients.id
label TEXT
url TEXT ← external links only, no file uploads
created_at TIMESTAMPTZ
notes
id UUID PK
client_id UUID FK → clients.id
body 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 FK → clients.id
service_id UUID FK → service_catalog.id
quantity NUMERIC
unit_price NUMERIC
subtotal NUMERIC
← NEVER exposed via client API
```
### Critical Design Principles — Locked
1. **`clients.token` is NOT the primary key.** Data is keyed by stable UUID `id`. Token is a separate, rotatable field. Rotation is a single UPDATE statement.
2. **Client API never exposes `quote_items`.** Server-side filtering enforces this; not a UI trick. The `accepted_total` field is the only price the client API returns.
3. **`deliverables.approved_at` is immutable.** Once set, it cannot be unset. Provides an audit trail for disputes.
4. **Two independent auth systems:**
- `/c/[token]/*` → Middleware validates token, 404 on miss
- `/admin/*` → Auth.js session check (Phase 2)
- No overlap; no shared session store
5. **No file hosting in v1.** Documents are external URLs only (Google Drive, PDFs, Figma links). File uploads → Phase 3+.
6. **No email in v1.** Deliverables are dashboard links, not email attachments. Email integration → Phase 2+.
### Directory Structure — Locked
```
IAMCAVALLI/
├── src/
│ ├── app/
│ │ ├── c/[token]/
│ │ │ ├── page.tsx ← Client dashboard route
│ │ │ └── layout.tsx
│ │ ├── admin/ ← Phase 2 (protected by middleware)
│ │ │ ├── page.tsx ← Admin dashboard
│ │ │ ├── clients/
│ │ │ │ ├── page.tsx
│ │ │ │ └── [id]/
│ │ │ ├── catalog/
│ │ │ └── ...
│ │ ├── layout.tsx
│ │ └── globals.css
│ ├── components/
│ │ ├── ui/ ← shadcn/ui components
│ │ ├── client-dashboard.tsx
│ │ ├── phase-timeline.tsx
│ │ ├── payment-status.tsx
│ │ ├── documents-section.tsx
│ │ ├── notes-section.tsx
│ │ └── ...
│ ├── db/
│ │ ├── schema.ts ← Drizzle schema (source of truth)
│ │ ├── migrations/ ← Generated by drizzle-kit
│ │ └── index.ts ← db client export
│ ├── lib/
│ │ ├── client-view.ts ← ClientView type + queries
│ │ ├── auth.ts ← Phase 2: Auth helpers
│ │ └── ...
│ └── middleware.ts ← Token validation at edge
├── scripts/
│ ├── seed.ts ← Insert first test client
│ └── ...
├── .env.local ← DATABASE_URL, secrets
├── drizzle.config.ts
├── next.config.ts
├── tailwind.config.ts
├── tsconfig.json
├── package.json
└── .planning/
├── ROADMAP.md
├── REQUIREMENTS.md
├── STATE.md
└── phases/
└── 01-foundation-client-dashboard/
├── 01-CONTEXT.md
├── 01-DISCUSSION-LOG.md
├── 01-01-PLAN.md
├── 01-02-PLAN.md
├── 01-03-PLAN.md
├── 01-04-PLAN.md
├── 01-05-PLAN.md
└── SKELETON.md
```
### Deployment — Locked
- **Host:** Vercel (Hobby plan, $0/month for Phase 1 scale)
- **Domain:** welcomeclient.iamcavalli.net (CNAME to Vercel DNS)
- **Database:** Postgres on Coolify (existing Hetzner server, Simone manages)
- **Environment:** DATABASE_URL injected via Vercel Secrets
- **SSL/TLS:** Vercel auto-provisioning for custom domain
### API Routes Structure (Phase 2+)
Routes created in Phase 2 will follow this pattern:
**Client-facing routes** (`/api/c/[token]/...`):
- No authentication library needed
- Middleware validates token
- Routes return ClientView shape only
**Admin routes** (`/api/admin/...`):
- Require Auth.js session
- Access full AdminView including quote_items
- CRUD operations on all entities
### UI Layer Principles — Locked
- **Light & clean visual style:** White backgrounds, strong typography, subtle gray accents
- **Mobile-first design:** Tailwind defaults ensure responsive behavior
- **Semantic HTML:** Proper heading hierarchy, accessible form controls
- **No client-side state management libraries:** Server Components + Server Actions for Phase 1-2
- **Progress visualization:** Global bar (top) + per-phase bars (sections) + task status badges
- **Brand consistency:** iamcavalli logo in corner, client brand_name prominent
### Security Assumptions — Locked
1. **Database credentials are secrets:** DATABASE_URL never logged, committed, or exposed
2. **Tokens are non-enumerable:** 21-character nanoid cannot be guessed
3. **Client API is isolated:** Admin data never leaks to `/c/[token]/*` routes
4. **Admin password** (Phase 2): env var `ADMIN_PASSWORD` protects `/admin/*` before Auth.js is added
5. **No PII in logs:** Payment amounts and tokens never logged to Vercel logs
---
## What This Skeleton Delivers
After Phase 1 execution:
**Functional client portal:**
- One client can open their secret link on any device
- Dashboard shows project phases, tasks, status, payments, documents, decision log
- No login required; link is the secret
**Production-ready infrastructure:**
- Database is live on Coolify Postgres
- Custom domain is verified and HTTPS-enabled
- Application is deployed on Vercel
- One-command deploy pipeline (`git push → Vercel auto-build`)
**Developer-friendly codebase:**
- TypeScript with strict mode
- Drizzle ORM manages schema as code
- Git-tracked migrations (reproducible database state)
- One seed script to populate test data
- No manual SQL; no database browser required
**Foundation for Phase 2:**
- Data model is stable and comprehensive
- Admin CRUD can be built without schema changes
- Auth.js integration point is clear
- Comments and approvals schema already exists
---
## Phase 1 → Phase 2 Contract
Phase 2 will extend this skeleton by:
1. **Admin authentication:** Middleware check + Auth.js session on `/admin/*` routes
2. **CRUD operations:** Forms and API routes to edit clients, phases, tasks, deliverables, payments
3. **Comments & approvals:** Client-facing UI for commenting and approving deliverables
4. **Admin workspace:** Dashboard to manage all clients with state summary and quick actions
5. **Payment management:** Update payment status, send payment reminders
**No schema changes required.** All Phase 2 features fit into the existing data model.
---
## Validation Checklist (End of Phase 1)
- [ ] Next.js 15 application compiles without TypeScript errors
- [ ] Database schema is live on Coolify Postgres (all 11 tables)
- [ ] Middleware validates tokens at edge
- [ ] Client portal route renders complete dashboard with seeded data
- [ ] Seed script inserts test client and prints shareable link
- [ ] DNS CNAME is live: welcomeclient.iamcavalli.net → Vercel
- [ ] Application is deployed on Vercel (accessible via https://welcomeclient.iamcavalli.net/)
- [ ] Invalid tokens return 404 (no information leakage)
- [ ] Payment amounts are NOT visible on client dashboard (only status)
- [ ] Mobile layout is responsive and readable
- [ ] All DASH-01 through DASH-10 requirements are satisfied (except DASH-05, DASH-06 which are Phase 2)
---
## Future Extensibility Notes
This skeleton is designed for:
- **Phase 2:** Admin CRUD + comments + approvals (no schema changes)
- **Phase 3:** Service catalog + quote builder (admin-only, client sees only total)
- **Phase 4 (v2):** Claude AI onboarding flow (optional; may defer indefinitely)
- **Beyond:** Multi-team support, real file uploads, email automation (major schema rework)
The current design is intentionally simple. Future phases should resist scope creep and maintain the "client sees only what they need" principle.
---
**Skeleton locked:** 2026-05-13
**Next checkpoint:** Phase 2 planning (`/gsd-plan-phase 2`)