178 lines
16 KiB
Markdown
178 lines
16 KiB
Markdown
# Project Research Summary
|
|
|
|
**Project:** teressa-copeland-homes
|
|
**Domain:** Real estate agent portal — PDF e-signing with document template management
|
|
**Researched:** 2026-04-06
|
|
**Confidence:** HIGH — all research based on direct codebase inspection + verified competitor analysis
|
|
|
|
## Executive Summary
|
|
|
|
This milestone (v1.3) adds a document template system to an existing, working PDF e-signing portal. The core product is already built: forms library, drag-drop field placement, AI auto-place, agent signature, multi-signer document sending, and the full signing flow are all live. The template feature is a focused extension — a new `document_templates` database table, a template editor that reuses the existing `FieldPlacer` component with a minor callback abstraction, and an "apply template" path in the new-document flow. No new npm packages are required. The entire feature is a schema extension plus UI reuse.
|
|
|
|
The recommended approach follows the industry-standard template model used by DocuSign, PandaDoc, and OpenSign: templates store a field layout with signer role labels (not actual emails), and roles are resolved to real signer emails at apply time. Template fields must be snapshotted — deep-copied with fresh UUIDs — into each new document at creation. Templates are never live references. This is the correct pattern for document immutability and is already the implicit design of the existing `documents.signatureFields` JSONB column. The implementation must not break this invariant.
|
|
|
|
The primary risks are all data-integrity issues that are easy to introduce and hard to recover from: field ID collisions if template fields carry stable IDs into documents, signer role labels leaking into the signing flow as fake email addresses, and hint text being burned into prepared PDFs as printed content. These risks are fully preventable if the document-creation route enforces ID re-stamping, role-to-email validation, and clear separation of `hint` vs `textFillData`. Build order matters: schema and CRUD API before editor UI, editor UI before apply flow, and the `FieldPlacer` persistence abstraction before the template editor.
|
|
|
|
---
|
|
|
|
## Key Findings
|
|
|
|
### Recommended Stack
|
|
|
|
The existing stack handles this feature entirely. See `.planning/research/STACK.md` for full detail.
|
|
|
|
**Core technologies:**
|
|
- **Drizzle ORM + PostgreSQL:** New `document_templates` table via additive migration — zero changes to existing tables
|
|
- **JSONB columns:** `signatureFields` + `signerSlots` on `document_templates`, matching the existing pattern on `documents`
|
|
- **@cantoo/pdf-lib + react-pdf:** No changes needed — template editor renders the same PDF, no new PDF manipulation
|
|
- **FieldPlacer (existing component):** Reused via a single `onPersist` callback prop addition — not duplicated
|
|
- **openai / GPT-4.1:** Existing AI auto-place pipeline reused via a parallel `/api/templates/[id]/ai-prepare` route
|
|
|
|
One schema distinction is critical: `form_templates` is a read-only seeded catalog of 120+ Utah/CCIM PDF forms. `document_templates` is agent-authored field layouts. These are different concerns and must remain separate tables with a FK from new to old.
|
|
|
|
### Expected Features
|
|
|
|
See `.planning/research/FEATURES.md` for full detail and competitor analysis.
|
|
|
|
**Must have (table stakes — P1):**
|
|
- Save a prepared document as a template (name, source form FK, field layout with role labels, text hints)
|
|
- Template list in portal (`/portal/templates`) with rename and delete actions
|
|
- Apply template when adding a document to a client (one-click pre-loaded document creation)
|
|
- Field layout is copied (snapshot), never referenced live
|
|
- Signer roles ("Seller", "Buyer", "Agent") stored in template; real emails assigned at apply time
|
|
- Text fill hints stored in template; actual values resolved from client record at apply time
|
|
- Agent can re-edit template (overwrite save, no versioning at v1)
|
|
- AI auto-place works in template editor (reuses existing pipeline)
|
|
|
|
**Should have (competitive — P2, after core validates):**
|
|
- "Create template from existing document" — promote a prepared document to a template
|
|
- Template preview — read-only PDF view with field overlays before applying
|
|
|
|
**Defer (v2+):**
|
|
- Template versioning — not needed until agent has >10 templates and reports pain
|
|
- Template sharing / team access — requires multi-agent architecture; out of scope
|
|
- Template categories / folders — flat list with good names is sufficient at <20 templates
|
|
- Public/shareable template links — distinct product surface requiring anonymous sessions
|
|
|
|
### Architecture Approach
|
|
|
|
The architecture is a clean extension of the existing document flow. The `document_templates` table holds field layouts and signer role slots. A `POST /api/templates/[id]/apply` route performs the document creation: copy PDF, deep-clone fields with fresh UUIDs, resolve role labels to emails from the agent-supplied map, insert document row. The template editor reuses `FieldPlacer` via an `onPersist` callback prop (a 5-line addition to the 822-line component). See `.planning/research/ARCHITECTURE.md` for full data flow and component boundaries.
|
|
|
|
**Major components:**
|
|
1. **`document_templates` table** — new schema; stores name, formTemplateId FK, signatureFields JSONB (role labels, not emails), signerSlots JSONB, timestamps
|
|
2. **Template CRUD API** (`/api/templates`, `/api/templates/[id]`) — list, create, fetch, save fields, soft-delete
|
|
3. **Template editor UI** (`TemplateEditorClient` + `TemplatePanel`) — thin wrapper over existing `FieldPlacer` with template-mode save; shares layout with `DocumentPageClient`
|
|
4. **Apply operation** (`POST /api/templates/[id]/apply`) — the core integration: PDF copy + field snapshot + role-to-email resolution + document INSERT
|
|
5. **`AddDocumentModal` update** — adds "Start from template" tab; shows `TemplatePicker` and role-to-email assignment UI
|
|
6. **`/api/templates/[id]/ai-prepare`** — mirrors existing `ai-prepare` route; writes to `document_templates.signatureFields`
|
|
|
|
### Critical Pitfalls
|
|
|
|
See `.planning/research/PITFALLS.md` for full detail, recovery strategies, and verification checklist.
|
|
|
|
1. **Field ID collision** — Template fields must NOT carry IDs into documents. Stamp fresh `crypto.randomUUID()` IDs on every field at document creation time. Never copy field IDs from the template row. Recovery is expensive (DB migration + textFillData rekeying).
|
|
|
|
2. **Template edit retroactively changing existing documents** — Never resolve a document's fields from the template at runtime. `documents.signatureFields` is the authoritative field list for the life of that document. The `formTemplateId` FK is lineage metadata only. Violating this breaks active signing sessions.
|
|
|
|
3. **Signer role labels leaking into `signerEmail`** — Templates use role strings ("Seller") where emails go. The document-creation route must enforce a complete role-to-email map before writing to DB. Validate all `signerEmail` values pass email format before INSERT. Role labels in `signerEmail` silently break signing token routing downstream.
|
|
|
|
4. **`hint` vs `textFillData` confusion** — `hint` is a placeholder shown in the signing UI; it must never be written to the PDF. `textFillData` holds agent-typed values that are burned into the prepared PDF. Storing hints in `textFillData` prints instruction text as PDF content. Distinction must be enforced in the template authoring phase.
|
|
|
|
5. **Template deletion breaking document history** — No hard deletes. Use soft-delete (`archivedAt` column). If hard delete is required, block it when `documents` rows reference the template. Never use `ON DELETE CASCADE` on `documents.formTemplateId` — that would destroy client documents.
|
|
|
|
---
|
|
|
|
## Implications for Roadmap
|
|
|
|
Based on the dependency graph in ARCHITECTURE.md and the pitfall-to-phase mapping in PITFALLS.md, four phases are recommended. Each phase is independently deployable and leaves the existing system working.
|
|
|
|
### Phase 1: Schema + Template CRUD API
|
|
**Rationale:** Everything else depends on the table existing and the API working. Schema is additive — no existing tables change. Safe to deploy to production immediately.
|
|
**Delivers:** `document_templates` table with migration; `GET/POST /api/templates`; `GET/PATCH/DELETE /api/templates/[id]`; `TemplateSignerSlot` interface in schema.ts
|
|
**Addresses:** Table stakes "save a prepared template", "template list with rename/delete"
|
|
**Avoids:** Template deletion cascade (establish soft-delete pattern here); file/DB desync (validate filename at create time)
|
|
**Research flag:** Standard patterns (follows existing `forms-library/route.ts` and `documents/[id]/route.ts` exactly — skip phase research)
|
|
|
|
### Phase 2: FieldPlacer Abstraction + Template Editor UI
|
|
**Rationale:** FieldPlacer must have the `onPersist` callback before the template editor can reuse it. This is a non-breaking change — all existing document consumers pass no prop, behavior unchanged. Template editor then becomes a thin configuration layer.
|
|
**Delivers:** `onPersist` prop on `FieldPlacer`; `TemplateEditorClient` + `TemplatePanel`; `/portal/(protected)/templates/[templateId]/page.tsx`; portal nav link
|
|
**Addresses:** "Agent can re-edit template", signer role slot management in editor
|
|
**Avoids:** Building a separate FieldPlacer implementation (diverging drag-drop logic); branching inside the 822-line component
|
|
**Research flag:** Standard patterns (direct component extension, well-understood — skip phase research)
|
|
|
|
### Phase 3: Apply Template Flow
|
|
**Rationale:** The apply operation is the value-delivery moment — it depends on Phases 1+2. This phase requires the most careful implementation due to three critical pitfalls (field ID collision, role-to-email validation, snapshot independence).
|
|
**Delivers:** `POST /api/templates/[id]/apply`; updated `AddDocumentModal` with "Start from template" tab; `TemplatePicker` component; role-to-email assignment UI; known hint keys auto-resolved from client record
|
|
**Addresses:** Core table stakes "apply template when adding a document"; signer role resolution; text fill hint auto-population
|
|
**Avoids:** Field ID collision (stamp UUIDs at apply time, never copy from template); role labels in signerEmail (validate before INSERT); live field resolution (snapshot is the rule)
|
|
**Research flag:** Needs careful implementation review — the three never-acceptable shortcuts in PITFALLS.md all live here. No external research needed, but this phase warrants the most internal code review.
|
|
|
|
### Phase 4: Template List UI + Portal Integration
|
|
**Rationale:** The list page can be built after the API exists (Phase 1), but is sequenced last because it depends on the full round-trip working (create template in editor, list it, apply it). Completing this phase closes the v1.3 loop.
|
|
**Delivers:** `TemplatesPageClient`; `/portal/(protected)/templates/page.tsx`; template cards with field count, source form name, rename/delete actions; archived template filtering
|
|
**Addresses:** "Template list in portal" table stakes; "template name includes form name" differentiator
|
|
**Avoids:** Archived templates surfacing in library (filter `archivedAt IS NULL` in list API); no preview confusion (show field count summary on card)
|
|
**Research flag:** Standard patterns (matches existing `ClientCard` + `ConfirmDialog` + `ClientsPageClient` pattern — skip phase research)
|
|
|
|
### Phase Ordering Rationale
|
|
|
|
- Schema must precede all API work (no table = no routes)
|
|
- FieldPlacer abstraction must precede template editor (can't reuse a component that isn't abstracted)
|
|
- Apply flow depends on both the API (template data) and the editor (template creation) being in place
|
|
- List UI is independently testable but benefits from the full create-apply round-trip being available for manual verification
|
|
- All phases avoid touching the signing flow, PDF preparation, email sending, and audit events — this isolation is deliberate and must be maintained
|
|
|
|
### Research Flags
|
|
|
|
Phases with standard patterns (skip research-phase):
|
|
- **Phase 1:** Schema migration follows established drizzle-kit pattern (migrations 0000-0011 already in place); CRUD API follows `forms-library/route.ts` and `documents/[id]/route.ts` exactly
|
|
- **Phase 2:** FieldPlacer abstraction is a well-understood prop injection; template editor composes existing components
|
|
- **Phase 4:** List UI follows existing `ClientsPageClient` + `ClientCard` + `ConfirmDialog` pattern verbatim
|
|
|
|
Phases needing implementation-time attention (not external research, but internal discipline):
|
|
- **Phase 3:** Three critical pitfalls converge here. Recommend a peer review checklist against PITFALLS.md "Looks Done But Isn't" section before marking phase complete.
|
|
|
|
---
|
|
|
|
## Confidence Assessment
|
|
|
|
| Area | Confidence | Notes |
|
|
|------|------------|-------|
|
|
| Stack | HIGH | Direct codebase inspection; zero new packages; existing JSONB pattern already in production |
|
|
| Features | HIGH | DocuSign, PandaDoc, OpenSign all confirmed; competitor analysis corroborates feature set; solo-agent scope confirmed from PROJECT.md |
|
|
| Architecture | HIGH | Based on direct inspection of 10 source files and 12 drizzle migrations; no external assumptions |
|
|
| Pitfalls | HIGH | Derived from schema.ts type analysis and existing route code; all pitfalls trace to specific lines in the codebase |
|
|
|
|
**Overall confidence:** HIGH
|
|
|
|
### Gaps to Address
|
|
|
|
- **FieldPlacer `signerEmail` format validation:** STACK.md flags that FieldPlacer and PreparePanel components may have email-format validation that would reject role strings. Confirm no `z.string().email()` or similar check runs on `signerEmail` during template editing before implementing Phase 2. If validation exists, introduce a `mode: "template" | "document"` prop alongside `onPersist`.
|
|
|
|
- **Role-to-email mapping UX for single-signer documents:** FEATURES.md notes that for solo-agent, single-signer use case the role-to-email mapping step can default to the client's email automatically. The exact trigger condition (when to auto-default vs when to prompt) needs to be defined in Phase 3 planning. Suggested rule: if the template has exactly one non-agent signer role, pre-populate it with the assigned client's email.
|
|
|
|
- **`updatedAt` auto-update convention:** Existing tables set `updatedAt` explicitly in update queries (no DB trigger). `document_templates` must follow the same convention. Flag this in Phase 1 implementation notes.
|
|
|
|
---
|
|
|
|
## Sources
|
|
|
|
### Primary (HIGH confidence)
|
|
- Direct inspection: `src/lib/db/schema.ts` — full table schema, `SignatureFieldData` interface, `textFillData` type
|
|
- Direct inspection: `src/app/api/documents/route.ts` — document creation flow (library path + upload path)
|
|
- Direct inspection: `src/app/api/documents/[id]/fields/route.ts` — GET/PUT pattern for signatureFields
|
|
- Direct inspection: `src/app/portal/_components/AddDocumentModal.tsx` — modal flow for new document creation
|
|
- Direct inspection: `drizzle/` migrations 0000-0011 — confirmed additive migration pattern
|
|
- Direct inspection: `package.json` — confirmed zero new packages needed
|
|
- [DocuSign: Working with Templates](https://support.docusign.com/en/guides/ndse-user-guide-working-with-templates)
|
|
- [OpenSign: Create Templates](https://docs.opensignlabs.com/docs/help/Templates/create-template/)
|
|
- [OpenSign: Use Template to Create Documents](https://docs.opensignlabs.com/docs/help/Templates/use-template/)
|
|
|
|
### Secondary (MEDIUM confidence)
|
|
- [PandaDoc: Creating Dynamic Document Templates](https://www.pandadoc.com/blog/creating-dynamic-document-templates/) — template variable and role model
|
|
- [BoldSign: Create eSignature Templates for Unlimited Reuse](https://boldsign.com/electronic-signature-features/templates/) — competitor feature comparison
|
|
|
|
---
|
|
*Research completed: 2026-04-06*
|
|
*Ready for roadmap: yes*
|