Files
2026-04-03 16:16:07 -06:00

12 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
16-multi-signer-ui 01 execute 1
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
true
MSIGN-01
MSIGN-02
MSIGN-03
truths artifacts key_links
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
path provides exports
teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx signers state, setSigners, unassignedFieldIds state, setUnassignedFieldIds — threaded to PreparePanel and PdfViewerWrapper
DocumentPageClient
path provides
teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/page.tsx Server-side documents.signers fetch, passed as initialSigners to DocumentPageClient
path provides
teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx Passes signers and unassignedFieldIds props through to PdfViewer/FieldPlacer
from to via pattern
page.tsx DocumentPageClient initialSigners prop from db.query.documents initialSigners=.*doc.signers
from to via pattern
DocumentPageClient PdfViewerWrapper signers and unassignedFieldIds props signers=.*unassignedFieldIds=
from to via pattern
DocumentPageClient PreparePanel signers, onSignersChange, unassignedFieldIds, onUnassignedFieldIdsChange props 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.

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

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

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):

interface DocumentPageClientProps {
  docId: string;
  docStatus: string;
  defaultEmail: string;
  clientName: string;
  agentDownloadUrl?: string | null;
  signedAt?: Date | null;
  clientPropertyAddress?: string | null;
}

From PdfViewerWrapper.tsx (current props):

{
  docId: string;
  docStatus?: string;
  onFieldsChanged?: () => void;
  selectedFieldId?: string | null;
  textFillData?: Record<string, string>;
  onFieldSelect?: (fieldId: string | null) => void;
  onFieldValueChange?: (fieldId: string, value: string) => void;
  aiPlacementKey?: number;
}

From FieldPlacer.tsx (current props — FieldPlacerProps interface at line 155):

interface FieldPlacerProps {
  docId: string;
  pageInfo: PageInfo | null;
  currentPage: number;
  children: React.ReactNode;
  readOnly?: boolean;
  onFieldsChanged?: () => void;
  selectedFieldId?: string | null;
  textFillData?: Record<string, string>;
  onFieldSelect?: (fieldId: string | null) => void;
  onFieldValueChange?: (fieldId: string, value: string) => void;
  aiPlacementKey?: number;
}

From PreparePanel.tsx (current props):

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<string, string>;
  selectedFieldId: string | null;
  onQuickFill: (fieldId: string, value: string) => void;
  onAiAutoPlace: () => Promise<void>;
}

From server page.tsx:

// 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<DocumentSigner[]>(initialSigners);`
- Add state: `const [unassignedFieldIds, setUnassignedFieldIds] = useState<Set<string>>(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<string>`
- Pass both through to `<FieldPlacer signers={signers} unassignedFieldIds={unassignedFieldIds} ... />`

**FieldPlacer.tsx:**
- Import `DocumentSigner` from `@/lib/db/schema`
- Add to FieldPlacerProps: `signers?: DocumentSigner[]`, `unassignedFieldIds?: Set<string>`
- 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

<success_criteria>

  • 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 </success_criteria>
After completion, create `.planning/phases/16-multi-signer-ui/16-01-SUMMARY.md`