--- phase: 16-multi-signer-ui plan: 01 subsystem: ui tags: [react, typescript, next.js, multi-signer, state-management] # Dependency graph requires: - phase: 14-multi-signer-schema provides: DocumentSigner interface and documents.signers JSONB column - phase: 15-multi-signer-backend provides: Backend APIs for multi-signer sending and token dispatch provides: - signers state (DocumentSigner[]) in DocumentPageClient, seeded from server-side doc.signers - unassignedFieldIds state (Set) in DocumentPageClient for send-block validation - Props threaded: DocumentPageClient -> PreparePanel (signers, onSignersChange, unassignedFieldIds, onUnassignedFieldIdsChange) - Props threaded: DocumentPageClient -> PdfViewerWrapper -> PdfViewer -> FieldPlacer (signers, unassignedFieldIds) affects: [16-02-prepare-panel-signer-list, 16-03-field-placer-signer-assignment, 16-04-send-block-validation] # Tech tracking tech-stack: added: [] patterns: - "State lifted to DocumentPageClient as single source of truth for signers and unassignedFieldIds" - "Optional prop threading pattern — new props added as optional to all components in chain; defaults applied at FieldPlacer leaf" key-files: created: [] 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/PreparePanel.tsx - 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 key-decisions: - "PreparePanel interface extended with optional signers props in this plan so TypeScript compiles cleanly — Wave 2 plans will consume these props" - "Prop threading uses optional types throughout the chain with sensible defaults ([] and new Set()) at FieldPlacer leaf" patterns-established: - "Multi-signer state lives in DocumentPageClient — single owner, threads down to both PreparePanel and PdfViewer chain" - "unassignedFieldIds uses Set to enable O(1) field lookup in FieldPlacer render loop" requirements-completed: [MSIGN-01, MSIGN-02, MSIGN-03] # Metrics duration: 5min completed: 2026-04-03 --- # Phase 16 Plan 01: Multi-Signer UI State Wiring Summary **DocumentSigner[] and unassignedFieldIds Set state added to DocumentPageClient and threaded down to PreparePanel and FieldPlacer (via PdfViewerWrapper/PdfViewer) for Wave 2 consumption** ## Performance - **Duration:** 5 min - **Started:** 2026-04-03T22:16:00Z - **Completed:** 2026-04-03T22:21:25Z - **Tasks:** 2 - **Files modified:** 6 ## Accomplishments - Server page now passes `doc.signers ?? []` as `initialSigners` to DocumentPageClient - DocumentPageClient owns `signers: DocumentSigner[]` and `unassignedFieldIds: Set` state - Both states threaded to PreparePanel and the full PdfViewerWrapper -> PdfViewer -> FieldPlacer chain - TypeScript compiles clean with zero errors ## Task Commits Each task was committed atomically: 1. **Task 1: Thread signers state through DocumentPageClient** - `ac1f1d6` (feat) 2. **Task 2: Thread signers and unassignedFieldIds through PdfViewerWrapper to FieldPlacer** - `9da2cc6` (feat) ## Files Created/Modified - `src/app/portal/(protected)/documents/[docId]/page.tsx` - Added `initialSigners={doc.signers ?? []}` prop - `src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx` - Added signers/unassignedFieldIds state, DocumentSigner import, prop threading - `src/app/portal/(protected)/documents/[docId]/_components/PreparePanel.tsx` - Added optional signers/onSignersChange/unassignedFieldIds/onUnassignedFieldIdsChange to interface - `src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx` - Added signers/unassignedFieldIds props, pass-through to PdfViewer - `src/app/portal/(protected)/documents/[docId]/_components/PdfViewer.tsx` - Added signers/unassignedFieldIds props, pass-through to FieldPlacer - `src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx` - Added DocumentSigner import, signers/unassignedFieldIds to FieldPlacerProps with defaults ## Decisions Made - PreparePanel's interface was extended with the new optional props in this plan (not Wave 2) to keep TypeScript clean. The props are accepted but not yet consumed in PreparePanel's render output — that is Wave 2 work. - Prop threading uses optional types (`signers?: DocumentSigner[]`) across all intermediate components, with defaults applied only at the leaf (FieldPlacer: `signers = [], unassignedFieldIds = new Set()`). ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 2 - Missing Critical] Extended PreparePanel interface to accept new multi-signer props** - **Found during:** Task 1 (thread signers state through DocumentPageClient) - **Issue:** DocumentPageClient was passing `signers`, `onSignersChange`, `unassignedFieldIds`, and `onUnassignedFieldIdsChange` to PreparePanel but PreparePanel's interface didn't declare these props — TypeScript would error - **Fix:** Added four optional props to `PreparePanelProps` interface with a comment noting they are consumed in Wave 2 - **Files modified:** `PreparePanel.tsx` - **Verification:** `npx tsc --noEmit` passes with zero errors - **Committed in:** ac1f1d6 (Task 1 commit) --- **Total deviations:** 1 auto-fixed (1 missing critical — interface extension needed for compile) **Impact on plan:** Necessary for correctness. PreparePanel was a direct recipient of the new props and had to be aware of them. No scope creep. ## Issues Encountered None — TypeScript resolved cleanly after the single interface extension. ## Known Stubs None — this plan wires state and props only. No data is rendered yet. Wave 2 plans will build the UI that consumes these props. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - Wave 2 plans (16-02 signer list UI, 16-03 FieldPlacer active signer, 16-04 send-block validation) can now receive `signers` and `unassignedFieldIds` as props — the full prop chain is established - No blockers --- *Phase: 16-multi-signer-ui* *Completed: 2026-04-03*