- Created 07-03-SUMMARY.md: human verification checkpoint approved
- STATE.md: Phase 7 marked complete (3/3 plans), all 4 criteria verified
- ROADMAP.md: Phase 7 status updated to Complete (3/3 plans)
- SIGN-07 and LEGAL-03 confirmed working end-to-end in live browser
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 07-02-SUMMARY.md: documents PreparePanel Signed panel, agentDownloadUrl server-side generation, Date Signed column, and auto-fix of ClientProfileClient type mismatch
- STATE.md: advanced to Phase 7 plan 2 of 3, added 3 key decisions, logged metrics row
- ROADMAP.md: updated phase 7 progress (2/3 plans complete)
- document detail page: import createAgentDownloadToken, generate agentDownloadUrl server-side for
signed docs (signedFilePath present), pass agentDownloadUrl and signedAt props to PreparePanel
- DocumentsTable: add signedAt to DocumentRow type, add Date Signed column header and cell
- dashboard page: add signedAt to db select so allRows includes signed date for Signed documents
- ClientProfileClient: add signedAt to local DocumentRow type (fixes type mismatch with DocumentsTable)
- clients/[id]/page: add signedAt to query select to satisfy updated DocumentRow type
- Added agentDownloadUrl and signedAt to PreparePanelProps interface (optional, nullable)
- Destructure new props in function signature
- Added Signed status branch: green panel with signed timestamp and Download Signed PDF anchor
- Kept Sent/Viewed branch: gray read-only message
- Draft status: existing prepare form unchanged
- Download is a plain <a href> anchor — no fetch/onClick; browser follows link directly
- Streams signed PDF via short-lived agent-download JWT (adt query param)
- Returns 401 for missing/expired token, 403 for ID mismatch or path traversal
- Returns 404 for unsigned documents or missing files on disk
- Path traversal guard: absPath.startsWith(UPLOADS_DIR) before readFile
- Token/route ID cross-check: documentId !== id returns 403
- new Uint8Array(fileBuffer) for Next.js 16 TypeScript strict mode compatibility
- Appends two new exports to token.ts (existing exports untouched)
- purpose: 'agent-download', 5-min TTL, no DB record
- Mirrors existing createDownloadToken/verifyDownloadToken pattern
- SPF: MISSING — no TXT records at root domain
- DKIM: MISSING — NXDOMAIN for all common selectors
- DMARC: EXISTS but rua points to hosting default, not teressa@
- SMTP: credentials still placeholder in .env.local
- DNS provider: GoDaddy (ns63/ns64.domaincontrol.com)
- Add POST handler to sign/[token]/route.ts with atomic one-time enforcement
- UPDATE signing_tokens SET usedAt WHERE usedAt IS NULL RETURNING — 0 rows = 409
- Log signature_submitted and pdf_hash_computed audit events
- Merge client dataURLs with server-stored field coordinates (NEVER trust client coords)
- Call embedSignatureInPdf, store pdfHash + signedFilePath in documents table
- Update document status to Signed with signedAt timestamp
- Fire-and-forget sendAgentNotificationEmail (catches errors without failing response)
- Create /sign/[token]/confirmed success page for POST redirect destination
- Create SignatureModal.tsx with signature_pad Draw tab (devicePixelRatio scaling, touch-none)
- Type tab renders name in Dancing Script cursive via offscreen canvas
- Use Saved tab conditionally shown when localStorage has saved signature
- Save for later checkbox persists drawn/typed sig to localStorage on confirm
- Update SigningPageClient.tsx: import modal, track signedFields as Map<string,string>
- Field overlay shows signature preview image after signing
- handleSubmit POSTs to /api/sign/[token] and redirects to /sign/[token]/confirmed on 200
- 06-03-SUMMARY.md created with decisions, deviations, file list
- STATE.md advanced to Plan 3 complete; two new decisions recorded
- ROADMAP.md phase 6 updated to 3/6 summaries complete
- page.tsx: server component validates JWT + one-time-use before rendering any UI
- Three error states (expired/used/invalid) show static pages with no canvas
- SigningPageClientWrapper: dynamic import (ssr:false) for react-pdf browser requirement
- SigningPageClient: full-scroll PDF viewer with pulsing blue field overlays
- Field overlay coordinates convert PDF user-space (bottom-left) to screen (top-left)
- SigningProgressBar: sticky bottom bar with X/Y count + jump-to-next + submit button
- api/sign/[token]/pdf: token-authenticated PDF streaming route (no agent auth)
- Add send/route.ts: creates signing token, sends branded email, logs email_sent, updates status to Sent
- Auth guard returns 401 for unauthenticated requests; 422 if not prepared; 409 if already signed
- Wraps sendMail in try/catch — returns 502 without DB update if email delivery fails
- Add logAuditEvent(document_prepared) to prepare/route.ts after successful PDF preparation
- Validates JWT with verifySigningToken(); returns expired/invalid/used/pending
- Checks signingTokens.usedAt for one-time-use enforcement
- Logs link_opened + document_viewed audit events on valid pending access
- Extracts IP from x-forwarded-for/x-real-ip headers for audit trail
- Public route — no auth() import or session required
- created 06-01-SUMMARY.md with full task and decision documentation
- STATE.md: advanced to phase 6 plan 1, added 5 signing foundation decisions
- ROADMAP.md: marked 06-01-PLAN.md complete, Signing Flow at 1/6
- REQUIREMENTS.md: marked SIGN-02, LEGAL-01, LEGAL-02 complete
- Add readOnly prop to FieldPlacer; when true: hide palette, disable all pointer
events on field boxes, show fields at 60% opacity, suppress delete button and
all four resize handles
- PdfViewer accepts docStatus prop and derives readOnly={docStatus==='Sent'||'Signed'}
- PdfViewerWrapper forwards docStatus prop to PdfViewer
- page.tsx passes docStatus={doc.status} to PdfViewerWrapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ResizeCorner type ('se' | 'sw' | 'ne' | 'nw')
- handleResizeStart now accepts a corner argument, stored in DraggingState
- Screen-space math: compute start rect (top/left/right/bottom px), apply delta
per corner, enforce 40px/20px minimums, then convert back to PDF units on pointerup
- renderFields renders four 10x10 blue square handles at each corner with the
correct CSS resize cursor (nw-resize, ne-resize, sw-resize, se-resize)
- Opposite corner is always the anchor; only the two edges adjacent to the dragged
corner move
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Native pointer events on field body for move drag (no dnd-kit conflict)
- 10x10 resize handle in bottom-right corner with se-resize cursor
- Event delegation via DroppableZone onPointerMove/onPointerUp
- DOM mutation during drag for smooth performance; commit to state on pointerUp
- data-no-move attribute prevents resize handle and delete button from triggering move
- draggingRef tracks in-progress drag; activeDragFieldId drives grabbing cursor + box-shadow
- Minimum field size enforced: 40x20 PDF units