Files
Simone Cavalli 20c25cbb70 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)
2026-05-15 21:30:34 +02:00

6.0 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
02-admin-area-interactive-features 03 admin-workspace
admin
tabs
server-actions
phases
tasks
payments
documents
comments
requires provides affects
02-02
admin-client-workspace
getClientFullDetail
client-detail-mutations
admin-area
client-data-management
added patterns
@radix-ui/react-tabs ^1.1.13 — tab primitive for client workspace
shadcn/ui tabs component — src/components/ui/tabs.tsx
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()
created modified
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
src/lib/admin-queries.ts
package.json
package-lock.json
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
duration_minutes completed_date tasks_completed tasks_total files_created files_modified
25 2026-05-15 2 2 7 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

  • src/app/admin/clients/[id]/page.tsx exists
  • src/app/admin/clients/[id]/actions.ts exists
  • src/components/admin/tabs/PhasesTab.tsx exists (153 lines > 60 min)
  • src/components/admin/tabs/PaymentsTab.tsx exists (96 lines > 40 min)
  • src/components/admin/tabs/DocumentsTab.tsx exists
  • src/components/admin/tabs/CommentsTab.tsx exists
  • Commit 7733566 exists (Task 1)
  • Commit 59a46d3 exists (Task 2)
  • npm run build passes cleanly

Self-Check: PASSED