docs(19-02): complete template editor UI plan

- SUMMARY.md documents list page, editor page, TemplatePageClient, TemplatePanel
- STATE.md: plan advanced to 2, progress 97%, 2 decisions logged
- ROADMAP.md: phase 19 updated (2/3 plans complete)
- REQUIREMENTS.md: TMPL-05 through TMPL-09 marked complete
This commit is contained in:
Chandler Copeland
2026-04-06 13:17:09 -06:00
parent 10ea48d5ba
commit c81e1e2187
4 changed files with 129 additions and 29 deletions

View File

@@ -129,17 +129,17 @@
### Template Management
- [x] **TMPL-01**: Agent can create a new template by selecting a PDF from the forms library
- [ ] **TMPL-02**: Agent can rename a template
- [ ] **TMPL-03**: Agent can delete a template (soft-delete — preserved if any documents were created from it)
- [ ] **TMPL-04**: Agent can view a list of all saved templates with the form name and field count
- [x] **TMPL-02**: Agent can rename a template
- [x] **TMPL-03**: Agent can delete a template (soft-delete — preserved if any documents were created from it)
- [x] **TMPL-04**: Agent can view a list of all saved templates with the form name and field count
### Template Editor
- [ ] **TMPL-05**: Agent can open a template in an editor and drag-drop fields onto the PDF (reuses existing FieldPlacer)
- [ ] **TMPL-06**: Agent can use AI auto-place to populate fields on a template
- [ ] **TMPL-07**: Template fields use signer role labels (e.g. "Buyer", "Seller") instead of specific email addresses — roles are resolved to real emails only when the template is applied to a client document
- [ ] **TMPL-08**: Agent can set text hints on client-text fields in the template (shown as placeholder to signers)
- [ ] **TMPL-09**: Agent can save the template — fields and role assignments are persisted
- [x] **TMPL-05**: Agent can open a template in an editor and drag-drop fields onto the PDF (reuses existing FieldPlacer)
- [x] **TMPL-06**: Agent can use AI auto-place to populate fields on a template
- [x] **TMPL-07**: Template fields use signer role labels (e.g. "Buyer", "Seller") instead of specific email addresses — roles are resolved to real emails only when the template is applied to a client document
- [x] **TMPL-08**: Agent can set text hints on client-text fields in the template (shown as placeholder to signers)
- [x] **TMPL-09**: Agent can save the template — fields and role assignments are persisted
### Apply Template to Document
@@ -271,14 +271,14 @@ Which phases cover which requirements. Updated during roadmap creation.
| DEPLOY-04 | Phase 17 | Complete |
| DEPLOY-05 | Phase 17 | Complete |
| TMPL-01 | Phase 18 | Complete |
| TMPL-02 | Phase 18 | Pending |
| TMPL-03 | Phase 18 | Pending |
| TMPL-04 | Phase 18 | Pending |
| TMPL-05 | Phase 19 | Pending |
| TMPL-06 | Phase 19 | Pending |
| TMPL-07 | Phase 19 | Pending |
| TMPL-08 | Phase 19 | Pending |
| TMPL-09 | Phase 19 | Pending |
| TMPL-02 | Phase 18 | Complete |
| TMPL-03 | Phase 18 | Complete |
| TMPL-04 | Phase 18 | Complete |
| TMPL-05 | Phase 19 | Complete |
| TMPL-06 | Phase 19 | Complete |
| TMPL-07 | Phase 19 | Complete |
| TMPL-08 | Phase 19 | Complete |
| TMPL-09 | Phase 19 | Complete |
| TMPL-10 | Phase 20 | Pending |
| TMPL-11 | Phase 20 | Pending |
| TMPL-12 | Phase 20 | Pending |

View File

@@ -416,8 +416,8 @@ Plans:
**Plans**: 3 plans
Plans:
- [ ] 19-01-PLAN.md — FieldPlacer/PdfViewer prop abstraction (onPersist, fieldsUrl, fileUrl), three template API routes (file, fields, ai-prepare), PortalNav Templates link
- [ ] 19-02-PLAN.md — Templates list page with create modal, template editor page (TemplatePageClient + TemplatePanel with roles, AI, save)
- [x] 19-01-PLAN.md — FieldPlacer/PdfViewer prop abstraction (onPersist, fieldsUrl, fileUrl), three template API routes (file, fields, ai-prepare), PortalNav Templates link
- [x] 19-02-PLAN.md — Templates list page with create modal, template editor page (TemplatePageClient + TemplatePanel with roles, AI, save)
- [ ] 19-03-PLAN.md — Full Phase 19 human verification checkpoint (9-step E2E browser test)
**UI hint**: yes
@@ -463,5 +463,5 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 →
| 16. Multi-Signer UI | v1.2 | 1/4 | Complete | 2026-04-03 |
| 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 | 0/TBD | Not started | - |
| 19. Template Editor UI | v1.3 | 2/3 | In Progress| |
| 20. Apply Template and Portal Nav | v1.3 | 0/TBD | Not started | - |

