--- phase: 03-agent-portal-shell plan: 04 type: execute wave: 4 depends_on: [03-03] files_modified: - teressa-copeland-homes/src/app/portal/(protected)/clients/[id]/page.tsx - teressa-copeland-homes/src/app/portal/_components/ConfirmDialog.tsx autonomous: false requirements: [CLIENT-03] must_haves: truths: - "Agent clicks a client card and lands on /portal/clients/{id} — sees the client's name, email, and an Edit button" - "Agent clicks Edit on the client profile — a modal opens pre-filled with the client's current name and email" - "Agent submits the edit modal — the profile page updates with the new values" - "Agent clicks Delete Client — a confirmation dialog appears; confirming deletes the client and redirects to /portal/clients" - "Documents section on the profile page shows the same table style as the dashboard (status badge, date sent)" - "Agent can verify the full portal flow end-to-end in the browser: login → dashboard → clients → profile → edit → delete" artifacts: - path: "teressa-copeland-homes/src/app/portal/(protected)/clients/[id]/page.tsx" provides: "Client profile page: client header + edit/delete actions + documents table" - path: "teressa-copeland-homes/src/app/portal/_components/ConfirmDialog.tsx" provides: "Reusable confirmation dialog for destructive actions" key_links: - from: "clients/[id]/page.tsx" to: "updateClient (bound with id)" via: "ClientModal with mode='edit' + clientId prop" pattern: "ClientModal.*mode.*edit" - from: "ConfirmDialog confirm button" to: "deleteClient server action" via: "form action calling deleteClient(id) then redirect to /portal/clients" pattern: "deleteClient" - from: "clients/[id]/page.tsx" to: "DocumentsTable" via: "documents query filtered by clientId" pattern: "DocumentsTable.*showClientColumn.*false" --- Build the client profile page with edit and delete capabilities, then run a human verification checkpoint covering the complete Phase 3 portal flow. Purpose: CLIENT-03 requires the agent to view a client's profile and associated documents. Edit + delete round out the client management feature set. The checkpoint verifies all 4 Phase 3 success criteria in a real browser. Output: Working /portal/clients/[id] page with edit modal, delete confirmation, and documents table; human approval of complete Phase 3 portal. @/Users/ccopeland/.claude/get-shit-done/workflows/execute-plan.md @/Users/ccopeland/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/STATE.md @.planning/phases/03-agent-portal-shell/03-CONTEXT.md @.planning/phases/03-agent-portal-shell/03-03-SUMMARY.md From src/app/portal/_components/DocumentsTable.tsx: ```typescript type DocumentRow = { id: string; name: string; clientName: string | null; status: "Draft" | "Sent" | "Viewed" | "Signed"; sentAt: Date | null; clientId: string; }; export function DocumentsTable({ rows, showClientColumn }: Props): JSX.Element // Pass showClientColumn={false} on the profile page — client is implied ``` From src/app/portal/_components/ClientModal.tsx: ```typescript // Already supports edit mode: type Props = { isOpen: boolean; onClose: () => void; mode?: "create" | "edit"; clientId?: string; defaultName?: string; defaultEmail?: string; }; export function ClientModal(props: Props): JSX.Element | null ``` From src/lib/actions/clients.ts: ```typescript // updateClient uses bind pattern — in profile page: // const boundUpdate = updateClient.bind(null, client.id); export async function updateClient( id: string, _prevState: { error?: string } | null, formData: FormData ): Promise<{ error?: string; success?: boolean }> export async function deleteClient(id: string): Promise<{ error?: string; success?: boolean }> ``` Next.js 16 dynamic params (MUST await): ```typescript export default async function ClientProfilePage({ params, }: { params: Promise<{ id: string }>; }) { const { id } = await params; // CRITICAL — sync access throws at build time } ``` Drizzle query for client profile: ```typescript import { eq, desc, sql } from "drizzle-orm"; // Fetch client const [client] = await db.select().from(clients).where(eq(clients.id, id)); if (!client) notFound(); // import from next/navigation // Fetch documents for this client const docs = await db .select({ id: documents.id, name: documents.name, status: documents.status, sentAt: documents.sentAt, clientId: documents.clientId, clientName: sql`${clients.name}`, }) .from(documents) .leftJoin(clients, eq(documents.clientId, clients.id)) .where(eq(documents.clientId, id)) .orderBy(desc(documents.createdAt)); ``` Task 1: Client profile page with edit/delete and documents table teressa-copeland-homes/src/app/portal/(protected)/clients/[id]/page.tsx teressa-copeland-homes/src/app/portal/_components/ConfirmDialog.tsx **`src/app/portal/_components/ConfirmDialog.tsx`** ("use client"): Props: `{ isOpen: boolean; title: string; message: string; onConfirm: () => void; onCancel: () => void; confirmLabel?: string }` - Overlay + centered dialog box (same overlay pattern as ClientModal: `fixed inset-0 z-50 bg-black/40`) - Title, message text, Cancel button + Confirm button - Confirm button: red/destructive styling (`bg-red-600 text-white hover:bg-red-700`) - `confirmLabel` defaults to "Delete" **`src/app/portal/(protected)/clients/[id]/page.tsx`**: This page needs both server data AND client modal/dialog state. Use the same pattern as clients/page.tsx: server component fetches data, renders a "use client" inner component with the interactive parts. Server component (`default export`): 1. `const { id } = await params` — CRITICAL: params is a Promise in Next.js 16 2. Fetch client by id (use `notFound()` if not found) 3. Fetch documents for this client with LEFT JOIN 4. Render `` Inline "use client" component (`ClientProfileClient`) in the same file OR in `_components/ClientProfileClient.tsx`: Props: `{ client: { id: string; name: string; email: string }; docs: DocumentRow[] }` State: `const [isEditOpen, setIsEditOpen] = useState(false)` and `const [isDeleteOpen, setIsDeleteOpen] = useState(false)` Layout (two-section layout per CONTEXT.md decision): - **Header section**: white card (`bg-white rounded-xl shadow-sm p-6 mb-6`) - Back link: arrow + "Back to Clients" as a Next.js `` with `text-[var(--gold)] text-sm` - Client name as `

