- SUMMARY: AddDocumentModal, PdfViewer, /portal/documents/[docId] page - STATE: current position updated to 04-03 complete, 3/4 plans in phase - ROADMAP: phase 4 progress updated (3 summaries of 4 plans) - REQUIREMENTS: DOC-03 marked complete
8.3 KiB
8.3 KiB
phase: 04-pdf-ingest
plan: 03
subsystem: ui
tags: [react-pdf, pdfjs-dist, next.js, modal, file-upload, pdf-viewer]
# Dependency graph
requires:
- phase: 04-pdf-ingest
provides: GET /api/forms-library, POST /api/documents, GET /api/documents/[id]/file API routes
- phase: 03-agent-portal-shell
provides: ClientProfileClient, DocumentsTable, portal route structure at (protected)/
provides:
- AddDocumentModal component: searchable forms library list with custom PDF file picker fallback
- PdfViewer component: react-pdf canvas renderer with page nav, zoom, and download controls
- /portal/documents/[docId] page: server component with auth, Drizzle relations query, PdfViewer
- Document names in DocumentsTable are now clickable links to /portal/documents/{id}
- documentsRelations and clientsRelations in schema.ts enabling db.query with-relations
affects: [04-04]
# Tech tracking
tech-stack:
added:
- react-pdf (v9+ with pdfjs-dist peer dep)
patterns:
- pdfjs worker setup via new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url) — no CDN
- transpilePackages: ['react-pdf', 'pdfjs-dist'] in next.config.ts for ESM compatibility
- db.query.documents.findFirst({ with: { client: true } }) using Drizzle relational API
- Modal state colocated in existing client sub-component (ClientProfileClient) — no server-to-client conversion needed
key-files:
created:
- teressa-copeland-homes/src/app/portal/_components/AddDocumentModal.tsx
- teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/page.tsx
- teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewer.tsx
modified:
- teressa-copeland-homes/src/app/portal/_components/ClientProfileClient.tsx
- teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx
- teressa-copeland-homes/src/lib/db/schema.ts
- teressa-copeland-homes/next.config.ts
- teressa-copeland-homes/package.json
key-decisions:
- "react-pdf v9 requires transpilePackages in next.config.ts — ships as ESM, Next.js webpack must transpile"
- "pdfjs worker uses import.meta.url pattern not CDN — works in local/Docker environments without internet access"
- "AddDocumentModal placed in shared _components — colocated with ClientProfileClient which renders it"
- "documentsRelations added to schema.ts — required for db.query relational API used in document detail page"
- "Document detail page placed in (protected)/ route group — inherits auth layout automatically"
patterns-established:
- "react-pdf usage: always import both AnnotationLayer.css and TextLayer.css; always configure worker via import.meta.url"
- "Modal pattern: conditional render {isOpen && } in existing client sub-component, no new file needed"
requirements-completed: [DOC-01, DOC-03]
# Metrics
duration: 5min
completed: 2026-03-20
Phase 4 Plan 03: PDF Viewer UI Summary
react-pdf AddDocumentModal with searchable forms library + file picker, and document detail page with PdfViewer (page nav, zoom, download) wired to the authenticated /api/documents/[id]/file stream
Performance
- Duration: ~5 min
- Started: 2026-03-20T03:41:23Z
- Completed: 2026-03-20T03:46:00Z
- Tasks: 2
- Files modified: 8
Accomplishments
- Installed react-pdf v9 with pdfjs-dist; configured next.config.ts transpilePackages for ESM compatibility
- Created AddDocumentModal: fetches forms library on open, searchable list, custom PDF file picker, POST to /api/documents
- Wired "Add Document" button into existing ClientProfileClient — modal state colocated, no server component conversion needed
- Updated DocumentsTable: document names are now links to /portal/documents/{id}
- Created PdfViewer: react-pdf Document/Page with Prev/Next, Zoom In/Out, and Download; worker via import.meta.url (no CDN)
- Created /portal/documents/[docId] page: auth check, Drizzle relational query (with: client), back link to client profile
- Added documentsRelations + clientsRelations to schema.ts for db.query API support
Task Commits
Each task was committed atomically:
- Task 1: Install react-pdf and configure Next.js for ESM compatibility -
63e5888(feat) - Task 2: Add Document modal + PDF viewer page -
c1f60ca(feat)
Plan metadata: (docs commit below)
Files Created/Modified
teressa-copeland-homes/next.config.ts- Added transpilePackages: ['react-pdf', 'pdfjs-dist']teressa-copeland-homes/package.json- react-pdf added as dependencyteressa-copeland-homes/src/app/portal/_components/AddDocumentModal.tsx- New: searchable forms library modal with file picker and POST to /api/documentsteressa-copeland-homes/src/app/portal/_components/ClientProfileClient.tsx- Added isAddDocOpen state, Add Document button in Documents header, AddDocumentModal renderteressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx- Document names now wrapped in Link to /portal/documents/{id}teressa-copeland-homes/src/lib/db/schema.ts- Added documentsRelations and clientsRelations for Drizzle relational APIteressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewer.tsx- New: react-pdf viewer with page nav, zoom, downloadteressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/page.tsx- New: document detail server page
Decisions Made
- react-pdf v9 ships as ESM —
transpilePackagesrequired in next.config.ts for Next.js webpack to process it - pdfjs worker configured via
new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url)— CDN URL would fail in offline/Docker environments - AddDocumentModal placed in shared
_components/folder alongside ClientProfileClient — avoids introducing per-route_componentssubdirectory for a shared component - documentsRelations needed for
db.query.documents.findFirst({ with: { client: true } })— Drizzle relational API requires explicit relation definitions in schema
Deviations from Plan
Auto-fixed Issues
1. [Rule 2 - Missing Critical] Added Drizzle relations to schema.ts
- Found during: Task 2 (document detail page)
- Issue: Plan's DocumentPage uses
db.query.documents.findFirst({ with: { client: true } })but schema.ts had no relations defined — the relational query would fail at runtime - Fix: Added
documentsRelations(one-to-one client) andclientsRelations(one-to-many documents) to schema.ts using Drizzlerelations()helper - Files modified:
teressa-copeland-homes/src/lib/db/schema.ts - Verification: Build compiles successfully with no type errors
- Committed in:
c1f60ca(Task 2 commit)
2. [Rule 2 - Missing Critical] DocumentsTable document names made clickable
- Found during: Task 2 (plan requirement: "document name in client profile links to /portal/documents/{id}")
- Issue: Plan's must_haves stated "Clicking a document name navigates to the document detail page" but existing DocumentsTable had no link on document names
- Fix: Imported Link from next/link, wrapped document name cell content in
<Link href="/portal/documents/{row.id}">with hover styles - Files modified:
teressa-copeland-homes/src/app/portal/_components/DocumentsTable.tsx - Verification: Build passes; link markup confirmed in component
- Committed in:
c1f60ca(Task 2 commit)
Total deviations: 2 auto-fixed (2 missing critical) Impact on plan: Both auto-fixes were explicitly required by plan's must_haves and action notes. No scope creep.
Issues Encountered
None. Build passed cleanly on first attempt after implementing all components.
User Setup Required
None - no external service configuration required.
Next Phase Readiness
- Agent can open the forms library modal from any client profile page
- Agent can add a document from the library or via file picker (both code paths wired)
- New document appears in client's Documents list after modal closes (router.refresh())
- Clicking any document name navigates to /portal/documents/{docId}
- Document detail page renders PDF via PdfViewer with full page navigation and zoom
- Phase 4 Plan 04 (document management: rename, delete, status updates) can build on this UI layer
Phase: 04-pdf-ingest Completed: 2026-03-20