View File

@@ -3,14 +3,14 @@ gsd_state_version: 1.0
milestone: v1.1
milestone_name: Smart Document Preparation
status: executing
stopped_at: Phase 19 plans verified and ready for execution
last_updated: "2026-04-06T19:06:35.220Z"
last_activity: 2026-04-06 -- Phase 19 execution started
stopped_at: Completed 19-02-PLAN.md
last_updated: "2026-04-06T19:16:58.181Z"
last_activity: 2026-04-06
progress:
total_phases: 21
completed_phases: 19
total_plans: 63
completed_plans: 59
completed_plans: 61
percent: 82
---
@@ -25,9 +25,9 @@ See: .planning/PROJECT.md (updated 2026-04-03)
## Current Position
Phase: 19 (template-editor-ui) — EXECUTING
Plan: 1 of 3
Status: Executing Phase 19
Last activity: 2026-04-06 -- Phase 19 execution started
Plan: 2 of 3
Status: Ready to execute
Last activity: 2026-04-06
## Note on v1.1
@@ -105,6 +105,7 @@ Progress: [████████████░░░] 82% (18/22 phases comp
| Phase 17 P02 | 3 | 3 tasks | 6 files |
| Phase 18-template-schema-and-crud-api P01 | 4 | 2 tasks | 2 files |
| Phase 18-template-schema-and-crud-api P02 | 3 | 2 tasks | 2 files |
| Phase 19-template-editor-ui P02 | 4 | 2 tasks | 5 files |
## Accumulated Context
@@ -195,6 +196,8 @@ Recent decisions affecting v1.1 work:
- [Phase 18-template-schema-and-crud-api]: archivedAt nullable timestamp: NULL = active, soft-delete only
- [Phase 18-template-schema-and-crud-api]: fieldCount derived server-side as (signatureFields ?? []).length per D-12 — not stored in DB
- [Phase 18-template-schema-and-crud-api]: POST validates FK existence before INSERT to return 404 not 500 on bad formTemplateId
- [Phase 19-02]: TemplatesListClient placed as sibling file to page.tsx — mirrors ClientsPageClient pattern for separation of concerns
- [Phase 19-02]: ConfirmDialog shown for all role removals (not just when fields > 0) — avoids async fetch for conditional UI; simpler and no UX flicker
### v1.2 Pre-decisions (from research)
@@ -230,6 +233,6 @@ None yet.
## Session Continuity
Last session: 2026-04-06T18:54:04.529Z
Stopped at: Phase 19 plans verified and ready for execution
Resume file: .planning/phases/19-template-editor-ui/19-01-PLAN.md
Last session: 2026-04-06T19:16:58.178Z
Stopped at: Completed 19-02-PLAN.md
Resume file: None

View File

@@ -0,0 +1,97 @@
---
phase: 19-template-editor-ui
plan: "02"
subsystem: template-editor-ui
tags: [templates, ui, pdf-viewer, field-placer, role-management, ai-auto-place]
dependency_graph:
requires:
- 19-01 # PdfViewerWrapper onPersist/fieldsUrl/fileUrl abstraction
- 18-02 # PATCH /api/templates/[id] and /api/templates/[id]/fields endpoints
provides:
- /portal/templates list page with create modal
- /portal/templates/[id] editor page with FieldPlacer in template mode
- TemplatePageClient state owner
- TemplatePanel right panel with role management, AI auto-place, save
affects:
- 20-01 # Apply template — depends on template editor page existing
tech_stack:
added: []
patterns:
- Server component queries documentTemplates with formTemplates LEFT JOIN (mirrors clients/page.tsx)
- TemplatePageClient mirrors DocumentPageClient: state owner pattern, PdfViewerWrapper reuse
- TemplatePanel mirrors PreparePanel: 280px right panel, inline styles, gold/navy color scheme
- Role labels stored in DocumentSigner.email slot (v1.3 Research decision)
- onPersist merges textFillData hints into field.hint for type='text' fields
key_files:
created:
- teressa-copeland-homes/src/app/portal/(protected)/templates/page.tsx
- teressa-copeland-homes/src/app/portal/(protected)/templates/TemplatesListClient.tsx
- teressa-copeland-homes/src/app/portal/(protected)/templates/[id]/page.tsx
- teressa-copeland-homes/src/app/portal/(protected)/templates/[id]/_components/TemplatePageClient.tsx
- teressa-copeland-homes/src/app/portal/(protected)/templates/[id]/_components/TemplatePanel.tsx
modified: []
decisions:
- "[19-02]: TemplatesListClient placed as sibling file to page.tsx (not inline) — mirrors ClientsPageClient pattern for separation of concerns"
- "[19-02]: TemplatePanel onRenameRole/onRemoveRole are async in props signature — enables TemplatePanel to await role operations without holding fetch logic itself"
- "[19-02]: ConfirmDialog shown for all role removals (not just when fields > 0) — simplifies TemplatePanel by avoiding a fields fetch just for the check; UI-SPEC allows this"
metrics:
duration_minutes: 4
completed_date: "2026-04-06"
tasks_completed: 2
files_created: 5
files_modified: 0
---
# Phase 19 Plan 02: Template Editor UI Summary
## One-liner
Template editor UI with list page, create modal, TemplatePageClient state owner, and TemplatePanel with role management, AI auto-place, and save — all wired to the Phase 18 CRUD API and Phase 19-01 PdfViewerWrapper abstractions.
## What Was Built
### Task 1: Templates list page (`/portal/templates`)
**`page.tsx`** (server component) queries `documentTemplates` LEFT JOINed with `formTemplates`, filtered `WHERE archivedAt IS NULL`, ordered by `updatedAt DESC`. Also fetches all `formTemplates` for the create modal picker. Renders `TemplatesListClient`.
**`TemplatesListClient.tsx`** (client component):
- Page heading "Templates" with subtitle showing count
- "+ New Template" button (gold `#C9A84C`)
- Empty state: "No templates yet" with body copy and CTA
- List rows: name (navy/600), form name (gray-500), field count, last-updated date. Row hover `#F0EDE8`. Click navigates via `useRouter().push()`.
- Create modal: overlay + white card, template name input + form select dropdown, POSTs to `/api/templates`, navigates to `/portal/templates/${id}` on 201 success.
### Task 2: Template editor pages
**`[id]/page.tsx`** (server component): queries with `with: { formTemplate: true }`, calls `notFound()` on missing/archived. Passes `templateId`, `templateName`, `formName`, `initialFields` to `TemplatePageClient`.
**`TemplatePageClient.tsx`** (client state owner):
- `deriveRolesFromFields()` extracts signer roles from existing `field.signerEmail` values; falls back to `[Buyer, Seller]` defaults if none.
- `textFillData` initialized from `field.hint` values on existing fields.
- `handlePersist` merges `textFillData[id]` into `field.hint` for `f.type === 'text'` before PATCHing.
- `handleAiAutoPlace` POSTs to `/api/templates/[id]/ai-prepare` and increments `aiPlacementKey` to reload FieldPlacer.
- `handleRenameRole` / `handleRemoveRole` fetch current fields, update `signerEmail`, and PATCH — then increment `aiPlacementKey` to reload.
- `handleSave` PATCHes the template name (fields already persisted via `onPersist` on each drag/drop/delete/resize).
- PdfViewerWrapper receives `fieldsUrl=/api/templates/[id]/fields` and `fileUrl=/api/templates/[id]/file` — operates in template mode.
**`TemplatePanel.tsx`** (right panel, 280px sticky):
- Template name: inline `<input>` with border-bottom style; `onBlur` PATCHes name immediately.
- "Signers / Roles" section: color-dot pills, click-to-rename inline input (Enter/blur commits, Escape cancels), `×` remove button with `ConfirmDialog` confirmation.
- Add role: text input + "Add" button + preset chips (Buyer, Co-Buyer, Seller, Co-Seller — filtered to exclude already-present roles).
- "AI Auto-place Fields" button (navy `#1B2B4B`): CSS spinner + "Placing..." loading state, red error text below.
- "Save Template" button (gold `#C9A84C`): "Saving..." at 0.7 opacity, "Saved" in green `#059669` for 3s on success.
## Verification
- `npx tsc --noEmit`: exits 0 (no errors)
- `npm run build`: succeeds — `/portal/templates` and `/portal/templates/[id]` listed as dynamic routes
## Deviations from Plan
None — plan executed exactly as written, with one minor UX simplification:
**ConfirmDialog shown for all role removals (not just when fields > 0):** The plan specifies fetching field count to conditionally show the dialog. Simplified to always show ConfirmDialog on role removal — avoids a round-trip fetch purely for conditional UI logic, and the dialog message is safe to show generically ("will unassign its field(s)"). This is strictly better UX (no flicker from async check) and consistent with the ConfirmDialog pattern elsewhere.
## Known Stubs
None — all data flows are wired. List page reads from DB. Editor page reads template + renders PDF via `/api/templates/[id]/file`. Fields load from `/api/templates/[id]/fields`. Persist writes to `/api/templates/[id]` via PATCH.