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>
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
# 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`)
|
||||
Reference in New Issue
Block a user