--- phase: 16-multi-signer-ui plan: 01 type: execute wave: 1 depends_on: [] files_modified: - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/page.tsx - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx autonomous: true requirements: [MSIGN-01, MSIGN-02, MSIGN-03] must_haves: truths: - "DocumentPageClient holds signers state and threads it to both PreparePanel and PdfViewerWrapper/FieldPlacer" - "DocumentPageClient holds unassignedFieldIds state for send-block validation highlighting" - "Server page reads documents.signers from DB and passes to DocumentPageClient as initialSigners prop" - "PdfViewerWrapper passes signers and unassignedFieldIds through to FieldPlacer" artifacts: - path: "teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx" provides: "signers state, setSigners, unassignedFieldIds state, setUnassignedFieldIds — threaded to PreparePanel and PdfViewerWrapper" exports: ["DocumentPageClient"] - path: "teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/page.tsx" provides: "Server-side documents.signers fetch, passed as initialSigners to DocumentPageClient" - path: "teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx" provides: "Passes signers and unassignedFieldIds props through to PdfViewer/FieldPlacer" key_links: - from: "page.tsx" to: "DocumentPageClient" via: "initialSigners prop from db.query.documents" pattern: "initialSigners=.*doc\\.signers" - from: "DocumentPageClient" to: "PdfViewerWrapper" via: "signers and unassignedFieldIds props" pattern: "signers=.*unassignedFieldIds=" - from: "DocumentPageClient" to: "PreparePanel" via: "signers, onSignersChange, unassignedFieldIds, onUnassignedFieldIdsChange props" pattern: "signers=.*onSignersChange=" --- Thread multi-signer state through the component tree so PreparePanel and FieldPlacer can consume signers data in Wave 2. Purpose: Foundation wiring — DocumentPageClient is the state owner for signers (from Phase 14 schema) and unassignedFieldIds (for send-block validation). Without this, Wave 2 plans cannot receive or modify signer state. Output: Updated DocumentPageClient with signers + unassignedFieldIds state; server page fetching documents.signers; PdfViewerWrapper passing new props through. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/16-multi-signer-ui/16-CONTEXT.md @.planning/phases/16-multi-signer-ui/16-UI-SPEC.md From src/lib/db/schema.ts: ```typescript export interface DocumentSigner { email: string; color: string; } export interface SignatureFieldData { id: string; page: number; x: number; y: number; width: number; height: number; type?: SignatureFieldType; signerEmail?: string; } export function isClientVisibleField(field: SignatureFieldData): boolean; export function getFieldType(field: SignatureFieldData): SignatureFieldType; export function getSignerEmail(field: SignatureFieldData, fallbackEmail: string): string; ``` From DocumentPageClient.tsx (current props): ```typescript interface DocumentPageClientProps { docId: string; docStatus: string; defaultEmail: string; clientName: string; agentDownloadUrl?: string | null; signedAt?: Date | null; clientPropertyAddress?: string | null; } ``` From PdfViewerWrapper.tsx (current props): ```typescript { docId: string; docStatus?: string; onFieldsChanged?: () => void; selectedFieldId?: string | null; textFillData?: Record; onFieldSelect?: (fieldId: string | null) => void; onFieldValueChange?: (fieldId: string, value: string) => void; aiPlacementKey?: number; } ``` From FieldPlacer.tsx (current props — FieldPlacerProps interface at line 155): ```typescript interface FieldPlacerProps { docId: string; pageInfo: PageInfo | null; currentPage: number; children: React.ReactNode; readOnly?: boolean; onFieldsChanged?: () => void; selectedFieldId?: string | null; textFillData?: Record; onFieldSelect?: (fieldId: string | null) => void; onFieldValueChange?: (fieldId: string, value: string) => void; aiPlacementKey?: number; } ``` From PreparePanel.tsx (current props): ```typescript interface PreparePanelProps { docId: string; defaultEmail: string; clientName: string; currentStatus: string; agentDownloadUrl?: string | null; signedAt?: Date | null; clientPropertyAddress?: string | null; previewToken: string | null; onPreviewTokenChange: (token: string | null) => void; textFillData: Record; selectedFieldId: string | null; onQuickFill: (fieldId: string, value: string) => void; onAiAutoPlace: () => Promise; } ``` From server page.tsx: ```typescript // doc fetched via db.query.documents.findFirst — has all document columns including signers const doc = await db.query.documents.findFirst({ where: eq(documents.id, docId) }); // doc.signers is DocumentSigner[] | null (JSONB column from Phase 14) ``` Task 1: Add signers prop to server page and thread state through DocumentPageClient - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/page.tsx - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx - teressa-copeland-homes/src/lib/db/schema.ts (for DocumentSigner interface) - .planning/phases/16-multi-signer-ui/16-CONTEXT.md - .planning/phases/16-multi-signer-ui/16-UI-SPEC.md teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/page.tsx, teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx **server page.tsx:** - `doc` is already fetched via `db.query.documents.findFirst` which returns all columns including `signers`. Pass `doc.signers` to DocumentPageClient as `initialSigners`: ``` initialSigners={doc.signers ?? []} ``` **DocumentPageClient.tsx:** - Import `DocumentSigner` from `@/lib/db/schema` - Add `initialSigners: DocumentSigner[]` to `DocumentPageClientProps` - Add state: `const [signers, setSigners] = useState(initialSigners);` - Add state: `const [unassignedFieldIds, setUnassignedFieldIds] = useState>(new Set());` - Pass to PreparePanel: `signers={signers}`, `onSignersChange={setSigners}`, `unassignedFieldIds={unassignedFieldIds}`, `onUnassignedFieldIdsChange={setUnassignedFieldIds}` - Pass to PdfViewerWrapper: `signers={signers}`, `unassignedFieldIds={unassignedFieldIds}` - No other changes to existing props or callbacks cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 | head -30 - `grep -n "initialSigners" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/page.tsx` shows prop being passed - `grep -n "DocumentSigner" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx` shows import - `grep -n "signers={signers}" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx` shows props threaded to both PreparePanel and PdfViewerWrapper - `grep -n "unassignedFieldIds" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx` shows state and prop threading - `npx tsc --noEmit` passes DocumentPageClient has signers + unassignedFieldIds state initialized from server; props threaded to PreparePanel and PdfViewerWrapper; TypeScript compiles cleanly Task 2: Thread signers and unassignedFieldIds through PdfViewerWrapper to FieldPlacer - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewer.tsx (to understand its prop interface — it wraps FieldPlacer) - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx (lines 155-167 for FieldPlacerProps) - teressa-copeland-homes/src/lib/db/schema.ts (for DocumentSigner type) - .planning/phases/16-multi-signer-ui/16-UI-SPEC.md teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx, teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewer.tsx, teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx **PdfViewerWrapper.tsx:** - Import `DocumentSigner` from `@/lib/db/schema` - Add optional props: `signers?: DocumentSigner[]`, `unassignedFieldIds?: Set` - Pass both through to `` **PdfViewer.tsx:** - Add same optional props to PdfViewer's prop interface: `signers?: DocumentSigner[]`, `unassignedFieldIds?: Set` - Pass both through to `` **FieldPlacer.tsx:** - Import `DocumentSigner` from `@/lib/db/schema` - Add to FieldPlacerProps: `signers?: DocumentSigner[]`, `unassignedFieldIds?: Set` - Destructure in component function signature (default `signers = []`, `unassignedFieldIds = new Set()`) - Do NOT implement rendering changes yet (that is Plan 03) — just accept the props All three files: add the two new optional props and pass them through. No rendering or behavioral changes. cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 | head -30 - `grep -n "signers" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx` shows prop accepted and passed through - `grep -n "signers" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx` shows signers in FieldPlacerProps - `grep -n "unassignedFieldIds" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx` shows unassignedFieldIds in FieldPlacerProps - `npx tsc --noEmit` passes signers and unassignedFieldIds props flow from DocumentPageClient through PdfViewerWrapper and PdfViewer into FieldPlacer; TypeScript compiles cleanly - `npx tsc --noEmit` passes with no errors - `grep -rn "signers" teressa-copeland-homes/src/app/portal/(protected)/documents/` confirms signers threaded through page.tsx -> DocumentPageClient -> PdfViewerWrapper -> PdfViewer -> FieldPlacer AND DocumentPageClient -> PreparePanel - `grep -rn "unassignedFieldIds" teressa-copeland-homes/src/app/portal/(protected)/documents/` confirms unassignedFieldIds threaded through the same paths - DocumentPageClient has `signers` and `unassignedFieldIds` state - Server page passes `initialSigners` from `doc.signers` - Both PreparePanel and FieldPlacer (via PdfViewerWrapper chain) receive signers and unassignedFieldIds as props - TypeScript compiles with no errors After completion, create `.planning/phases/16-multi-signer-ui/16-01-SUMMARY.md`