Commit Graph

341 Commits

Author SHA1 Message Date
Chandler Copeland
cac5d5bbb6 fix(07-04): hide Download anchor in PdfViewer for Signed documents (LEGAL-03)
- Wrap Download anchor in conditional: only rendered when docStatus !== 'Signed'
- PDF viewer still loads original via /file for in-browser display regardless of status
- PreparePanel presigned URL remains sole download path for signed PDFs
2026-03-21 10:54:43 -06:00
Chandler Copeland
6775cc76eb fix(07-04): restrict /file route to original PDF only (LEGAL-03)
- Remove signedFilePath fallback from /file route
- Route now always serves doc.filePath (unsigned original)
- Signed PDF exclusively available via presigned /download?adt=[token]
2026-03-21 10:53:56 -06:00
Chandler Copeland
6239a30bfd docs(07-audit-trail-and-download): create gap closure plan 07-04 for LEGAL-03 2026-03-21 10:52:53 -06:00
Chandler Copeland
3ac1d1e1ea docs(07): add verification report — gaps found 2026-03-21 10:50:40 -06:00
Chandler Copeland
e942a28247 docs(07-03): complete Phase 7 browser verification — Phase 7 complete
- 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>
2026-03-21 10:46:26 -06:00
Chandler Copeland
86ca6dce85 docs(07-02): complete download UI wiring plan — SUMMARY, STATE, ROADMAP updated
- 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)
2026-03-21 10:40:46 -06:00
Chandler Copeland
68d94a779f feat(07-02): wire agentDownloadUrl to doc detail page, add signedAt to dashboard and client profile
- 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
2026-03-21 10:39:03 -06:00
Chandler Copeland
b823ae5c58 feat(07-02): extend PreparePanel with agentDownloadUrl/signedAt props and Signed download section
- 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
2026-03-21 10:37:37 -06:00
Chandler Copeland
36069cb1ef docs(07-01): complete agent download token and route plan
- 07-01-SUMMARY.md: execution summary with decisions and file references
- STATE.md: position updated to Phase 7 Plan 1 complete; two decisions logged
- ROADMAP.md: Phase 7 progress updated (1/3 plans complete)
- REQUIREMENTS.md: SIGN-07 and LEGAL-03 marked complete
2026-03-21 10:36:19 -06:00
Chandler Copeland
ebc47ae954 feat(07-01): create GET /api/documents/[id]/download agent download route
- 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
2026-03-21 10:34:43 -06:00
Chandler Copeland
cd4cb75b60 feat(07-01): add createAgentDownloadToken and verifyAgentDownloadToken
- Appends two new exports to token.ts (existing exports untouched)
- purpose: 'agent-download', 5-min TTL, no DB record
- Mirrors existing createDownloadToken/verifyDownloadToken pattern
2026-03-21 10:33:53 -06:00
Chandler Copeland
9fe7936304 docs(07-audit-trail-and-download): create phase 7 plan
3 plans in 3 sequential waves: agent download token + API route (01),
UI wiring for download button + signedAt column (02), human verification
checkpoint (03). Covers SIGN-07 and LEGAL-03.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 10:30:05 -06:00
Chandler Copeland
45f49ce498 docs(07): research phase audit-trail-and-download 2026-03-21 10:24:17 -06:00
Chandler Copeland
cf877d7443 wip: 06-signing-flow paused at post-execution bug fixes 2026-03-21 10:16:06 -06:00
Chandler Copeland
bf6d361973 fix(06): log audit events and set Viewed status in signing page server component 2026-03-21 10:15:16 -06:00
Chandler Copeland
1171b2fa86 fix(06): update status to Viewed on link open; serve signedFilePath in agent portal after signing 2026-03-21 10:01:46 -06:00
Chandler Copeland
5aef96786a fix(06): wire /send route after /prepare in PreparePanel — signing email was never being sent 2026-03-21 09:53:38 -06:00
Chandler Copeland
69614cabf9 docs(phase-06): complete phase execution 2026-03-21 09:49:14 -06:00
Chandler Copeland
04c3720096 fix(06): correct clientName in agent notification email 2026-03-21 09:49:06 -06:00
Chandler Copeland
04e3d5cb54 docs(06-06): complete DNS verification plan — LEGAL-04 satisfied, Phase 6 complete
- 06-06-SUMMARY.md: SPF/DKIM/DMARC verified green via Resend for tcopelandhomes.com
- STATE.md: Plan 06 complete, completed_plans 24/24, Phase 6 fully complete
- ROADMAP.md: Phase 6 marked complete (6/6 plans), completed 2026-03-21
- REQUIREMENTS.md: LEGAL-04 marked complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 09:44:23 -06:00
Chandler Copeland
7121279654 feat(06-06): update domain to tcopelandhomes.com, configure Resend SMTP 2026-03-21 09:42:10 -06:00
Chandler Copeland
32ea324504 chore(06-06): automated DNS verification check results
- 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)
2026-03-20 11:46:49 -06:00
Chandler Copeland
119edc2491 docs(06-05): complete confirmation page + download route plan — SUMMARY, STATE, ROADMAP updated
- Post-signing confirmation page with success checkmark, document name, timestamp, 15-min download token
- GET /api/sign/[token]/download streams signedFilePath PDF via short-lived download JWT
- Phase 6 (signing flow) all 5 plans complete
- SIGN-06 requirement marked complete
2026-03-20 11:44:32 -06:00
Chandler Copeland
4cdd9eea80 feat(06-05): confirmation page + router.push redirect after signing
- Rewrite confirmed/page.tsx: verifies signing token, shows document name + signed timestamp + download button
- Generate 15-min download token server-side; pass as dt= query param to /api/sign/[token]/download
- Success checkmark (navy circle + gold checkmark), document name, formatted signed date
- Download link valid for 15 minutes note shown below button
- Update SigningPageClient.tsx: replace window.location.href with router.push for SPA navigation
2026-03-20 11:42:24 -06:00
Chandler Copeland
a276da0da1 feat(06-05): download token utilities + download API route
- Add createDownloadToken and verifyDownloadToken to token.ts (15-min TTL, purpose:'download' claim)
- Create GET /api/sign/[token]/download route: validates dt query param JWT, streams signedFilePath as PDF
- Path traversal guard: signedFilePath must start with UPLOADS_DIR
- Auto-fix: Buffer cast to Uint8Array for Response BodyInit compatibility (Next.js 16 / TypeScript strict)
2026-03-20 11:41:18 -06:00
Chandler Copeland
5c1ea3568e docs(06-04): complete signature modal + submission plan — SUMMARY, STATE, ROADMAP updated
- SignatureModal with Draw/Type/Use Saved tabs (signature_pad DPR scaling)
- POST /api/sign/[token] atomic one-time claim, PDF embed, SHA-256 hash, audit trail
- SIGN-04, SIGN-05, LEGAL-01, LEGAL-02 requirements complete
2026-03-20 11:39:10 -06:00
Chandler Copeland
d445c282c1 feat(06-04): POST /api/sign/[token] atomic submission + confirmed page
- 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
2026-03-20 11:37:00 -06:00
Chandler Copeland
05b5207305 feat(06-04): SignatureModal with Draw/Type/Use Saved tabs + wire SigningPageClient
- 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
2026-03-20 11:35:40 -06:00
Chandler Copeland
a3026fb44f docs(06-03): complete signing page plan — SUMMARY, STATE, ROADMAP updated
- 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
2026-03-20 11:32:40 -06:00
Chandler Copeland
90bd066016 docs(06-02): complete email delivery layer plan
- Add 06-02-SUMMARY.md: SigningRequestEmail, signing-mailer, send route, prepare audit log
- Update STATE.md: Plan 2 complete, decisions logged, session updated
- Update ROADMAP.md: Phase 6 plan progress (2 of 5 summaries)
- Mark SIGN-01 complete in REQUIREMENTS.md
2026-03-20 11:31:35 -06:00
Chandler Copeland
dcf503dfea feat(06-03): signing page — server component, PDF viewer, field overlays, progress bar
- 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)
2026-03-20 11:30:38 -06:00
Chandler Copeland
877ad66ead feat(06-02): POST /api/documents/[id]/send + document_prepared audit log
- 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
2026-03-20 11:29:54 -06:00
Chandler Copeland
f41db49ff7 feat(06-02): branded signing request email + mailer utilities
- Add SigningRequestEmail.tsx React Email component (navy/gold brand colors, CTA button)
- Add signing-mailer.tsx with sendSigningRequestEmail() and sendAgentNotificationEmail()
- Uses CONTACT_SMTP_* env vars (same SMTP provider as contact form)
- Sender: "Teressa Copeland" <teressa@teressacopelandhomes.com>
2026-03-20 11:29:05 -06:00
Chandler Copeland
e1306dab69 feat(06-03): GET /api/sign/[token] route — token validation + audit logging
- 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
2026-03-20 11:28:51 -06:00
Chandler Copeland
4bca04f988 docs(06-01): complete signing foundation plan — SUMMARY, STATE, ROADMAP updated
- 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
2026-03-20 11:27:07 -06:00
Chandler Copeland
2929581ab9 feat(06-01): create signing utility library (token, audit, embed)
- token.ts: createSigningToken() + verifySigningToken() using jose HS256
- audit.ts: logAuditEvent() inserts typed audit events with server timestamp
- embed-signature.ts: embedSignatureInPdf() embeds PNG sigs, returns SHA-256 hash
- added SIGNING_JWT_SECRET to .env.local (random 32-char base64 secret)
2026-03-20 11:24:56 -06:00
Chandler Copeland
fa68a1bcb4 feat(06-01): install packages + extend schema + generate migration
- installed signature_pad, @react-email/render, @react-email/components
- added signingTokens table (jti pk, documentId, expiresAt, usedAt)
- added auditEvents table with auditEventTypeEnum (6 event types)
- added signedFilePath, pdfHash, signedAt columns to documents table
- generated and applied migration 0005_signing_flow.sql
2026-03-20 11:24:02 -06:00
Chandler Copeland
6cf228c779 docs(06-signing-flow): create phase plan 2026-03-20 11:18:47 -06:00
Chandler Copeland
d049f92c61 docs(06): research signing flow phase 2026-03-20 11:07:33 -06:00
Chandler Copeland
08bf795646 docs(06): capture phase context 2026-03-20 10:55:47 -06:00
Chandler Copeland
d24dd54062 fix(05-04): reject field updates when document status is not Draft 2026-03-20 10:44:21 -06:00
Chandler Copeland
c6f5800394 wip: phase 05-04 paused — readOnly lock not working after Sent transition 2026-03-20 10:42:35 -06:00
Chandler Copeland
bd73f0cc76 feat(05-04): lock field placer to read-only when document is Sent
- 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>
2026-03-20 10:36:25 -06:00
Chandler Copeland
08719a6109 feat(05-04): replace single resize handle with 4-corner resize handles
- 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>
2026-03-20 10:36:04 -06:00
Chandler Copeland
51a77ef7d2 feat(05-04): add move and resize to placed signature fields
- 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
2026-03-20 01:02:45 -06:00
Chandler Copeland
3d6f0ea68c fix(05-04): disable DragOverlay drop animation to eliminate snap-back 2026-03-20 01:00:02 -06:00
Chandler Copeland
cbad0a1a34 fix(05-04): remove transform from DraggableToken to prevent snap-back animation 2026-03-20 00:59:27 -06:00
Chandler Copeland
f8897cce34 fix(05-04): sync defaultEmail into state after hydration 2026-03-20 00:55:22 -06:00
Chandler Copeland
0a719c9d60 fix(05-04): fetch client email via direct join, improve recipients hint text 2026-03-20 00:53:46 -06:00
Chandler Copeland
1319d4310e fix(05-04): guard parseEmails against undefined defaultEmail 2026-03-20 00:51:32 -06:00