---
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"
---

<objective>
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.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.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

<interfaces>
From schema.ts:
```typescript
export const documents = pgTable("documents", {
  id: text("id").primaryKey(),
  // ...
  signers: jsonb("signers").$type<DocumentSigner[]>(),
  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)
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Add signing token counts to dashboard query</name>
  <read_first>
    - 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)
  </read_first>
  <files>teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx</files>
  <action>
    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<number>`count(*)`.as('total'),
        signed: sql<number>`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 `<DocumentsTable rows={enrichedRows} ...>` 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,
    ```
  </action>
  <verify>
    <automated>cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 | head -30</automated>
  </verify>
  <acceptance_criteria>
    - `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
  </acceptance_criteria>
  <done>Dashboard query fetches signing token counts per document and passes enriched rows to DocumentsTable</done>
</task>

<task type="auto">
  <name>Task 2: Render N/M signed badge in DocumentsTable Status column</name>
  <read_first>
    - 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)
  </read_first>
  <files>teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx</files>
  <action>
    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 `<td>` cell (currently just `<StatusBadge status={row.status} />`), add the N/M badge after the StatusBadge:

    ```tsx
    <td style={{ padding: "0.875rem 1.5rem" }}>
      <StatusBadge status={row.status} />
      {row.hasMultipleSigners && row.status === 'Sent' && row.totalSigners != null && row.totalSigners > 0 && (
        <span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-blue-50 text-blue-700 ml-1.5">
          {row.signedCount ?? 0}/{row.totalSigners} signed
        </span>
      )}
    </td>
    ```

    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).
  </action>
  <verify>
    <automated>cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 | head -30</automated>
  </verify>
  <acceptance_criteria>
    - `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
  </acceptance_criteria>
  <done>
    - 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
  </done>
</task>

</tasks>

<verification>
- `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
</verification>

<success_criteria>
- 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
</success_criteria>

<output>
After completion, create `.planning/phases/16-multi-signer-ui/16-04-SUMMARY.md`
</output>
