--- phase: 16-multi-signer-ui plan: 03 type: execute wave: 2 depends_on: ["16-01"] files_modified: - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx autonomous: true requirements: [MSIGN-02, MSIGN-03] must_haves: truths: - "Active signer dropdown appears above palette when signers.length > 0" - "Dragged fields auto-get signerEmail of the active signer" - "Field boxes with signerEmail render in signer color instead of type color" - "Fields with no signerEmail render in existing type color (unchanged)" - "Fields in unassignedFieldIds set show red validation outline" - "Active signer defaults to first signer on load" artifacts: - path: "teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx" provides: "Active signer selector, per-signer field coloring, validation overlay" contains: "Active signer:" key_links: - from: "FieldPlacer handleDragEnd" to: "newField.signerEmail" via: "activeSigner.email assignment on field creation" pattern: "signerEmail.*activeSigner" - from: "FieldPlacer renderFields" to: "signer color lookup" via: "signers.find matching field.signerEmail for color" pattern: "signers.*find.*signerEmail.*color" --- Add the active signer selector dropdown and per-signer field coloring to FieldPlacer per D-05, D-06, D-07, D-08, D-09/D-10. Purpose: MSIGN-02 (tag fields to signers) and MSIGN-03 (color-coded fields by signer). When a signer is active, every field dropped gets that signer's email. Field boxes render in the signer's color when assigned. Output: FieldPlacer with active signer dropdown, signer-aware field creation, per-signer color rendering, and red validation overlay for unassigned fields. @$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 @.planning/phases/16-multi-signer-ui/16-01-SUMMARY.md From FieldPlacer (after Plan 01): ```typescript interface FieldPlacerProps { // ... existing props ... signers?: DocumentSigner[]; // from DocumentPageClient via PdfViewerWrapper unassignedFieldIds?: Set; // from DocumentPageClient for send-block validation } ``` From 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; } ``` FieldPlacer existing color system (lines 70-78): ```typescript const PALETTE_TOKENS: Array<{ id: SignatureFieldType; label: string; color: string }> = [ { id: 'client-signature', label: 'Signature', color: '#2563eb' }, { id: 'initials', label: 'Initials', color: '#7c3aed' }, { id: 'checkbox', label: 'Checkbox', color: '#059669' }, { id: 'date', label: 'Date', color: '#d97706' }, { id: 'text', label: 'Text', color: '#64748b' }, { id: 'agent-signature', label: 'Agent Signature', color: '#dc2626' }, { id: 'agent-initials', label: 'Agent Initials', color: '#ea580c' }, ]; ``` Color Override Rule (D-06, D-07): - field.signerEmail set AND matching signer in signers[] => use signer.color - field.signerEmail absent => use PALETTE_TOKENS type color (unchanged) Validation overlay (D-09/D-10, UI-SPEC section 3): - unassignedFieldIds contains field.id => border: 2px solid #ef4444, bg: #ef444414 Task 1: Add active signer selector and signer-aware field coloring - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx (FULL FILE — 822 lines) - teressa-copeland-homes/src/lib/db/schema.ts (DocumentSigner, SignatureFieldData) - .planning/phases/16-multi-signer-ui/16-CONTEXT.md (D-05, D-06, D-07, D-08, D-09, D-10) - .planning/phases/16-multi-signer-ui/16-UI-SPEC.md (Component Inventory sections 2 and 3) teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx **1. Active signer state (inside FieldPlacer function body):** ```typescript const [activeSignerEmail, setActiveSignerEmail] = useState(null); ``` Default to first signer on load and when signers change (per D-08): ```typescript useEffect(() => { if (signers && signers.length > 0) { setActiveSignerEmail(prev => { // Keep current selection if still valid if (prev && signers.some(s => s.email === prev)) return prev; return signers[0].email; }); } else { setActiveSignerEmail(null); } }, [signers]); ``` **2. Active signer selector UI** — insert ABOVE the palette div (line ~756), ONLY when `signers && signers.length > 0`: ```tsx {!readOnly && signers && signers.length > 0 && (
Active signer: {/* Color indicator dot next to the dropdown */} {(() => { const activeSigner = signers.find(s => s.email === activeSignerEmail); return activeSigner ? ( ) : null; })()}
)} ``` **3. handleDragEnd modification** — at line ~294 where `newField` is created, add `signerEmail`: Change the `newField` construction to include: ```typescript const newField: SignatureFieldData = { id: crypto.randomUUID(), page: currentPage, x: pdfX, y: pdfY, width: fieldW, height: fieldH, type: droppedType, ...(activeSignerEmail ? { signerEmail: activeSignerEmail } : {}), }; ``` Add `activeSignerEmail` to the useCallback dependency array of handleDragEnd. **4. renderFields color override** — in the renderFields function (around line 581-584), replace the existing color lookup: ```typescript // Per-type color and label const fieldType = getFieldType(field); const tokenMeta = PALETTE_TOKENS.find((t) => t.id === fieldType); // Color override: signer color when signerEmail is set (D-06), else type color (D-07) let fieldColor = tokenMeta?.color ?? '#2563eb'; if (field.signerEmail && signers) { const matchedSigner = signers.find(s => s.email === field.signerEmail); if (matchedSigner) { fieldColor = matchedSigner.color; } } // Validation overlay: unassigned field highlight (D-09/D-10) const isUnassigned = unassignedFieldIds?.has(field.id) ?? false; const fieldLabel = tokenMeta?.label ?? 'Signature'; ``` **5. Field box style override for validation** — in the field box's `style` object (around line 627-653): - Change `border` line to: `border: isUnassigned ? '2px solid #ef4444' : \`2px solid ${fieldColor}\``, - Change `background` line to: `background: isUnassigned ? '#ef444414' : (readOnly ? \`${fieldColor}0d\` : \`${fieldColor}1a\`)`, **6. DragOverlay ghost color** — in the DragOverlay section (around line 796-798), the ghost should also use signer color if active signer is set. Update: ```typescript {isDraggingToken ? (() => { const tokenMeta = PALETTE_TOKENS.find((t) => t.id === isDraggingToken); const label = tokenMeta?.label ?? 'Field'; // Ghost uses active signer color if one is selected, else type color let ghostColor = tokenMeta?.color ?? '#2563eb'; if (activeSignerEmail && signers) { const activeSigner = signers.find(s => s.email === activeSignerEmail); if (activeSigner) ghostColor = activeSigner.color; } const isCheckbox = isDraggingToken === 'checkbox'; // ... rest uses ghostColor instead of color ```
cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 | head -30 - `grep -n "Active signer:" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx` shows dropdown label - `grep -n "activeSignerEmail" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx` shows state variable - `grep -n "signerEmail.*activeSignerEmail" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx` shows field creation sets signerEmail - `grep -n "signers.*find.*signerEmail" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx` shows signer color lookup in renderFields - `grep -n "#ef4444" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx` shows validation overlay border color - `grep -n "#ef444414" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx` shows validation overlay background - `grep -n "height.*32px" teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx` shows dropdown is 32px tall - `npx tsc --noEmit` passes - Active signer dropdown appears above palette when signers exist, hidden otherwise per D-05 - Dropdown defaults to first signer per D-08 - Dragged fields get signerEmail = active signer's email per D-06 - Fields with signerEmail render in signer color; fields without use type color per D-06/D-07 - Fields in unassignedFieldIds set show red #ef4444 outline and #ef444414 background per D-09/D-10 - TypeScript compiles cleanly
- `npx tsc --noEmit` passes - Active signer selector renders above palette with correct styling - Field color follows signer color when assigned, type color when not - Validation overlay visible on unassigned fields - Agent can select active signer from dropdown before placing fields - Fields placed with active signer are tagged and colored accordingly - Unassigned fields show red validation highlight when send-block triggers - Existing behavior unchanged when no signers configured - TypeScript compiles with no errors After completion, create `.planning/phases/16-multi-signer-ui/16-03-SUMMARY.md`