docs(02-03): complete admin client workspace plan summary
- SUMMARY.md for Plan 02-03: Radix Tabs workspace with all four tab components - Documents deviations: Next.js 16 params await fix and FormData type annotations - Records all threat mitigations applied (T-02-10 through T-02-14)
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
---
|
||||
phase: "02-admin-area-interactive-features"
|
||||
plan: "03"
|
||||
subsystem: "admin-workspace"
|
||||
tags: [admin, tabs, server-actions, phases, tasks, payments, documents, comments]
|
||||
dependency_graph:
|
||||
requires: ["02-02"]
|
||||
provides: ["admin-client-workspace", "getClientFullDetail", "client-detail-mutations"]
|
||||
affects: ["admin-area", "client-data-management"]
|
||||
tech_stack:
|
||||
added:
|
||||
- "@radix-ui/react-tabs ^1.1.13 — tab primitive for client workspace"
|
||||
- "shadcn/ui tabs component — src/components/ui/tabs.tsx"
|
||||
patterns:
|
||||
- "Inline Server Action closures in RSC props (action={async (fd) => { 'use server'; ... }})"
|
||||
- "getClientFullDetail() — waterfall DB query assembling full client data in one call"
|
||||
- "Server-side allowlist validation on all status mutations before db.update()"
|
||||
key_files:
|
||||
created:
|
||||
- src/app/admin/clients/[id]/page.tsx
|
||||
- src/app/admin/clients/[id]/actions.ts
|
||||
- src/components/admin/tabs/PhasesTab.tsx
|
||||
- src/components/admin/tabs/PaymentsTab.tsx
|
||||
- src/components/admin/tabs/DocumentsTab.tsx
|
||||
- src/components/admin/tabs/CommentsTab.tsx
|
||||
- src/components/ui/tabs.tsx
|
||||
modified:
|
||||
- src/lib/admin-queries.ts
|
||||
- package.json
|
||||
- package-lock.json
|
||||
decisions:
|
||||
- "Inline Server Action closures capture clientId/phaseId/taskId from RSC scope — no cross-client pollution (T-02-14)"
|
||||
- "approved_at immutability enforced by omission in addDeliverable — field not set in insert, never updated"
|
||||
- "quote_items never queried in getClientFullDetail — accepted_total is the only price surface returned"
|
||||
- "params in Next.js 16 App Router must be awaited (Promise<{ id: string }>) — applied as deviation fix"
|
||||
metrics:
|
||||
duration_minutes: 25
|
||||
completed_date: "2026-05-15"
|
||||
tasks_completed: 2
|
||||
tasks_total: 2
|
||||
files_created: 7
|
||||
files_modified: 3
|
||||
---
|
||||
|
||||
# Phase 2 Plan 03: Admin Client Workspace (Tabs) Summary
|
||||
|
||||
**One-liner:** Full-featured admin client workspace with Radix Tabs covering phases/tasks, payments, documents, and comments — all mutations via inline Server Actions with server-side validation.
|
||||
|
||||
## What Was Built
|
||||
|
||||
The `/admin/clients/[id]` route delivers a complete project management workspace for the admin. Four tabs cover every concern of a client's lifecycle:
|
||||
|
||||
- **Fasi & Task** — Add phases and nested tasks, update phase/task status with select dropdowns
|
||||
- **Pagamenti** — Edit `accepted_total` (auto-splits to 50% per payment row), update payment status (sets `paid_at` on saldato)
|
||||
- **Documenti** — Add document links (label + external URL) and delete them
|
||||
- **Commenti** — Read all client/admin comments chronologically; post admin replies against any task or deliverable
|
||||
|
||||
All mutations are Server Actions in `src/app/admin/clients/[id]/actions.ts`. The page calls `getClientFullDetail()` which assembles client + phases + tasks + deliverables + payments + documents + notes + comments in a single waterfall query sequence.
|
||||
|
||||
## Tasks Completed
|
||||
|
||||
| Task | Name | Commit | Key Files |
|
||||
|------|------|--------|-----------|
|
||||
| 1 | Install tabs, add getClientFullDetail, create Server Actions | 7733566 | package.json, admin-queries.ts, [id]/actions.ts, ui/tabs.tsx |
|
||||
| 2 | Build detail page and all four tab components | 59a46d3 | [id]/page.tsx, PhasesTab, PaymentsTab, DocumentsTab, CommentsTab |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] Next.js 16 params must be awaited**
|
||||
- **Found during:** Task 2
|
||||
- **Issue:** In Next.js 16 (project uses 16.2.6), dynamic route `params` is a `Promise<{ id: string }>` not a plain object. The plan's template used `params.id` directly which would fail at runtime.
|
||||
- **Fix:** Changed page signature to `params: Promise<{ id: string }>` and added `const { id } = await params;` before use.
|
||||
- **Files modified:** src/app/admin/clients/[id]/page.tsx
|
||||
|
||||
**2. [Rule 2 - Missing critical functionality] Explicit TypeScript types on Server Action closure params**
|
||||
- **Found during:** Task 2
|
||||
- **Issue:** Inline closures `async (fd) => { "use server"; ... }` lacked explicit `FormData` type annotation, which could cause TypeScript inference issues.
|
||||
- **Fix:** Added explicit `: FormData` type annotation on all closure parameters.
|
||||
- **Files modified:** PhasesTab.tsx, PaymentsTab.tsx, DocumentsTab.tsx, CommentsTab.tsx
|
||||
|
||||
## Architecture Constraints Respected
|
||||
|
||||
- `clients.token` — Read-only in this plan. Never used as primary key.
|
||||
- `quote_items` — Not queried anywhere in this plan. `accepted_total` is the only price value exposed.
|
||||
- `deliverables.approved_at` — Enforced by omission: `addDeliverable` inserts `status: "pending"` with no `approved_at` field.
|
||||
- Two independent auth paths — Admin workspace is under `/admin/*`, protected by middleware session from Phase 2 Plan 01.
|
||||
|
||||
## Security Notes (Threat Register Mitigations Applied)
|
||||
|
||||
- **T-02-10:** `updateTaskStatus` and `updatePaymentStatus` both validate status against an allowlist before any `db.update()` call.
|
||||
- **T-02-11:** `deleteDocument` is only reachable through admin-protected routes; no client can reach it without a valid Auth.js session.
|
||||
- **T-02-13:** `postAdminComment` parses the composite `"type:id"` entity value and validates `entity_type` is exactly `"task"` or `"deliverable"` before insert.
|
||||
- **T-02-14:** Inline closures capture `clientId`, `phaseId`, `taskId` from the Server Component's own scope, preventing cross-client data pollution.
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None — all data is live from the database via `getClientFullDetail()`.
|
||||
|
||||
## Threat Flags
|
||||
|
||||
None — no new network endpoints, auth paths, or schema changes introduced beyond what the plan's threat model covers.
|
||||
|
||||
## Self-Check
|
||||
|
||||
- [x] src/app/admin/clients/[id]/page.tsx exists
|
||||
- [x] src/app/admin/clients/[id]/actions.ts exists
|
||||
- [x] src/components/admin/tabs/PhasesTab.tsx exists (153 lines > 60 min)
|
||||
- [x] src/components/admin/tabs/PaymentsTab.tsx exists (96 lines > 40 min)
|
||||
- [x] src/components/admin/tabs/DocumentsTab.tsx exists
|
||||
- [x] src/components/admin/tabs/CommentsTab.tsx exists
|
||||
- [x] Commit 7733566 exists (Task 1)
|
||||
- [x] Commit 59a46d3 exists (Task 2)
|
||||
- [x] npm run build passes cleanly
|
||||
|
||||
## Self-Check: PASSED
|
||||
Reference in New Issue
Block a user