Files
red/.planning/phases/19-template-editor-ui/19-CONTEXT.md
2026-04-06 12:24:17 -06:00

8.2 KiB

Phase 19: Template Editor UI - Context

Gathered: 2026-04-06 Status: Ready for planning

## Phase Boundary

UI-only phase (plus one new API route for AI auto-place). Builds the template editor page at /portal/templates/[id] and the supporting infrastructure.

Deliverables:

  1. FieldPlacer — add optional onPersist callback prop (non-breaking)
  2. /portal/templates — list page (all active templates, click to open editor)
  3. /portal/templates/[id] — template editor page (PDF + FieldPlacer in template mode + role panel + save button)
  4. POST /api/templates/[id]/ai-prepare — AI auto-place for templates
  5. Top nav: "Templates" link
## Implementation Decisions

FieldPlacer Abstraction

  • D-01: Add onPersist?: (fields: SignatureFieldData[]) => Promise<void> | void prop to FieldPlacer. When provided, this replaces the internal persistFields(docId, fields) call. When absent (all existing consumers), falls back to the existing internal persistFields behavior — fully backwards compatible.
  • D-02: persistFields is called in 4 places in FieldPlacer. All 4 must be updated to call onPersist(fields) if provided, else persistFields(docId, fields). The internal persistFields function stays for backwards compat.
  • D-03: FieldPlacer's signers prop accepts DocumentSigner[] where email carries either a real email (document mode) OR a role label (template mode). No type change needed — the slot already accepts any string.

