diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index e017bd0..cfabaf1 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -468,4 +468,4 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → | 17. Docker Deployment | v1.2 | 2/2 | Complete | 2026-04-03 | | 18. Template Schema and CRUD API | v1.3 | 2/2 | Complete | 2026-04-06 | | 19. Template Editor UI | v1.3 | 3/3 | Complete | 2026-04-06 | -| 20. Apply Template and Portal Nav | v1.3 | 2/2 | Complete | 2026-04-06 | +| 20. Apply Template and Portal Nav | v1.3 | 2/2 | Complete | 2026-04-06 | diff --git a/.planning/STATE.md b/.planning/STATE.md index dd3b802..a26aa65 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,15 +2,15 @@ gsd_state_version: 1.0 milestone: v1.1 milestone_name: Smart Document Preparation -status: complete -stopped_at: Completed 20-02 — human verified all 12 steps; v1.3 Document Templates milestone complete -last_updated: "2026-04-06T21:01:00.000Z" +status: completed +stopped_at: Completed 20-02 — human verified all 12 steps; v1.3 milestone complete +last_updated: "2026-04-06T21:15:36.957Z" last_activity: 2026-04-06 progress: total_phases: 22 - completed_phases: 22 + completed_phases: 21 total_plans: 65 - completed_plans: 65 + completed_plans: 64 percent: 100 --- @@ -24,8 +24,8 @@ See: .planning/PROJECT.md (updated 2026-04-03) ## Current Position -Phase: 20 (apply-template-and-portal-nav) — COMPLETE -Plan: 2 of 2 +Phase: 20 +Plan: Not started Status: All plans complete — v1.3 Document Templates milestone shipped Last activity: 2026-04-06 diff --git a/.planning/phases/20-apply-template-and-portal-nav/20-VERIFICATION.md b/.planning/phases/20-apply-template-and-portal-nav/20-VERIFICATION.md new file mode 100644 index 0000000..4a907b8 --- /dev/null +++ b/.planning/phases/20-apply-template-and-portal-nav/20-VERIFICATION.md @@ -0,0 +1,147 @@ +--- +phase: 20-apply-template-and-portal-nav +verified: 2026-04-06T21:30:00Z +status: passed +score: 8/8 must-haves verified +re_verification: false +gaps: [] +human_verification: + - test: "Start from template end-to-end flow" + expected: "Agent can pick a saved template in Add Document modal, create a document with pre-loaded fields, and use hint chips in PreparePanel — confirmed by human in Plan 02 Task 2 checkpoint (all 12 steps approved)" + why_human: "Visual rendering, field position accuracy, and real-time quick-fill behavior cannot be verified programmatically" +--- + +# Phase 20: Apply Template and Portal Nav — Verification Report + +**Phase Goal:** Agent can start any new client document from a saved template — all fields are pre-loaded with fresh IDs, roles map to real signer emails, text hints appear as quick-fill suggestions — and "Templates" is a top-level portal destination + +**Verified:** 2026-04-06T21:30:00Z +**Status:** PASSED +**Re-verification:** No — initial verification + +--- + +## Goal Achievement + +### Observable Truths + +| # | Truth | Status | Evidence | +|---|-------|--------|---------| +| 1 | Agent sees a "My Templates" tab in the Add Document modal alongside the existing Forms Library | VERIFIED | `AddDocumentModal.tsx` lines 117–140: tab bar with "Forms Library" and "My Templates" buttons using underline-style active state | +| 2 | Agent can pick a saved template and click Add Document to create a document with all template fields pre-loaded | VERIFIED | `AddDocumentModal.tsx` lines 80–89: `selectedDocTemplate` branch sends `documentTemplateId` to POST /api/documents; route.ts lines 59–116: template branch fetches fields, copies with fresh UUIDs, inserts document with `signatureFields: copiedFields` | +| 3 | Every field copied from a template has a fresh UUID — no template field ID appears in the new document | VERIFIED | `route.ts` line 74: `rawFields.map(f => ({ ...f, id: crypto.randomUUID() }))` — spread preserves all field data, only id is overwritten | +| 4 | Template signer roles are auto-mapped to client contacts (first role to client email, second to co-buyer email) | VERIFIED | `route.ts` lines 78–99: unique roles collected in appearance order, client fetched, `clientEmails` built from `[client.email, ...contacts[].email]`, mapped with `clientEmails[i] ?? role` fallback | +| 5 | Agent can override the signer mapping before sending | VERIFIED | `PreparePanel.tsx` lines 143–165: `handleAddSigner` / `handleRemoveSigner` with `onSignersChange` callback; agent can add/remove any signer at any time before sending | +| 6 | Text field hints from template appear as quick-fill suggestions in PreparePanel | VERIFIED | `DocumentPageClient.tsx` lines 37, 50–55, 101–103, 135: fields state fetched from `/api/documents/${docId}/fields`, `selectedFieldHint` derived from `fields.find`, passed as prop; `PreparePanel.tsx` lines 98, 116, 351–359: optional prop accepted, "Template Hint" chip rendered when hint exists | +| 7 | "Templates" is a top-level portal navigation destination | VERIFIED | `PortalNav.tsx` line 10: `{ href: "/portal/templates", label: "Templates" }` in `navLinks` array; route exists at `src/app/portal/(protected)/templates/page.tsx` | +| 8 | Templates list page shows all templates with form name, field count, and last-updated date | VERIFIED | `TemplatesListClient.tsx` lines 126–164: each row renders `template.name`, `template.formName`, field count (`signatureFields.length`), and `new Date(template.updatedAt).toLocaleDateString()` | + +**Score:** 8/8 truths verified + +--- + +### Required Artifacts + +| Artifact | Expected | Status | Details | +|----------|---------|--------|---------| +| `src/app/portal/_components/AddDocumentModal.tsx` | Two-tab modal with Forms Library + My Templates | VERIFIED | 238 lines, contains `activeTab`, `docTemplates`, `selectedDocTemplate`, "My Templates" text, lazy fetch on tab switch | +| `src/app/api/documents/route.ts` | Template apply branch in POST handler | VERIFIED | 143 lines, contains `documentTemplateId`, `documentTemplates`, `clients`, `SIGNER_COLORS`, `signatureFields: copiedFields` | +| `src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx` | Fields state fetch + selectedFieldHint derivation | VERIFIED | Contains `fields` state, `useEffect` fetching `/api/documents/${docId}/fields`, `selectedFieldHint` derivation, prop pass to PreparePanel | +| `src/app/portal/(protected)/documents/[docId]/_components/PreparePanel.tsx` | Template Hint chip in Quick Fill section | VERIFIED | Contains `selectedFieldHint?: string` in interface, destructured, renders "Template Hint" chip at line 357 | +| `src/app/portal/(protected)/templates/page.tsx` | Templates list page | VERIFIED | Server component, queries `documentTemplates` left-joined with `formTemplates`, passes to `TemplatesListClient` | +| `src/app/portal/_components/PortalNav.tsx` | "Templates" nav link | VERIFIED | `{ href: "/portal/templates", label: "Templates" }` confirmed | + +--- + +### Key Link Verification + +| From | To | Via | Status | Details | +|------|----|-----|--------|---------| +| `AddDocumentModal.tsx` | POST /api/documents | `fetch('/api/documents', { body: JSON.stringify({ documentTemplateId: selectedDocTemplate.id }) })` | WIRED | Lines 81–89: method POST, Content-Type application/json, `documentTemplateId` in body | +| POST /api/documents | `documentTemplates` table | `db.query.documentTemplates.findFirst({ where: eq(documentTemplates.id, documentTemplateId) })` | WIRED | Lines 60–63: fetches template with `formTemplate` relation | +| POST /api/documents | `clients` table | `db.query.clients.findFirst({ where: eq(clients.id, clientId) })` | WIRED | Lines 88–94: fetches client, builds `clientEmails` array | +| `DocumentPageClient.tsx` | GET /api/documents/:id/fields | `fetch(\`/api/documents/${docId}/fields\`)` in useEffect | WIRED | Line 51: fetches on mount and on `aiPlacementKey` change, sets `fields` state | +| `DocumentPageClient.tsx` | PreparePanel | `selectedFieldHint={selectedFieldHint}` prop | WIRED | Line 135: prop passed; PreparePanel interface declares `selectedFieldHint?: string` at line 98 | +| `PreparePanel.tsx` | `onQuickFill` callback | hint chip `onClick={() => onQuickFill(selectedFieldId, selectedFieldHint)}` | WIRED | Lines 352–355: standard quick-fill callback pattern matching existing chips | + +--- + +### Data-Flow Trace (Level 4) + +| Artifact | Data Variable | Source | Produces Real Data | Status | +|----------|---------------|--------|--------------------|--------| +| `AddDocumentModal.tsx` — My Templates tab | `docTemplates` | `fetch('/api/templates')` → `GET /api/templates/route.ts` → `db.select(...)from(documentTemplates)` | Yes — real DB query | FLOWING | +| `route.ts` template branch | `copiedFields` | `docTemplate.signatureFields` from DB, mapped with fresh UUIDs | Yes — real DB data | FLOWING | +| `route.ts` template branch | `mappedSigners` | `clientEmails` from `db.query.clients.findFirst`, mapped to `uniqueRoles` | Yes — real DB data | FLOWING | +| `DocumentPageClient.tsx` | `selectedFieldHint` | `fields.find(f => f.id === selectedFieldId)?.hint` from `/api/documents/${docId}/fields` → `doc.signatureFields` from DB | Yes — real DB query in fields route | FLOWING | +| `TemplatesListClient.tsx` | `templates` prop | Server component `db.select(...).from(documentTemplates).leftJoin(formTemplates, ...)` | Yes — real DB query | FLOWING | + +--- + +### Behavioral Spot-Checks + +Step 7b: SKIPPED for UI/modal components (requires running browser). TypeScript compilation substitute: + +| Behavior | Command | Result | Status | +|----------|---------|--------|--------| +| TypeScript compiles without errors | `npx tsc --noEmit` | Zero output (exit 0) | PASS | + +--- + +### Requirements Coverage + +| Requirement | Source Plan | Description | Status | Evidence | +|-------------|------------|-------------|--------|---------| +| TMPL-10 | 20-01-PLAN | Agent can choose "Start from template" and pick a saved template when adding a document | SATISFIED | My Templates tab in `AddDocumentModal.tsx` with template list and `selectedDocTemplate` flow | +| TMPL-11 | 20-01-PLAN | Applying a template creates a document with all template fields pre-loaded (fresh field IDs, positions, types, role assignments copied) | SATISFIED | `route.ts` lines 72–75: `rawFields.map(f => ({ ...f, id: crypto.randomUUID() }))` — all fields copied verbatim with only id replaced; inserted as `signatureFields: copiedFields` | +| TMPL-12 | 20-01-PLAN | Template signer roles auto-mapped to client contacts; agent can override before sending | SATISFIED | Auto-map: `route.ts` lines 78–99. Override: `PreparePanel.tsx` `handleAddSigner` / `handleRemoveSigner` with `onSignersChange` persists to DB | +| TMPL-13 | 20-02-PLAN | Text hints from template appear as quick-fill suggestions in PreparePanel | SATISFIED | `PreparePanel.tsx` lines 351–359: "Template Hint" chip renders when `selectedFieldHint` is truthy, calls `onQuickFill` | +| TMPL-14 | 20-01-PLAN | Editing a template does NOT retroactively change documents created from it | SATISFIED BY DESIGN | Template branch writes a deep copy (spread + new UUID per field) at document creation time. Template `signatureFields` and document `signatureFields` are independent JSONB columns. Confirmed by human in Plan 02 checkpoint step 12. | +| TMPL-15 | 20-02-PLAN (Phase 19 deliverable, verified here) | "Templates" appears as top-level section in portal navigation | SATISFIED | `PortalNav.tsx` line 10: `{ href: "/portal/templates", label: "Templates" }` | +| TMPL-16 | 20-02-PLAN (Phase 19 deliverable, verified here) | Templates list page shows all templates with form name, field count, last-updated date | SATISFIED | `TemplatesListClient.tsx` renders all three data points; `page.tsx` queries DB with `leftJoin` for `formName` and `updatedAt` | + +All 7 TMPL requirements (TMPL-10 through TMPL-16) are accounted for. No orphaned requirements found. + +--- + +### Anti-Patterns Found + +| File | Line | Pattern | Severity | Impact | +|------|------|---------|----------|--------| +| `AddDocumentModal.tsx` | 185 | `Loading templates...` — shown while fetch is in-flight | Info | Intentional loading state, not a stub | + +No blockers or warnings found. The `Loading templates...` text is correct behavior (lazy-load state), not a placeholder. + +--- + +### Human Verification Required + +#### 1. Full Template-to-Document Flow + +**Test:** (Already performed — Plan 02 Task 2 checkpoint approved by human) + +Steps verified: +1. /portal/templates list page shows saved templates with form name, field count, updated date (TMPL-16) +2. "Templates" appears in portal top nav (TMPL-15) +3. Add Document modal shows two tabs: "Forms Library" and "My Templates" (TMPL-10) +4. My Templates tab shows saved templates with name, form name, field count +5. Selecting a template auto-fills document name +6. Add Document creates the document and returns to client page +7. Newly created document shows fields pre-loaded at correct positions (TMPL-11) +8. Text field with hint shows "Template Hint" chip in PreparePanel Quick Fill (TMPL-13) +9. Clicking the chip fills the field with the hint text +10. Forms Library tab still works as before (D-04 regression) +11. Editing the template does NOT change an already-created document's fields (TMPL-14) + +**Status:** APPROVED by human — all 12 steps confirmed in 20-02-SUMMARY.md + +--- + +### Gaps Summary + +No gaps. All 8 must-haves are verified at all four levels (exists, substantive, wired, data-flowing). All 7 TMPL requirements are satisfied. TypeScript compiles with zero errors. Human checkpoint confirmed the end-to-end flow. + +--- + +_Verified: 2026-04-06T21:30:00Z_ +_Verifier: Claude (gsd-verifier)_