Files
red/.planning/phases/12.1-per-field-text-editing-and-quick-fill/12.1-01-SUMMARY.md
Chandler Copeland 46c7aaa7d5 docs(12.1-01): complete per-field text fill and click-to-select plan
- SUMMARY.md: document field-ID-keyed text fill and click-to-select interaction
- STATE.md: advance to Phase 12.1 Plan 1 complete; add 3 key decisions
- ROADMAP.md: update phase 12.1 progress (1/2 plans complete)
- REQUIREMENTS.md: mark TXTF-01 and TXTF-03 complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 16:23:42 -06:00

5.7 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
12.1-per-field-text-editing-and-quick-fill 01 pdf-generation, document-editor
text-fill
field-selection
pdf
ux
requires provides affects
field-ID-keyed text drawing in preparePdf
per-field click-to-select + inline input in FieldPlacer
optional prop chain PdfViewerWrapper -> PdfViewer -> FieldPlacer
teressa-copeland-homes/src/lib/pdf/prepare-document.ts
teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx
teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewer.tsx
teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx
added patterns
field-ID-keyed lookup (UUID direct lookup replaces positional sort)
click-to-select inline input overlay (data-no-move + stopPropagation)
created modified
teressa-copeland-homes/src/lib/pdf/prepare-document.ts
teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx
teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewer.tsx
teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx
[Phase 12.1-01]: preparePdf text fill is now keyed by SignatureFieldData.id (UUID) — direct lookup replaces positional sort; AcroForm Strategy A and Strategy B top-of-page fallback both removed
[Phase 12.1-01]: FieldPlacer click-to-select uses onClick (fires only on no-drag clicks due to MouseSensor distance:5 threshold) — onPointerDown move handler is preserved and unaffected
[Phase 12.1-01]: data-no-move attribute on the inline input's onPointerDown stops move handler activation — consistent with existing delete button and resize handle pattern
duration_minutes completed_date tasks_completed files_modified
3 2026-03-21 2 4

Phase 12.1 Plan 01: Per-Field Text Fill and Click-to-Select Summary

One-liner: Field-ID-keyed UUID text drawing in preparePdf with Strategy A/B removed; click-to-select inline input overlay on text fields in FieldPlacer; prop chain PdfViewerWrapper -> PdfViewer -> FieldPlacer wired and ready for Plan 02.

Tasks Completed

# Task Commit Files
1 Replace positional text fill with field-ID-keyed lookup in preparePdf df02a1e prepare-document.ts
2 Add optional text-edit props to PdfViewerWrapper, PdfViewer, FieldPlacer click-to-select eaf377d PdfViewerWrapper.tsx, PdfViewer.tsx, FieldPlacer.tsx

What Was Built

Task 1: preparePdf field-ID-keyed text fill

Replaced the broken positional text assignment pipeline in prepare-document.ts:

  • Removed AcroForm Strategy A: pdfDoc.getForm(), form.getTextField(), form.flatten(), acroFilledKeys, hasAcroForm
  • Removed positional sort loop: remainingEntries, textFields_sorted, fieldConsumedKeys, textFields_sorted.forEach()
  • Removed Strategy B top-of-page stamp: unstampedEntries, firstPage.drawText(${key}: ${value}, ...)
  • Added Phase 12.1 field-ID loop: textFields[field.id] direct UUID lookup, draws at field.x+4, field.y+4 with font 6-11pt clamped to field height

The textFields parameter type (Record<string, string>) is unchanged — backward-compatible. Calling routes pass body.textFillData which will now be { [fieldId]: value } once Plan 02 wires the UI.

Task 2: Optional prop chain and click-to-select

PdfViewerWrapper.tsx and PdfViewer.tsx: Added 4 optional props to each and forwarded them down the chain:

  • selectedFieldId?: string | null
  • textFillData?: Record<string, string>
  • onFieldSelect?: (fieldId: string | null) => void
  • onFieldValueChange?: (fieldId: string, value: string) => void

PdfViewer also calls onFieldSelect?.(null) on Prev/Next page button clicks to deselect on page change.

FieldPlacer.tsx:

  • Extended FieldPlacerProps with the 4 new optional props
  • DroppableZone now accepts onClick?: (e: React.MouseEvent<HTMLDivElement>) => void — background clicks deselect via onFieldSelect?.(null) when target is not inside [data-field-id]
  • Per-field div gets onClick handler: text fields call onFieldSelect?.(field.id) with e.stopPropagation() to prevent DroppableZone deselect; non-text fields call onFieldSelect?.(null)
  • renderFields() computes isSelected = selectedFieldId === field.id and currentValue = textFillData?.[field.id] ?? ''
  • Text field content: isSelected shows <input data-no-move autoFocus> with transparent styling; not selected shows value or label in truncating <span>
  • Cursor set to 'text' for text fields (vs 'grab' for others)
  • BoxShadow ring 0 0 0 2px {fieldColor}66 when text field is selected

Deviations from Plan

None — plan executed exactly as written.

Verification

  • npx tsc --noEmit passes with zero errors
  • prepare-document.ts contains textFields[field.id] and does NOT contain remainingEntries, unstampedEntries, fieldConsumedKeys, getForm(), or form.flatten()
  • FieldPlacer.tsx exports FieldPlacer with all 4 new optional props in FieldPlacerProps
  • PdfViewerWrapper.tsx and PdfViewer.tsx accept and forward the 4 new optional props
  • Plan 02 can wire selectedFieldId, textFillData, onFieldSelect, onFieldValueChange state from DocumentPageClient without any additional changes to these files

Self-Check: PASSED

  • teressa-copeland-homes/src/lib/pdf/prepare-document.ts exists and modified
  • FieldPlacer.tsx, PdfViewer.tsx, PdfViewerWrapper.tsx exist and modified
  • Commit df02a1e exists (Task 1)
  • Commit eaf377d exists (Task 2)