diff --git a/.planning/phases/05-pdf-fill-and-field-mapping/.continue-here.md b/.planning/phases/05-pdf-fill-and-field-mapping/.continue-here.md index 8bb4a81..0873923 100644 --- a/.planning/phases/05-pdf-fill-and-field-mapping/.continue-here.md +++ b/.planning/phases/05-pdf-fill-and-field-mapping/.continue-here.md @@ -1,100 +1,72 @@ --- phase: 05-pdf-fill-and-field-mapping -task: 0 -total_tasks: 0 -status: pre-planning -last_updated: 2026-03-20T04:56:27.959Z +plan: 04 +task: checkpoint-verification +status: in_progress +last_updated: 2026-03-20T16:41:16.656Z --- -Phase 5 (PDF Fill and Field Mapping) has NOT been planned yet. We were interrupted mid-session before planning could begin. - -A separate side-task is in progress: writing a Playwright scraper script to download the 148 SkySlope Forms PDFs into `seeds/forms/` so they can be seeded into the `form_templates` table. The scraper is partially working — it logs in via Utah Real Estate SSO, navigates to the Browse Libraries page, finds 100 form rows via "Add" buttons, but `interceptPdfOnClick` is not capturing PDF responses when form rows are clicked. +Phase 5 all plans executed. In the human verification loop for plan 05-04. Most bugs fixed. One remaining: after document transitions to "Sent", user can still delete signature field boxes. readOnly prop chain is verified correct in code — bug is at runtime. -## Phase 4 (PDF Ingest) — FULLY COMPLETE ✓ -- 04-01: form_templates table, documents schema extension, seed:forms script -- 04-02: /api/forms-library, /api/documents (POST), /api/documents/[id]/file (GET) with auth + path traversal guards -- 04-03: AddDocumentModal (searchable + file picker), PdfViewer (react-pdf, local worker, SSR fix via PdfViewerWrapper), document detail page -- 04-04: Human verification approved by Teressa +- 05-01: DB migration 0003, preparePdf utility, GET/PUT /fields + POST /prepare APIs, Y-flip tests +- 05-02: FieldPlacer.tsx drag-and-drop with dnd-kit +- 05-03: TextFillForm + PreparePanel + document page layout +- 05-04 bug fixes: + - Coordinate placement: uses active.rect.current.translated + canvas getBoundingClientRect + - Canvas offset state via useLayoutEffect + - dnd-kit delete button conflict fixed (MouseSensor distance:5, stopPropagation) + - Client selector replaced with single pre-filled email textarea (comma-separated) + - Text fill Strategy B stamps at 60pt from top, 10pt font + - DragOverlay snap-back: removed transform from DraggableToken, dropAnimation={null} + - 4-corner resize handles (nw/ne/sw/se), opposite corner anchored + - readOnly mode wired: page.tsx docStatus → PdfViewerWrapper → PdfViewer → FieldPlacer + - PreparePanel hydration: useEffect syncs defaultEmail into recipients state + - Client email: direct JOIN query (relation was returning undefined) + - parseEmails null guard -## Bug fixes applied during Phase 4 verification: -- Browse files button unstyled → replaced raw file input with visible bordered button showing selected filename -- Back button too small → styled as blue pill button -- DOMMatrix SSR crash → wrapped PdfViewer in dynamic() + ssr:false client component (PdfViewerWrapper.tsx) - -## SkySlope Forms scraper (scripts/scrape-skyslope-forms.ts): -- URE SSO login working (utahrealestate.com → /sso/connect/client/skyslope → forms.skyslope.com) -- 2FA detection + polling loop (user completes in browser, script waits) -- Session saved to scripts/.ure-session.json (gitignored) — 2FA skipped on re-runs -- Browse Libraries page navigation working (forms.skyslope.com/browse-libraries) -- 148 forms visible, 100 "Add" button rows found -- PROBLEM: interceptPdfOnClick returns null — clicking form rows does not trigger a PDF network response -## Immediate: Fix SkySlope scraper -- Figure out what clicking a form row actually does in SkySlope (opens preview modal? loads iframe? requires adding to a file first?) -- Take a screenshot of what the page looks like AFTER clicking a form name row to understand the preview UI -- Check if there's a preview/eye icon per row, or if the form name itself is a link -- The form rows are found via "Add" button ancestors — need to identify the clickable name element separately from the Add button -- May need to: click form name → wait for modal → find PDF URL in modal iframe/embed → fetch it +- FIX: readOnly not enforced at runtime — delete button still works after status=Sent + - Code is correct: {!readOnly && ( gates delete button, readOnly prop chain verified + - Likely cause: PdfViewer loaded via dynamic(ssr:false) may not re-render when router.refresh() fires + - Recommended fix: add server-side guard to PUT /api/documents/[id]/fields rejecting updates when doc.status='Sent' + - Also consider: FieldPlacer fetching its own status via useEffect on the fields API +- Get user "approved" on all 10 verification steps +- Run gsd-verifier for phase goal verification +- Mark phase 05 complete in ROADMAP.md -## After scraper works: -- Run `npm run seed:forms` to populate form_templates table -- Then proceed to Phase 5 planning - -## Phase 5 planning (next major task): -- Run `/gsd:plan-phase 5` (no CONTEXT.md exists — will be asked to continue without or discuss first) -- Phase 5 goal: drag-and-drop signature field placement, coordinate conversion (PDF user space), agent text fill, assign to client + initiate signing -- Requirements: DOC-04, DOC-05, DOC-06 -- SkySlope scraper uses URE SSO (not SkySlope direct login) — credentials: URE_USERNAME=Copte1, URE_PASSWORD=Jackson@nd8 in .env.local -- NRDS credentials also in .env.local: SKYSLOPE_LAST_NAME=Copeland, SKYSLOPE_NRDS_ID=837075029 -- Session persistence: scripts/.ure-session.json stores Playwright storageState to skip 2FA on reruns -- react-pdf installed with transpilePackages in next.config.ts; worker uses import.meta.url (no CDN) -- PdfViewerWrapper.tsx pattern: client component wrapper with dynamic()+ssr:false to avoid SSR DOMMatrix crash +- active.rect.current.translated for drop position (field appears where ghost was, not at cursor) +- Pointer events for move/resize of placed fields (not dnd-kit, avoids conflicts) +- Single recipients textarea pre-filled with doc client email +- Text fill always stamps via drawText (no AcroForm fields in client PDFs yet) +- readOnly: palette hidden, delete+resize handles hidden, pointerEvents:none on fields + -- SkySlope scraper: clicking form rows doesn't trigger a PDF network response. Need to investigate what the form row click actually triggers in the SkySlope UI before interceptPdfOnClick can work. +- readOnly not propagating at runtime after Sent transition. Server-side guard on PUT /fields is the reliable backstop fix. + -The scraper strategy: URE SSO → browse-libraries page shows all 148 forms in a list. Each row has the form name and an "Add" button. Current approach tries to intercept PDF responses when clicking rows. The interception likely fails because SkySlope loads the PDF preview in an iframe/embed inside a modal rather than as a direct network response to the click. - -Next approach to try: -1. Click the form name (not the Add button) to open preview -2. Wait for a modal/dialog to appear -3. Look for an iframe src or embed src pointing to a PDF URL -4. OR look for a download button inside the modal -5. Extract the PDF URL and fetch it with cookies - -Key file: teressa-copeland-homes/scripts/scrape-skyslope-forms.ts +All Phase 5 features are functionally working. The UX iteration has been extensive — coordinate math, dnd-kit conflicts, client email display, text stamping. The one remaining issue is a React hydration/re-render timing problem with the readOnly lock after a status transition in the same session. Fresh page load with status=Sent probably works; same-session transition may not re-render PdfViewer correctly because of dynamic import. -Start with: Open `scripts/scrape-skyslope-forms.ts`, add a debug step that clicks the FIRST form name row, waits 3 seconds, takes a screenshot, and prints all iframe/embed src attributes and any new PDF-related network responses. This will reveal what the form preview UI looks like and how to extract the PDF URL. - -Command to run after fixing: -```bash -cd /Users/ccopeland/temp/red/teressa-copeland-homes && npm run scrape:forms -``` - -Then after forms are downloaded: -```bash -npm run seed:forms -``` - -Then plan Phase 5: -``` -/gsd:plan-phase 5 -``` +1. Read teressa-copeland-homes/src/app/api/documents/[id]/fields/route.ts +2. Add status check: if doc.status !== 'Draft', return 403 for PUT requests +3. This server-side guard prevents field deletion regardless of UI state +4. Test: prepare a document, try to delete fields — should fail silently or show error +5. After confirmed fixed, get user approval on all 10 steps and proceed to verification