` (`text-[var(--navy)] text-2xl font-semibold`) - Email below the name (gray text) - Two buttons on the right: "Edit" button (opens edit modal) + "Delete Client" button (opens confirm dialog) - Edit button: outlined style (`border border-[var(--navy)] text-[var(--navy)] px-4 py-2 rounded-lg text-sm`) - Delete button: red outlined style (`border border-red-300 text-red-600 px-4 py-2 rounded-lg text-sm`) - **Documents section**: white card (`bg-white rounded-xl shadow-sm p-6`) - `

Documents

` heading - If `docs.length === 0`: "No documents yet." gray text - If `docs.length > 0`: `` **Edit modal**: ` setIsEditOpen(false)} mode="edit" clientId={client.id} defaultName={client.name} defaultEmail={client.email} />` **Delete flow**: - ` setIsDeleteOpen(false)} />` - `handleDelete`: call `await deleteClient(client.id)` then `router.push("/portal/clients")` — import `useRouter` from `next/navigation` PITFALL: `deleteClient` is a server action. To call it from a client component, import it and call it directly in an async event handler (Next.js 15+ allows this). The action runs server-side; the client awaits the promise. PITFALL: `await params` before any usage of `id` — synchronous access causes build failure in Next.js 16.
cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 Client profile page renders with client details header, edit/delete buttons, and documents table. ConfirmDialog component exported. TypeScript compiles cleanly.
Task 2: Full Phase 3 portal verification in browser Start the dev server and walk through the complete portal verification checklist below. none Human approval of all 5 checklist sections below. Agent approves all 5 portal sections: authentication, dashboard, clients list, client profile, navigation. Complete Phase 3 Agent Portal Shell: - /portal/dashboard — documents table with status filter dropdown; 4 seeded placeholder documents visible - /portal/clients — card grid with 2 seeded clients; "+ Add Client" button opens modal - /portal/clients/[id] — client profile with edit button (opens pre-filled modal), delete button (confirmation dialog), documents table for that client - Top nav on all portal pages with Dashboard and Clients links + sign-out button - Middleware protects all /portal/* routes Start the dev server: `cd teressa-copeland-homes && npm run dev` **1. Authentication and routing (Phase 1 preserved)** - Visit http://localhost:3000/portal/dashboard while logged out → should redirect to /agent/login - Log in with the seeded agent account → should land on /portal/dashboard (not /agent/dashboard) **2. Dashboard (DASH-01 + DASH-02)** - Verify you see a table with 4 documents: "Purchase Agreement - 842 Maple Dr", "Seller Disclosure - 842 Maple Dr", "Buyer Rep Agreement", "Purchase Agreement - 1205 Oak Ave" - Verify status badges: two "Signed" (green), one "Sent" (blue), one "Draft" (gray) - Verify Client column shows "Sarah Johnson" and "Mike Torres" - Select "Signed" in the filter dropdown → URL changes to ?status=Signed → table shows only 2 rows - Select "All statuses" → table returns to 4 rows **3. Clients list (CLIENT-01 + CLIENT-02)** - Visit http://localhost:3000/portal/clients - Verify card grid shows 2 client cards: Sarah Johnson and Mike Torres - Each card shows name, email, document count, and last activity date - Click "+ Add Client" → modal opens with name + email fields - Fill in a test client (e.g., "Test User" + "test@example.com") → click "Add Client" → modal closes → new card appears in grid **4. Client profile (CLIENT-03)** - Click on "Sarah Johnson" card → lands on /portal/clients/[sarah's id] - Verify header shows "Sarah Johnson", her email, Edit and Delete Client buttons - Verify documents section shows Sarah's 2 documents with status badges - Click "Edit" → modal opens pre-filled with "Sarah Johnson" and her email → change name → save → header updates - Click "Delete Client" → confirmation dialog appears → click Cancel → client is NOT deleted - (Optional) Delete the test client you created in step 3 → confirm → redirected to /portal/clients **5. Navigation** - Verify top nav is visible on all three portal pages - Click "Dashboard" nav link → navigates to /portal/dashboard - Click "Clients" nav link → navigates to /portal/clients - Current page should be highlighted in nav (gold underline or similar) - Sign out → redirects to /agent/login Type "approved" if all checklist items pass. Describe any issues found and I will fix them before you approve.
1. `npx tsc --noEmit` produces zero TypeScript errors 2. Client profile page uses `await params` (not synchronous params.id) 3. ConfirmDialog shows title, message, Cancel + Delete buttons 4. Edit modal opens pre-filled with client's current name and email 5. Human verifier approves all 5 portal sections in the browser - Agent can see client profile with name, email, and associated documents (CLIENT-03) - Agent can edit client name/email via modal — profile updates immediately - Agent can delete a client via confirmation dialog — redirected to clients list after - All four Phase 3 success criteria confirmed in real browser by agent - TypeScript compiles cleanly with zero errors After completion, create `.planning/phases/03-agent-portal-shell/03-04-SUMMARY.md`