2026-03-20 00:09:26 -06:00
---
phase: 05-pdf-fill-and-field-mapping
plan: "04"
2026-03-20 00:24:50 -06:00
subsystem: documents/pdf-fill
tags: [human-verify, checkpoint, bug-fix, dnd-kit, pdf-lib, coordinate-math, form-prepare]
2026-03-20 00:09:26 -06:00
# Dependency graph
requires:
- phase: 05-02-pdf-fill-and-field-mapping
provides: Drag-and-drop FieldPlacer component on PDF pages with DB persistence
- phase: 05-03-pdf-fill-and-field-mapping
provides: TextFillForm and PreparePanel components wired to /api/documents/[id]/prepare
- phase: 05-01-pdf-fill-and-field-mapping
provides: POST /api/documents/[id]/prepare endpoint that writes prepared PDF and transitions status to Sent
provides:
- Human sign-off on Phase 5 end-to-end workflow (DOC-04, DOC-05, DOC-06)
affects:
- 06-signing-flow
# Tech tracking
tech-stack:
2026-03-20 00:24:50 -06:00
added:
- email_addresses jsonb column in documents table (migration 0004)
2026-03-20 00:09:26 -06:00
patterns:
2026-03-20 00:24:50 -06:00
- "pageInfo.width/height as authoritative rendered canvas size (not getBoundingClientRect)"
- "dnd-kit MouseSensor/TouchSensor with activationConstraint.distance to guard click targets"
- "onPointerDown e.stopPropagation() on buttons inside DndContext to prevent drag capture"
- "useLayoutEffect for tracking container dimensions in state"
- "preparePdf Strategy A (AcroForm) + Strategy B (drawText fallback) for text fill"
- "email_addresses jsonb column in documents table for Phase 6 signing"
2026-03-20 00:09:26 -06:00
key-files:
2026-03-20 00:24:50 -06:00
created:
- teressa-copeland-homes/drizzle/0004_military_maximus.sql
modified:
- teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx
- teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreparePanel.tsx
- teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/page.tsx
- teressa-copeland-homes/src/app/api/documents/[id]/prepare/route.ts
- teressa-copeland-homes/src/lib/pdf/prepare-document.ts
- teressa-copeland-homes/src/lib/db/schema.ts
2026-03-20 00:09:26 -06:00
key-decisions:
2026-03-20 00:24:50 -06:00
- "Use pageInfo.width/height (from react-pdf page load callback) not containerRect.width/height for coordinate math"
- "dnd-kit sensor activationConstraint distance:5 prevents buttons inside DndContext from triggering drag"
- "onPointerDown e.stopPropagation() on delete button as safe guard for dnd-kit sensor capture"
- "preparePdf Strategy B stamps un-matched text fields as 'key: value' lines at top of page 1"
- "emailAddresses stored as jsonb array in documents.email_addresses ready for Phase 6; no email sent in Phase 5"
- "assignedClientId locked (read-only) when already set; defaults to document owner but changeable when null"
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
patterns-established:
- "pageInfo.width/height pattern for PDF canvas coordinate math in FieldPlacer"
- "preparePdf dual-strategy text fill: AcroForm + drawText fallback"
2026-03-20 00:09:26 -06:00
requirements-completed: [DOC-04, DOC-05, DOC-06]
# Metrics
2026-03-20 00:24:50 -06:00
duration: 30min
2026-03-20 00:09:26 -06:00
completed: "2026-03-20"
---
2026-03-20 00:24:50 -06:00
# Phase 5 Plan 04: Human Verification Checkpoint + Bug Fixes Summary
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Four bugs found during human testing were fixed: signature field misplacement, unclickable delete button, client selector pre-selection with manual email entry, and text fill data silently dropped from output PDF.**
2026-03-20 00:09:26 -06:00
## Performance
2026-03-20 00:24:50 -06:00
- **Duration:** ~30 min (bug fixes after checkpoint)
- **Completed:** 2026-03-20
- **Bugs fixed:** 4
- **Commits:** 3 (Bugs 1+2 in same file, Bugs 3 and 4 separate)
- **Files modified:** 6 + 1 migration created
2026-03-20 00:09:26 -06:00
## Accomplishments
2026-03-20 00:24:50 -06:00
### Bug 1 — Signature field placement lands in wrong position (FIXED)
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Root cause:** `renderFields()` called `containerRef.current?.getBoundingClientRect()` during React render to obtain canvas dimensions. `containerRect.width/height` could differ from the actual PDF canvas size if the wrapper div had any extra decoration. Also, calling `getBoundingClientRect()` during render is unreliable.
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Fix:** Used `pageInfo.width/height` (set from `react-pdf` 's `page.onLoadSuccess` ) as the authoritative rendered canvas dimensions. Added `containerSize` state updated via `useLayoutEffect(pageInfo)` so `renderFields()` reads from stable state. `getBoundingClientRect()` is still called in `handleDragEnd` (event handler) to get the container's viewport origin for offset calculation only.
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Commit:** `126e10d`
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
### Bug 2 — Cannot click the x button to delete a signature field (FIXED)
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Root cause:** dnd-kit's default sensor captured all `pointerdown` events inside `DndContext` , including on the delete button.
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Fix (two-part):**
1. Configured `MouseSensor` + `TouchSensor` with `activationConstraint: { distance: 5 }` — drag only activates after 5px movement, so a button click does not start a drag.
2. Added `onPointerDown={(e) => e.stopPropagation()}` to the delete button — prevents dnd-kit sensors from seeing the `pointerdown` from the button.
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
Field overlay divs now have `zIndex: 10` and the button `zIndex: 11` .
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Commit:** `126e10d` (same file as Bug 1)
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
### Bug 3 — Client selector should default to document's client; allow manual email entry (FIXED)
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Root cause:** `PreparePanel` received a single `currentClientId` prop (resolved `assignedClientId ?? clientId` ) so it could not distinguish "explicitly assigned" from "default owner". No email input existed.
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Fix:**
- `page.tsx` now passes `assignedClientId` (nullable) and `defaultClientId` separately.
- `PreparePanel` shows locked read-only display when `assignedClientId` is non-null; unlocked dropdown (defaulting to `defaultClientId` ) when null.
- Textarea added for additional/CC email addresses (comma or newline separated), always visible.
- `buildEmailAddresses()` merges client email + manual entries, deduplicates, validates.
- `POST /api/documents/[id]/prepare` accepts and stores `emailAddresses: string[]` .
- Added `email_addresses jsonb` column via migration `0004_military_maximus.sql` (applied).
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Commit:** `05915aa`
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
### Bug 4 — Text fill fields do nothing in the prepared PDF (FIXED)
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Root cause:** `preparePdf()` silently caught all AcroForm errors with no fallback. Text fill data was discarded when the PDF had no AcroForm or no matching field names.
2026-03-20 00:09:26 -06:00
2026-03-20 00:24:50 -06:00
**Fix:** Added Strategy B: after Strategy A (AcroForm), any un-filled entries are drawn as `key: value` text lines near the top of page 1 using `@cantoo/pdf-lib` 's `drawText()` . `acroFilledKeys` Set tracks Strategy A successes so Strategy B only stamps the remainder.
**Commit:** `ef10dd5`
## Task Commits
| Commit | Description | Bugs |
|---------|----------------------------------------------------------|------|
| 126e10d | fix(05-04): fix signature field placement coordinate math | 1, 2 |
| 05915aa | fix(05-04): pre-select document client and add manual email entry | 3 |
| ef10dd5 | fix(05-04): always stamp text fill data into prepared PDF | 4 |
## Files Created/Modified
| File | Change |
|------|--------|
| `FieldPlacer.tsx` | Coordinate math, sensor config, delete button guard |
| `PreparePanel.tsx` | Locked client, manual email textarea, emailAddresses |
| `page.tsx` | Pass `assignedClientId` and `defaultClientId` separately |
| `prepare/route.ts` | Accept and store `emailAddresses` array |
| `prepare-document.ts` | Strategy B drawText fallback for text fill |
| `schema.ts` | Added `emailAddresses` jsonb field |
| `0004_military_maximus.sql` | New migration: email_addresses column |
## Deviations from Plan
None — these were continuation bug fixes from human testing, not plan deviations.
2026-03-20 00:09:26 -06:00
## Self-Check: PASSED
2026-03-20 00:24:50 -06:00
- All 6 source files exist and TypeScript compiles clean (tsc --noEmit)
- Migration 0004 generated and applied to local DB
- Commits 126e10d, 05915aa, ef10dd5 confirmed in git log