- Change startY from pageHeight-20 to pageHeight-60 (~0.83 inch from top)
- Increase lineHeight from 12 to 14 for better readability
- Increase stamp font size from 8pt to 10pt for better visibility
Strategy A still attempts AcroForm filling by field name (matching
fields in the PDF's form dict). Strategy B is now a mandatory fallback:
any text field entries that did not match an AcroForm field (or when
the PDF has no AcroForm at all) are drawn as 'key: value' text lines
near the top of page 1 using @cantoo/pdf-lib drawText.
This ensures text fill data supplied in the PreparePanel is always
visible in the output PDF regardless of whether the source PDF was
built with interactive form fields.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PreparePanel now receives separate assignedClientId (nullable) and
defaultClientId props so it can distinguish an explicitly locked
client from just a default
- When document already has assignedClientId: show locked read-only
display; user cannot change the primary recipient
- When document has no assignedClientId: default to document owner in
dropdown but allow changing; option to clear and enter email manually
- Added textarea for additional/CC email addresses (comma or newline
separated) that is always visible for either mode
- POST /api/documents/[id]/prepare now accepts and stores emailAddresses
array alongside assignedClientId
- Added email_addresses jsonb column to documents table via migration
0004_military_maximus.sql
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Created src/lib/pdf/__tests__/prepare-document.test.ts with 10 test cases
- Tests verify Y-axis flip: screenY=0 → pdfY≈792, screenY=792 → pdfY≈0
- Tests verify scale-invariance at both 1:1 and 50% zoom ratios
- Installed jest, ts-jest, @types/jest and added jest config to package.json
- All 10 tests pass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Installed @cantoo/pdf-lib for server-side PDF mutation
- Created src/lib/pdf/prepare-document.ts with preparePdf function using atomic tmp->rename write pattern
- form.flatten() called before drawing signature rectangles
- Created GET/PUT /api/documents/[id]/fields routes for signature field storage
- Created POST /api/documents/[id]/prepare route that calls preparePdf and transitions status to Sent
- Fixed pre-existing null check error in scripts/debug-inspect2.ts (Rule 3: blocking build)
- Build compiles successfully with 2 new API routes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create AddDocumentModal: searchable forms library list + custom file picker
- Wire Add Document button into ClientProfileClient in Documents section header
- Update DocumentsTable: document names now link to /portal/documents/{id}
- Create PdfViewer with page nav, zoom, and download controls (pdfjs worker via import.meta.url)
- Create /portal/documents/[docId] page: server component with auth check, doc/client query
- Add documentsRelations and clientsRelations to schema.ts for db.query with-relations support
- Build verified: /portal/documents/[docId] route present, no errors
- Add nodemailer@^7.0.7 and @types/nodemailer (v7 required by next-auth peer dep)
- Create src/lib/contact-mailer.ts: Nodemailer SMTP transporter + sendContactEmail()
- Create src/lib/contact-action.ts: Server Action with Zod validation, honeypot check
- SMTP credentials read from CONTACT_EMAIL_USER/PASS/SMTP_HOST/PORT env vars
- Add CONTACT_* placeholder vars to .env.local (gitignored, for local setup docs)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs:
1. auth.ts imported postgres (Node.js TCP) which crashes in Edge Runtime,
causing Auth.js to silently fall back to redirecting all requests to login.
Fix: split into auth.config.ts (Edge-safe, no DB) used by middleware,
and auth.ts (full, with DB) used by server components.
2. /agent/layout.tsx applied to /agent/login, so unauthenticated login page
visits redirected to themselves in an infinite loop.
Fix: moved dashboard + layout into (protected) route group so login page
has no auth layout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neon serverless driver requires remote WebSocket — incompatible with local
PostgreSQL. Replaced with postgres.js (drizzle-orm/postgres-js) in db/index.ts,
scripts/seed.ts, and drizzle.config.ts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Created src/lib/auth.ts with NextAuth JWT strategy, 7-day rolling session, Credentials provider
- Created src/app/api/auth/[...nextauth]/route.ts with GET/POST handlers and force-dynamic
- Created middleware.ts at project root (not src/) protecting /agent/* routes
- Fixed db/index.ts: lazy Proxy singleton prevents neon() crash during Next.js build
- npm run build passes; /api/auth/[...nextauth] renders as Dynamic route
- Created src/lib/db/schema.ts with users table (id, email, password_hash, created_at)
- Created src/lib/db/index.ts exporting db singleton via Drizzle + Neon HTTP driver
- Created drizzle.config.ts pointing to schema.ts with postgresql dialect
- Created scripts/seed.ts reading AGENT_EMAIL/AGENT_PASSWORD to create hashed user row
- Generated drizzle/0000_milky_black_cat.sql migration (committed per user decision)
- db:migrate and db:seed pending user Neon database setup