--- phase: 16-multi-signer-ui plan: 04 type: execute wave: 3 depends_on: ["16-02", "16-03"] files_modified: - teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx - teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx autonomous: true requirements: [MSIGN-09] must_haves: truths: - "Multi-signer Sent documents show N/M signed badge in Status column" - "Single-signer documents show no N/M badge (unchanged)" - "Fully signed documents show existing Signed badge only (no N/M)" - "N/M is computed from signingTokens with usedAt IS NOT NULL" artifacts: - path: "teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx" provides: "Server-side query joining signingTokens for per-signer completion count" contains: "signingTokens" - path: "teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx" provides: "N/M signed badge rendering in Status column" contains: "signed" key_links: - from: "dashboard/page.tsx query" to: "DocumentsTable rows" via: "signedCount and totalSigners fields on row data" pattern: "signedCount|totalSigners" - from: "DocumentsTable" to: "N/M badge" via: "conditional render when totalSigners > 0 and status === Sent" pattern: "totalSigners.*>.*0" --- Add the N/M signed badge to the dashboard documents table for multi-signer documents per D-11, D-12, D-13. Purpose: MSIGN-09 (dashboard shows per-signer completion status). Agent sees at a glance how many signers have completed for documents that are partially signed. Output: Dashboard query includes signing token counts; DocumentsTable renders "N/M signed" badge next to Status for multi-signer Sent documents. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/16-multi-signer-ui/16-CONTEXT.md @.planning/phases/16-multi-signer-ui/16-UI-SPEC.md From schema.ts: ```typescript export const documents = pgTable("documents", { id: text("id").primaryKey(), // ... signers: jsonb("signers").$type(), status: text("status").$type<"Draft" | "Sent" | "Viewed" | "Signed">().default("Draft").notNull(), // ... }); export const signingTokens = pgTable('signing_tokens', { id: text('id').primaryKey(), documentId: text('document_id').notNull().references(() => documents.id, { onDelete: 'cascade' }), signerEmail: text('signer_email'), createdAt: timestamp('created_at').defaultNow().notNull(), expiresAt: timestamp('expires_at').notNull(), usedAt: timestamp('used_at'), }); ``` DocumentsTable current row type: ```typescript type DocumentRow = { id: string; name: string; clientName: string | null; status: "Draft" | "Sent" | "Viewed" | "Signed"; sentAt: Date | null; signedAt: Date | null; clientId: string; }; ``` StatusBadge: ```typescript export function StatusBadge({ status }: { status: "Draft" | "Sent" | "Viewed" | "Signed" }); ``` UI-SPEC badge spec: - Style: `inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-blue-50 text-blue-700 ml-1.5` - Text: "N/M signed" - Only for multi-signer (signers non-empty) AND status "Sent" - Not for status "Signed" (existing badge sufficient) Task 1: Add signing token counts to dashboard query - teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx - teressa-copeland-homes/src/lib/db/schema.ts (signingTokens table, documents.signers) - .planning/phases/16-multi-signer-ui/16-CONTEXT.md (D-11, D-12, D-13) - .planning/phases/16-multi-signer-ui/16-UI-SPEC.md (Component Inventory section 5) teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx Import `signingTokens` from `@/lib/db/schema` and `sql, isNotNull, count` from `drizzle-orm`. The dashboard currently uses a simple select+leftJoin query. To add signer counts, use a subquery approach to avoid N+1: After the main `allRows` query, compute signing progress for multi-signer documents. Use a separate query for signing token counts to keep it simple: ```typescript import { signingTokens } from "@/lib/db/schema"; import { sql } from "drizzle-orm"; // After allRows query, fetch signing token progress for all documents in one query const tokenCounts = await db .select({ documentId: signingTokens.documentId, total: sql`count(*)`.as('total'), signed: sql`count(${signingTokens.usedAt})`.as('signed'), }) .from(signingTokens) .groupBy(signingTokens.documentId); const tokenMap = new Map(tokenCounts.map(t => [t.documentId, { total: Number(t.total), signed: Number(t.signed) }])); ``` Then augment the rows passed to DocumentsTable: ```typescript const enrichedRows = filteredRows.map(row => { const tc = tokenMap.get(row.id); return { ...row, signedCount: tc?.signed ?? null, totalSigners: tc?.total ?? null, }; }); ``` Pass `enrichedRows` to `` instead of `filteredRows`. Also need to add `signers` to the allRows select to know which documents are multi-signer: ```typescript signers: documents.signers, ``` And include it in enrichedRows: ```typescript hasMultipleSigners: Array.isArray(row.signers) && row.signers.length > 0, ``` cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 | head -30 - `grep -n "signingTokens" teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx` shows import and query - `grep -n "signedCount" teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx` shows field enrichment - `grep -n "totalSigners" teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx` shows field enrichment - `grep -n "hasMultipleSigners" teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx` shows multi-signer detection - `npx tsc --noEmit` passes Dashboard query fetches signing token counts per document and passes enriched rows to DocumentsTable Task 2: Render N/M signed badge in DocumentsTable Status column - teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx - teressa-copeland-homes/src/app/portal/_components/StatusBadge.tsx - .planning/phases/16-multi-signer-ui/16-CONTEXT.md (D-11, D-12, D-13) - .planning/phases/16-multi-signer-ui/16-UI-SPEC.md (Component Inventory section 5, badge spec) teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx Update the `DocumentRow` type to include optional new fields: ```typescript type DocumentRow = { id: string; name: string; clientName: string | null; status: "Draft" | "Sent" | "Viewed" | "Signed"; sentAt: Date | null; signedAt: Date | null; clientId: string; signedCount?: number | null; totalSigners?: number | null; hasMultipleSigners?: boolean; }; ``` In the Status `` cell (currently just ``), add the N/M badge after the StatusBadge: ```tsx {row.hasMultipleSigners && row.status === 'Sent' && row.totalSigners != null && row.totalSigners > 0 && ( {row.signedCount ?? 0}/{row.totalSigners} signed )} ``` Per D-12: Only show for multi-signer documents (`hasMultipleSigners`). Per D-13: Only show for status "Sent" — not for "Signed" (existing Signed badge sufficient). The UI-SPEC specifies `bg-blue-50 text-blue-700` for the badge (matching the blue theme of Sent status). cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 | head -30 - `grep -n "signedCount" teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx` shows field in type and render - `grep -n "totalSigners" teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx` shows field in type and render - `grep -n "hasMultipleSigners" teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx` shows conditional - `grep -n "bg-blue-50 text-blue-700" teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx` shows badge styles - `grep -n "ml-1.5" teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx` shows margin from StatusBadge - `grep -n "signed" teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx` shows "signed" text in badge - `npx tsc --noEmit` passes - Multi-signer Sent documents show "N/M signed" badge next to Sent status badge per D-11 - Single-signer documents show no N/M badge per D-12 - Signed documents show only existing "Signed" badge per D-13 - Badge uses exact UI-SPEC classes: `inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-blue-50 text-blue-700 ml-1.5` - TypeScript compiles cleanly - `npx tsc --noEmit` passes - Dashboard query includes token count subquery - N/M badge renders only for multi-signer Sent documents - Badge styling matches UI-SPEC exactly - Dashboard shows "1/2 signed" badge for partially-signed multi-signer documents - No badge for single-signer or fully-signed documents - Badge computed server-side from signingTokens count - TypeScript compiles with no errors After completion, create `.planning/phases/16-multi-signer-ui/16-04-SUMMARY.md`