+ );
+ }
+ ```
+
+ **B. Wire "Add Document" button into the existing client profile page**
+
+ Read `src/app/portal/clients/[id]/page.tsx` first to understand the current structure (Phase 3 built this). Then:
+ - Add a state-controlled "Add Document" button near the top of the documents section (or the page header)
+ - The button triggers a `showModal` state that renders ` setShowModal(false)} />`
+ - Since the profile page is likely a server component with a `ClientProfileClient` sub-component (per Phase 3 pattern), add the modal trigger and state inside the existing client component — do NOT convert the server page to a client component. Add the button and modal inside `_components/ClientProfileClient.tsx` or the equivalent client sub-component.
+
+ **C. Create PdfViewer.tsx**
+
+ `src/app/portal/documents/[docId]/_components/PdfViewer.tsx`:
+ ```typescript
+ 'use client';
+ import { useState } from 'react';
+ import { Document, Page, pdfjs } from 'react-pdf';
+ import 'react-pdf/dist/Page/AnnotationLayer.css';
+ import 'react-pdf/dist/Page/TextLayer.css';
+
+ // Worker setup — must use import.meta.url for local/Docker environments (no CDN)
+ pdfjs.GlobalWorkerOptions.workerSrc = new URL(
+ 'pdfjs-dist/build/pdf.worker.min.mjs',
+ import.meta.url,
+ ).toString();
+
+ export function PdfViewer({ docId }: { docId: string }) {
+ const [numPages, setNumPages] = useState(0);
+ const [pageNumber, setPageNumber] = useState(1);
+ const [scale, setScale] = useState(1.0);
+
+ return (
+
+ );
+ }
+ ```
+
+ NOTE: The `with: { client: true }` relation requires that the Drizzle relations are defined in schema.ts. If Phase 3 already defined client/documents relations, use those. If not, add the minimal relation:
+ ```typescript
+ // In schema.ts, add after table definitions:
+ import { relations } from 'drizzle-orm';
+ export const documentsRelations = relations(documents, ({ one }) => ({
+ client: one(clients, { fields: [documents.clientId], references: [clients.id] }),
+ }));
+ ```
+ If relations already exist in schema.ts (check the existing file), extend them rather than overwriting.
+
+ Also ensure that clicking a document name in the client profile's documents table navigates to `/portal/documents/{doc.id}` — read the existing DocumentsTable component from Phase 3 and add the link if not present.
+
+
+ ```bash
+ cd /Users/ccopeland/temp/red/teressa-copeland-homes && npm run build 2>&1 | grep -E "error|Error|compiled" | head -20
+ ```
+ Expected: "compiled successfully" or "Compiled" with no errors referencing react-pdf, PdfViewer, or AddDocumentModal.
+
+
+ - Client profile page has "Add Document" button that opens the modal.
+ - Modal shows searchable template list and file picker fallback.
+ - Submitting the modal creates a document and refreshes the documents list.
+ - Document name in client profile links to `/portal/documents/{id}`.
+ - Document detail page renders PdfViewer with page nav, zoom, and download controls.
+ - Build passes with no errors.
+
+
+
+
+
+
+- `npm run build` completes without errors
+- AddDocumentModal imports from react-pdf DO NOT include CDN worker URLs
+- Both CSS layers imported in PdfViewer: AnnotationLayer.css and TextLayer.css
+- Document detail page uses `await params` (Next.js 15 pattern)
+- PdfViewer uses `new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url)` for worker — not a CDN URL
+
+
+
+Agent can open the forms library modal, add a document from the library or via file picker, see it appear in the documents list, click it to navigate to the detail page, and see the PDF rendered in the browser with page navigation and zoom.
+
+
+
diff --git a/.planning/phases/04-pdf-ingest/04-04-PLAN.md b/.planning/phases/04-pdf-ingest/04-04-PLAN.md
new file mode 100644
index 0000000..d61eb53
--- /dev/null
+++ b/.planning/phases/04-pdf-ingest/04-04-PLAN.md
@@ -0,0 +1,134 @@
+---
+phase: 04-pdf-ingest
+plan: 04
+type: execute
+wave: 4
+depends_on:
+ - 04-01
+ - 04-02
+ - 04-03
+files_modified: []
+autonomous: false
+requirements:
+ - DOC-01
+ - DOC-02
+ - DOC-03
+must_haves:
+ truths:
+ - "Agent can open 'Add Document' modal from a client profile page"
+ - "Forms library list appears in the modal (either seeded forms or empty-state message)"
+ - "Agent can search/filter the forms list by name"
+ - "Agent can add a document via template selection and see it appear in the documents list"
+ - "Agent can upload a custom PDF via the file picker and see it appear in the documents list"
+ - "Agent can click a document name and reach the document detail page"
+ - "PDF renders in the browser on the document detail page — pages visible, not blank"
+ - "Page navigation (Prev/Next) and Zoom In/Out controls work"
+ - "Download button downloads the PDF"
+ artifacts:
+ - path: "teressa-copeland-homes/uploads/clients/"
+ provides: "Confirms files are being stored at the correct path"
+ key_links:
+ - from: "Client profile Add Document button"
+ to: "Document detail page PDF render"
+ via: "modal → POST /api/documents → documents list → /portal/documents/{id} → PdfViewer → /api/documents/{id}/file"
+---
+
+
+Human verification of the complete Phase 4 PDF ingest flow. Agent (Teressa) confirms the end-to-end workflow works in the browser before Phase 4 is declared complete.
+
+Purpose: Functional verification catches UI/UX issues that automated checks miss — blank PDFs, missing form items, broken nav controls.
+Output: Phase 4 approved or issues logged for gap closure.
+
+
+
+@/Users/ccopeland/.claude/get-shit-done/workflows/execute-plan.md
+@/Users/ccopeland/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/phases/04-pdf-ingest/04-CONTEXT.md
+
+
+
+
+
+
+ Complete Phase 4 PDF ingest pipeline:
+ - forms_templates DB table + seed script (npm run seed:forms)
+ - GET /api/forms-library — authenticated template list
+ - POST /api/documents — creates document record + copies PDF to uploads/clients/{id}/
+ - GET /api/documents/[id]/file — authenticated PDF streaming with path traversal protection
+ - "Add Document" modal on client profile page with searchable library + file picker fallback
+ - Document detail page with react-pdf viewer (page nav, zoom, download)
+
+
+ Run the dev server if not already running:
+ ```
+ cd teressa-copeland-homes && npm run dev
+ ```
+
+ **Step 1 — Seed a test form (if seeds/forms/ is empty):**
+ - Download any real estate PDF (or use any PDF) and copy it to `teressa-copeland-homes/seeds/forms/purchase-agreement.pdf`
+ - Run: `npm run seed:forms`
+ - Confirm output: "Seeded 1 forms into form_templates."
+
+ **Step 2 — Open a client profile:**
+ - Log in at http://localhost:3000/login
+ - Navigate to Clients, click on any client
+ - Confirm: "Add Document" button is visible on the client profile page
+
+ **Step 3 — Add document from library:**
+ - Click "Add Document"
+ - Confirm: modal opens with a searchable list (shows "purchase agreement" or whatever was seeded, OR "No forms found" if seeds are empty)
+ - Type in the search box — confirm list filters
+ - Click a form to select it — confirm document name pre-fills
+ - Edit the name to something like "Test Purchase Agreement"
+ - Click "Add Document"
+ - Confirm: modal closes, new document appears in the documents list on the profile page
+
+ **Step 4 — Add custom PDF via file picker:**
+ - Click "Add Document" again
+ - Click "Browse files" / the file input
+ - Select any PDF from your computer
+ - Confirm: name pre-fills from filename
+ - Edit name, submit
+ - Confirm: document appears in the list
+
+ **Step 5 — View PDF in browser:**
+ - Click a document name from the documents list
+ - Confirm: navigates to `/portal/documents/{id}`
+ - Confirm: PDF renders (pages visible — NOT blank white)
+ - Confirm: "Prev" / "Next" buttons work for multi-page PDFs
+ - Confirm: "Zoom In" / "Zoom Out" buttons change page size
+ - Confirm: "Download" button downloads the PDF
+ - Confirm: "Back to [Client Name]" link returns to the client profile
+
+ **Step 6 — Verify file storage:**
+ - Check that `teressa-copeland-homes/uploads/clients/{clientId}/` directory contains `.pdf` files
+ - Confirm files persist after stopping and restarting the dev server
+
+ **Step 7 — Verify authentication:**
+ - In an incognito window (no session), try: http://localhost:3000/api/forms-library
+ - Confirm: returns "Unauthorized" (not a PDF list)
+ - Try: http://localhost:3000/api/documents/any-id/file
+ - Confirm: returns "Unauthorized" (not a file)
+
+
+ Type "approved" if all 7 steps pass.
+ Or describe any issues found (e.g., "PDF renders blank", "modal list empty", "upload fails").
+
+
+
+
+
+
+All 7 verification steps pass as described above.
+
+
+
+Agent confirms: forms library accessible, document creation from template works, custom upload works, PDF renders with navigation controls, files stored in uploads/, unauthenticated API access blocked.
+
+
+