fix(01-foundation): resolve plan checker blockers — 3 fixes across 01-02, 01-03, 01-04

- 01-02: wave corrected from 1 to 2 (has depends_on: ["01-01"])
- 01-03: middleware rewritten to Edge-compatible fetch pattern; internal API route
  app/api/internal/validate-token/route.ts handles DB query in Node.js runtime;
  tasks/deliverables queries scoped with inArray(); accepted_total null-coalesced
- 01-04: Task 1 and Task 6 merged → 5 tasks total (was 6, exceeded threshold)
- STATE.md: updated to reflect Phase 1 planning verified, ready for execution

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Simone Cavalli
2026-05-13 15:20:50 +02:00
parent 81c667838f
commit 2123dc9d00
4 changed files with 267 additions and 148 deletions
@@ -97,14 +97,17 @@ Output: Fully rendered client portal with all DASH-02 through DASH-10 requiremen
<tasks>
<task type="auto">
<name>Task 1: Update tailwind.config.ts with light & clean design tokens and extend globals.css</name>
<name>Task 1: Configure design tokens (tailwind.config.ts + globals.css) and wire app/c/[token]/page.tsx to ClientDashboard</name>
<files>
tailwind.config.ts
src/app/globals.css
app/c/[token]/page.tsx
</files>
<read_first>
tailwind.config.ts (current bootstrap)
src/app/globals.css (current bootstrap)
src/components/client-dashboard.tsx (will exist after Task 2 — read after Task 2 completes)
src/lib/client-view.ts (ClientView interface)
</read_first>
<action>
Update `tailwind.config.ts` to define light & clean design tokens:
@@ -190,7 +193,6 @@ Output: Fully rendered client portal with all DASH-02 through DASH-10 requiremen
line-height: 1.6;
}
/* Typography */
h1 {
@apply text-3xl font-bold tracking-tight;
}
@@ -211,7 +213,6 @@ Output: Fully rendered client portal with all DASH-02 through DASH-10 requiremen
@apply text-accent hover:underline transition-colors;
}
/* Subtle border utilities */
.border-subtle {
@apply border border-border-light;
}
@@ -220,16 +221,62 @@ Output: Fully rendered client portal with all DASH-02 through DASH-10 requiremen
@apply bg-bg-subtle;
}
```
Update `app/c/[token]/page.tsx` to replace the Plan 03 placeholder with the full ClientDashboard render:
```typescript
import { getClientView } from '@/lib/client-view';
import { ClientDashboard } from '@/components/client-dashboard';
import { notFound } from 'next/navigation';
export const revalidate = 60;
export async function generateMetadata({
params,
}: {
params: { token: string };
}) {
const view = await getClientView(params.token);
if (!view) {
return { title: 'Not Found' };
}
return {
title: `${view.client.brand_name} — Project Status | iamcavalli`,
description: view.client.brief || 'Project status dashboard',
};
}
export default async function ClientPage({
params,
}: {
params: { token: string };
}) {
const view = await getClientView(params.token);
if (!view) {
notFound();
}
return <ClientDashboard view={view} />;
}
```
Note: `getClientView` is called twice (once in `generateMetadata`, once in `ClientPage`). Next.js 15 deduplicates fetch calls within the same render, and since this is a DB query via Drizzle (not fetch), use React `cache()` in `client-view.ts` if double-call is a concern — acceptable for Phase 1 given low traffic.
</action>
<verify>
<automated>grep -q "colors:" tailwind.config.ts && echo "Color tokens defined"</automated>
<automated>grep -q "primary\|accent\|success" tailwind.config.ts && echo "Key colors present"</automated>
<automated>grep -q "@tailwind" src/app/globals.css && echo "Tailwind directives in globals.css"</automated>
<automated>npm run build 2>&1 | grep -v "warning" | grep -q "error" && echo "TypeScript errors" || echo "Build OK"</automated>
<automated>grep -q "ClientDashboard" app/c/\[token\]/page.tsx && echo "ClientDashboard wired in page"</automated>
<automated>grep -q "generateMetadata" app/c/\[token\]/page.tsx && echo "Dynamic metadata present"</automated>
</verify>
<acceptance_criteria>
- `tailwind.config.ts` contains color tokens: primary, secondary, accent, success, warning
- `globals.css` includes Tailwind directives and base typography
- `app/c/[token]/page.tsx` renders `<ClientDashboard view={view} />` with dynamic metadata
- 404 returned if token invalid
- `npm run build` succeeds
</acceptance_criteria>
</task>
@@ -552,14 +599,12 @@ Output: Fully rendered client portal with all DASH-02 through DASH-10 requiremen
</task>
<task type="auto">
<name>Task 4: Create PaymentStatus, DocumentsSection, and NotesSection components</name>
<name>Task 4: Create PaymentStatus component (accepted_total + payment rows with status badges)</name>
<files>
src/components/payment-status.tsx
src/components/documents-section.tsx
src/components/notes-section.tsx
</files>
<read_first>
src/lib/client-view.ts (payments, documents, notes shapes)
src/lib/client-view.ts (payments shape, PaymentStatus type)
.planning/phases/01-foundation-client-dashboard/01-CONTEXT.md (D-10, D-11)
</read_first>
<action>
@@ -637,6 +682,37 @@ Output: Fully rendered client portal with all DASH-02 through DASH-10 requiremen
}
```
Key points:
- Shows `accepted_total` formatted as Euro currency — NEVER individual line-item amounts
- Two payment rows (Acconto 50%, Saldo 50%) with status badges only
- Status badge colors: da_saldare = blue, inviata = yellow, saldato = green
- Card + Badge from shadcn/ui
</action>
<verify>
<automated>test -f src/components/payment-status.tsx && echo "PaymentStatus component exists"</automated>
<automated>grep -q "export function PaymentStatus" src/components/payment-status.tsx && echo "Component exported"</automated>
<automated>grep -q "accepted_total" src/components/payment-status.tsx && echo "Total displayed"</automated>
<automated>grep -q "da_saldare\|inviata\|saldato" src/components/payment-status.tsx && echo "Status config present"</automated>
</verify>
<acceptance_criteria>
- Component exists and is exported
- Displays accepted_total formatted as Euro (no individual amounts)
- Renders payment rows with status badges (da_saldare/inviata/saldato)
- Uses shadcn/ui Card and Badge
</acceptance_criteria>
</task>
<task type="auto">
<name>Task 5: Create DocumentsSection and NotesSection components (external links + read-only notes)</name>
<files>
src/components/documents-section.tsx
src/components/notes-section.tsx
</files>
<read_first>
src/lib/client-view.ts (documents and notes shapes)
.planning/phases/01-foundation-client-dashboard/01-CONTEXT.md (D-12)
</read_first>
<action>
Create `src/components/documents-section.tsx`:
```typescript
@@ -721,99 +797,26 @@ Output: Fully rendered client portal with all DASH-02 through DASH-10 requiremen
```
Key points:
- PaymentStatus: shows accepted_total + 2 payment rows (Acconto 50%, Saldo 50%) with status badges (no amounts)
- DocumentsSection: clickable external links with ExternalLink icon
- NotesSection: read-only notes with formatted timestamps
- All use Card + Badge components from shadcn/ui
- DocumentsSection: clickable external links with ExternalLink icon, `rel="noopener noreferrer"` for security
- NotesSection: read-only, client never writes (admin writes in Phase 2 admin area)
- NotesSection: empty state shown as italic hint when no notes exist
- Timestamps formatted in Italian locale
</action>
<verify>
<automated>test -f src/components/payment-status.tsx && echo "PaymentStatus component exists"</automated>
<automated>test -f src/components/documents-section.tsx && echo "DocumentsSection component exists"</automated>
<automated>test -f src/components/notes-section.tsx && echo "NotesSection component exists"</automated>
<automated>grep -q "accepted_total" src/components/payment-status.tsx && echo "Total displayed"</automated>
<automated>grep -q "ExternalLink" src/components/documents-section.tsx && echo "Link icon present"</automated>
<automated>grep -q "export function DocumentsSection" src/components/documents-section.tsx && echo "DocumentsSection exported"</automated>
<automated>grep -q "export function NotesSection" src/components/notes-section.tsx && echo "NotesSection exported"</automated>
<automated>grep -q "noopener noreferrer" src/components/documents-section.tsx && echo "External link security present"</automated>
</verify>
<acceptance_criteria>
- All three components exist and are exported
- PaymentStatus displays accepted_total + 2 payment rows with status (no amounts)
- DocumentsSection shows clickable external links
- NotesSection shows read-only notes with timestamps (or empty state)
- All components use shadcn/ui Card and Badge
- Both components exist and are exported
- DocumentsSection renders clickable external links with ExternalLink icon and secure rel attributes
- NotesSection shows read-only notes with Italian-formatted timestamps
- NotesSection shows empty state hint when notes array is empty
</acceptance_criteria>
</task>
<task type="auto">
<name>Task 5: Update app/c/[token]/page.tsx to render ClientDashboard with real data</name>
<files>
app/c/[token]/page.tsx
</files>
<read_first>
src/components/client-dashboard.tsx
src/lib/client-view.ts
</read_first>
<action>
Update `app/c/[token]/page.tsx`:
```typescript
import { getClientView } from '@/lib/client-view';
import { ClientDashboard } from '@/components/client-dashboard';
import { notFound } from 'next/navigation';
export const revalidate = 60; // ISR: revalidate every 60 seconds
export async function generateMetadata({
params,
}: {
params: { token: string };
}) {
const view = await getClientView(params.token);
if (!view) {
return {
title: 'Not Found',
};
}
return {
title: `${view.client.brand_name} — Project Status | ClientHub`,
description: view.client.brief || 'Project status dashboard',
};
}
export default async function ClientPage({
params,
}: {
params: { token: string };
}) {
const view = await getClientView(params.token);
if (!view) {
notFound();
}
return <ClientDashboard view={view} />;
}
```
This page:
- Fetches ClientView data
- Returns 404 if not found
- Generates dynamic metadata with client brand name
- Renders ClientDashboard with real data
</action>
<verify>
<automated>test -f app/c/\[token\]/page.tsx && echo "Page file exists"</automated>
<automated>grep -q "ClientDashboard" app/c/\[token\]/page.tsx && echo "ClientDashboard rendered"</automated>
<automated>grep -q "getClientView" app/c/\[token\]/page.tsx && echo "Data fetched"</automated>
<automated>npm run build 2>&1 | grep -v "warning" | grep -q "error" && echo "Build errors" || echo "Build OK"</automated>
</verify>
<acceptance_criteria>
- Page renders ClientDashboard component with ClientView data
- 404 is returned if token is invalid
- Page metadata is dynamic (includes client brand name)
- `npm run build` succeeds
</acceptance_criteria>
</task>
</tasks>