Role Labels in Template Editor

  • D-04: Role labels are free-form strings that the agent types OR picks from presets. Preset list: "Buyer", "Co-Buyer", "Seller", "Co-Seller". Agent can type any string (e.g. "Lender", "Trustee").
  • D-05: Template editor starts with two default role labels pre-configured: "Buyer" (color #6366f1 indigo) and "Seller" (color #f43f5e rose). Agent can add, remove, or rename these.
  • D-06: Role colors: same palette as document signers — ['#6366f1', '#f43f5e', '#10b981', '#f59e0b'] cycling by index.
  • D-07: Role labels are stored in documentTemplates.signatureFields via field.signerEmail. When the template editor saves, it calls PATCH /api/templates/[id] with the full signatureFields array (same route as Phase 18 D-10).
  • D-08: The "Active role" selector in the template editor is identical in behavior to the "Active signer" selector in the document FieldPlacer — it's the same component, just with role label strings instead of emails. No new component needed.

Template Editor Page

  • D-09: Route: /portal/templates/[id] — single page view + edit (same pattern as /portal/clients/[id]). No separate /edit route.
  • D-10: Templates list page at /portal/templates — shows all active templates (name, form name, field count, last updated). Click a row to open the editor.
  • D-11: "Templates" appears in the portal top nav (alongside Dashboard, Clients, Profile).
  • D-12: Template editor page layout: PDF viewer on the left (full FieldPlacer), slim right panel (TemplatePanel) with: template name (editable), role list (add/remove/rename), AI Auto-place button, Save button.
  • D-13: TemplatePanel is a new component (separate from PreparePanel). PreparePanel is document-specific (prepare/send/preview). TemplatePanel is template-specific (roles, save).

AI Auto-place for Templates

  • D-14: New route POST /api/templates/[id]/ai-prepare — reads formTemplate.filename to locate the PDF in seeds/forms/, calls existing extractBlanks(filePath) + classifyFieldsWithAI(blanks, null) (no client data for pre-fill in template mode), writes result to documentTemplates.signatureFields via PATCH.
  • D-15: No client name/address pre-fill in template AI mode — classifyFieldsWithAI receives null for client context. Fields are placed by type only.
  • D-16: AI Auto-place button in TemplatePanel triggers POST /api/templates/[id]/ai-prepare, then reloads the FieldPlacer via a key increment (same pattern as existing aiPlacementKey in DocumentPageClient).

Navigation

  • D-17: Portal nav: add "Templates" between "Clients" and "Profile" in the top nav.
  • D-18: Templates list page is a server component that fetches from the database directly (same pattern as the clients list page).

Claude's Discretion

  • Exact TemplatePanel layout details (spacing, button order)
  • Whether to show field count on the editor page header
  • Loading states for AI auto-place and save operations

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

Files Being Modified

  • teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx (822 lines — add onPersist prop)
  • teressa-copeland-homes/src/app/portal/_components/SiteNav.tsx — add Templates nav link

New Files

  • teressa-copeland-homes/src/app/portal/(protected)/templates/page.tsx — list page
  • teressa-copeland-homes/src/app/portal/(protected)/templates/[id]/page.tsx — editor server component
  • teressa-copeland-homes/src/app/portal/(protected)/templates/[id]/_components/TemplatePageClient.tsx — state owner (like DocumentPageClient)
  • teressa-copeland-homes/src/app/portal/(protected)/templates/[id]/_components/TemplatePanel.tsx — right panel (roles, AI, save)
  • teressa-copeland-homes/src/app/api/templates/[id]/ai-prepare/route.ts — AI auto-place route

Existing Patterns to Reuse

  • teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx — state owner pattern; TemplatePageClient follows same structure
  • teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx — onPersist prop addition
  • teressa-copeland-homes/src/app/api/documents/[id]/ai-prepare/route.ts — AI route pattern to copy for templates
  • teressa-copeland-homes/src/app/portal/(protected)/clients/page.tsx — list page pattern
  • teressa-copeland-homes/src/lib/ai/extract-text.ts + field-placement.ts — reused as-is for template AI

Phase 18 Output

  • teressa-copeland-homes/src/lib/db/schema.tsdocumentTemplates table + documentTemplatesRelations
  • teressa-copeland-homes/src/app/api/templates/route.ts — GET/POST
  • teressa-copeland-homes/src/app/api/templates/[id]/route.ts — PATCH/DELETE

</canonical_refs>

<code_context>

Existing Code Insights

Reusable Assets

  • aiPlacementKey + setAiPlacementKey pattern in DocumentPageClient — TemplatePageClient uses the same increment-to-reload mechanism
  • SIGNER_COLORS constant in PreparePanel — same palette for role colors in TemplatePanel
  • isClientVisibleField() — already imported in FieldPlacer; no changes needed
  • PdfViewerWrapper — passes through to FieldPlacer; reused as-is in template editor

Established Patterns

  • Server component page → PageClient.tsx state owner → child components (PreparePanel, FieldPlacer)
  • db.query.documentTemplates.findFirst(...) available via Drizzle relations from Phase 18
  • Field persistence: FieldPlacer calls persistFields(id, fields) internally; adding onPersist prop makes this injectable

Integration Points

  • documentTemplates.signatureFields (Phase 18) — written by template editor via PATCH
  • PATCH /api/templates/[id] (Phase 18) — accepts { signatureFields?: SignatureFieldData[] } — used by TemplatePanel save and AI auto-place
  • Phase 20 reads documentTemplates.signatureFields to copy fields into a new document

</code_context>

## Specific Ideas
  • The "Save" button in TemplatePanel calls PATCH /api/templates/[id] with the current signatureFields from FieldPlacer state.
  • AI Auto-place flow: button click → POST /api/templates/[id]/ai-prepare → route reads PDF → calls extractBlanks + classifyFieldsWithAI → writes to documentTemplates.signatureFields → returns fields → TemplatePageClient increments aiPlacementKey to reload FieldPlacer.
  • The onPersist callback in FieldPlacer: async (fields) => await fetch('/api/templates/{id}', { method: 'PATCH', body: JSON.stringify({ signatureFields: fields }) })
## Deferred Ideas

None — discussion stayed within phase scope.


Phase: 19-template-editor-ui Context gathered: 2026-04-06