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,273 @@
|
||||
---
|
||||
phase: "01-foundation-client-dashboard"
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- package.json
|
||||
- tsconfig.json
|
||||
- next.config.ts
|
||||
- src/app/layout.tsx
|
||||
- src/app/page.tsx
|
||||
- .env.local
|
||||
autonomous: true
|
||||
requirements:
|
||||
- DASH-01
|
||||
- DASH-02
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "Next.js 15 App Router is bootstrapped and compiles without errors"
|
||||
- "DATABASE_URL env var is set and Drizzle can connect to Postgres"
|
||||
- "A simple test route exists and responds with 200"
|
||||
- "TypeScript strict mode is enabled"
|
||||
artifacts:
|
||||
- path: "package.json"
|
||||
provides: "All dependencies for Next.js + Drizzle + auth + UI"
|
||||
contains: "next@15"
|
||||
- path: "src/app/layout.tsx"
|
||||
provides: "Root layout with Tailwind setup"
|
||||
min_lines: 15
|
||||
- path: ".env.local"
|
||||
provides: "DATABASE_URL pointing to Coolify Postgres"
|
||||
contains: "DATABASE_URL"
|
||||
key_links:
|
||||
- from: ".env.local"
|
||||
to: "Drizzle client initialization"
|
||||
via: "process.env.DATABASE_URL"
|
||||
pattern: "DATABASE_URL=postgres://"
|
||||
- from: "src/db/index.ts"
|
||||
to: "Postgres on Coolify"
|
||||
via: "postgres-js driver"
|
||||
pattern: "import.*postgres.*from.*postgres-js"
|
||||
---
|
||||
|
||||
<objective>
|
||||
**Walking Skeleton:** Bootstrap the Next.js project, install all Phase 1 dependencies, configure Tailwind, connect to the Postgres database on Coolify via Drizzle ORM, and verify the entire stack is operational with a simple test route.
|
||||
|
||||
Purpose: Establish the project foundation so subsequent plans can build on a known-good state. This plan proves Next.js 15 + Drizzle + postgres-js + Tailwind work together before writing any feature code.
|
||||
|
||||
Output: Runnable Next.js dev server (`npm run dev`) with DB connection confirmed, TypeScript types working, Tailwind CSS active, ready for schema creation.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/01-foundation-client-dashboard/01-CONTEXT.md
|
||||
@.planning/research/STACK.md
|
||||
@.planning/research/ARCHITECTURE.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Bootstrap Next.js 15 with TypeScript, App Router, src/ directory, and Tailwind CSS v4</name>
|
||||
<files>
|
||||
package.json
|
||||
tsconfig.json
|
||||
next.config.ts
|
||||
src/app/layout.tsx
|
||||
src/app/page.tsx
|
||||
tailwind.config.ts
|
||||
postcss.config.mjs
|
||||
.gitignore
|
||||
</files>
|
||||
<read_first>
|
||||
None (greenfield project)
|
||||
</read_first>
|
||||
<action>
|
||||
Execute: `npx create-next-app@latest . --typescript --tailwind --app --src-dir --eslint --import-alias '@/*'`
|
||||
|
||||
Verify created:
|
||||
- `src/` directory with `app/` subdirectory
|
||||
- `tsconfig.json` with `"strict": true`
|
||||
- `tailwind.config.ts` (v4, CSS-first)
|
||||
- `postcss.config.mjs`
|
||||
- Next.js 15.x in package.json
|
||||
|
||||
After creation, modify `src/app/layout.tsx`:
|
||||
- Import Tailwind globals: `import './globals.css'`
|
||||
- Set viewport and basic meta tags
|
||||
- Ensure `<html>` and `<body>` exist with proper className for Tailwind
|
||||
|
||||
Modify `src/app/page.tsx`:
|
||||
- Replace default template with a simple div: `<div className="text-center py-20">Welcome to ClientHub</div>`
|
||||
- Keep it minimal — this route will be replaced in Phase 2
|
||||
</action>
|
||||
<verify>
|
||||
<automated>grep -q "\"next\": \"^15" package.json && echo "Next.js 15 installed"</automated>
|
||||
<automated>grep -q "\"strict\": true" tsconfig.json && echo "TypeScript strict mode enabled"</automated>
|
||||
<automated>test -f src/app/layout.tsx && grep -q "globals.css" src/app/layout.tsx && echo "Tailwind globals imported"</automated>
|
||||
<automated>test -f next.config.ts && echo "next.config.ts exists"</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `npm install` succeeds without errors
|
||||
- `npm run build` succeeds (no TypeScript errors, no Next.js errors)
|
||||
- `npm run dev` starts server without crashing
|
||||
- Visiting http://localhost:3000 returns 200 and displays the welcome message
|
||||
</acceptance_criteria>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Install Drizzle ORM, postgres-js, and supporting libraries; create .env.local with DATABASE_URL</name>
|
||||
<files>
|
||||
package.json
|
||||
.env.local
|
||||
.env.example
|
||||
src/db/index.ts
|
||||
</files>
|
||||
<read_first>
|
||||
None (greenfield)
|
||||
</read_first>
|
||||
<action>
|
||||
Install packages:
|
||||
```
|
||||
npm install drizzle-orm postgres
|
||||
npm install -D drizzle-kit
|
||||
```
|
||||
|
||||
Note: The package is `postgres` (not `postgres-js` — that's the npm package name for postgres-js driver).
|
||||
|
||||
Create `src/db/index.ts`:
|
||||
```typescript
|
||||
import { Client } from 'postgres';
|
||||
import * as schema from './schema';
|
||||
|
||||
if (!process.env.DATABASE_URL) {
|
||||
throw new Error('DATABASE_URL env var is required');
|
||||
}
|
||||
|
||||
const client = new Client({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
});
|
||||
|
||||
export const db = drizzle(client, { schema });
|
||||
```
|
||||
|
||||
Create `.env.local`:
|
||||
```
|
||||
DATABASE_URL=postgresql://[user]:[password]@[coolify-host]:5432/[database]
|
||||
```
|
||||
Use the actual Coolify credentials. If not yet available, use a placeholder and update before plan 02.
|
||||
|
||||
Create `.env.example`:
|
||||
```
|
||||
DATABASE_URL=postgresql://user:password@host:5432/database
|
||||
```
|
||||
|
||||
Install additional dependencies:
|
||||
```
|
||||
npm install nanoid zod @hookform/resolvers react-hook-form
|
||||
npm install -D @types/node
|
||||
```
|
||||
|
||||
Auth.js will be installed in a later plan (Phase 2 only).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>grep -q "drizzle-orm" package.json && echo "Drizzle installed"</automated>
|
||||
<automated>grep -q "postgres" package.json && echo "postgres-js installed"</automated>
|
||||
<automated>grep -q "drizzle-kit" package.json && echo "drizzle-kit installed"</automated>
|
||||
<automated>test -f .env.local && grep -q "DATABASE_URL" .env.local && echo ".env.local exists with DATABASE_URL"</automated>
|
||||
<automated>test -f .env.example && echo ".env.example exists"</automated>
|
||||
<automated>grep -q "postgres" src/db/index.ts && echo "postgres-js driver imported in db/index.ts"</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `npm install` succeeds
|
||||
- `src/db/index.ts` exists and exports `db` object
|
||||
- `.env.local` contains DATABASE_URL (value will be filled in by executor or user)
|
||||
- `npm run build` succeeds with no import errors
|
||||
</acceptance_criteria>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Install shadcn/ui components and configure; add lucide-react icons</name>
|
||||
<files>
|
||||
package.json
|
||||
components.json
|
||||
src/components/ui/*.tsx (multiple)
|
||||
</files>
|
||||
<read_first>
|
||||
tailwind.config.ts
|
||||
</read_first>
|
||||
<action>
|
||||
Initialize shadcn/ui:
|
||||
```
|
||||
npx shadcn@latest init --yes
|
||||
```
|
||||
|
||||
This creates `components.json` with the proper configuration.
|
||||
|
||||
Add essential components for Phase 1:
|
||||
```
|
||||
npx shadcn@latest add button card badge progress input label select separator table textarea
|
||||
```
|
||||
|
||||
Install lucide-react:
|
||||
```
|
||||
npm install lucide-react
|
||||
```
|
||||
|
||||
Verify `src/components/ui/` directory contains all component files.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>test -f components.json && echo "components.json created"</automated>
|
||||
<automated>test -d src/components/ui && ls src/components/ui/ | wc -l | grep -qE "[0-9]+" && echo "UI components installed"</automated>
|
||||
<automated>grep -q "lucide-react" package.json && echo "lucide-react installed"</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `components.json` exists with proper shadcn configuration
|
||||
- At least 8 component files exist in `src/components/ui/`
|
||||
- `npm run build` succeeds
|
||||
</acceptance_criteria>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| Client (browser) → API | Clients access `/c/[token]/*` routes; middleware must validate token |
|
||||
| Client (browser) → Database | Drizzle queries filtered by token; no client can see other clients' data |
|
||||
| Admin → Vercel environment variables | DATABASE_URL, future ADMIN_PASSWORD must be secret |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|-------------|-----------------|
|
||||
| T-01-001 | Information Disclosure | DATABASE_URL in .env.local | mitigate | Never commit .env.local; .gitignore enforces this; use Vercel Secrets for production |
|
||||
| T-01-002 | Tampering | Schema initialization | mitigate | Use Drizzle migrations + drizzle-kit push before any data is written; immutable migration history |
|
||||
| T-01-003 | Denial of Service | Database connection pooling | accept | postgres-js handles connection lifecycle; Coolify Postgres has resource limits acceptable for Phase 1 scale |
|
||||
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
After plan execution:
|
||||
1. Run `npm run build` → no errors
|
||||
2. Run `npm run dev` → server starts on http://localhost:3000
|
||||
3. Visit http://localhost:3000 → page loads with welcome message
|
||||
4. Check `src/db/index.ts` → imports postgres-js correctly
|
||||
5. Check `.env.local` → DATABASE_URL is set (value may be placeholder)
|
||||
6. Check `components.json` → exists with @/ alias
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Next.js dev server starts and responds to requests
|
||||
- TypeScript compiles without errors
|
||||
- Tailwind CSS is active (can verify via DevTools)
|
||||
- Database connection string is configured (even if not yet tested with actual DB)
|
||||
- All Phase 1 dependencies are installed
|
||||
- Ready to proceed to Task 02 (schema creation)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/01-foundation-client-dashboard/01-01-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user