Files
red/.planning/phases/19-template-editor-ui/19-UI-SPEC.md
2026-04-06 12:31:10 -06:00

250 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 19
slug: template-editor-ui
status: draft
shadcn_initialized: false
preset: none
created: 2026-04-06
---
# Phase 19 — UI Design Contract
> Visual and interaction contract for the template editor UI phase. Generated by gsd-ui-researcher, verified by gsd-ui-checker.
---
## Design System
| Property | Value |
|----------|-------|
| Tool | none — manual inline styles + Tailwind utilities |
| Preset | not applicable |
| Component library | none (Radix not installed; components are custom) |
| Icon library | none (text/Unicode symbols used inline, e.g. `+`, `✓`, `×`) |
| Font (sans) | Geist Sans via `var(--font-sans)` / `var(--font-geist-sans)` |
| Font (serif/display) | Cormorant Garamond via `var(--font-serif)` — wordmark only |
Source: `globals.css`, `PortalNav.tsx`, `layout.tsx`
**Note:** `components.json` was not found. The project does not use shadcn. All new components follow the existing inline-style + selective Tailwind pattern established in PreparePanel, ClientsPageClient, and PortalNav.
---
## Spacing Scale
Declared values (multiples of 4 only):
| Token | Value | Usage |
|-------|-------|-------|
| xs | 4px | Icon gaps, dot indicator margin |
| sm | 8px | Inline element spacing, button padding vertical |
| md | 16px | Panel internal padding, form field gaps |
| lg | 24px | Section spacing within panels, list gaps |
| xl | 32px | Page-level top margin |
| 2xl | 48px | Empty-state vertical padding |
| 3xl | 64px | Nav height (established: PortalNav is 64px) |
Exceptions:
- Role color dot: 8px diameter (not a layout token — cosmetic)
- Signer/role pill height: 32px (matches PreparePanel signer pill pattern)
- Right panel (TemplatePanel) width: 280px fixed — matches PreparePanel column width in DocumentPageClient split layout
- AI Auto-place button and Save button minimum height: 36px (matches existing PreparePanel button height)
Source: `PreparePanel.tsx`, `PortalNav.tsx`, `layout.tsx` (2rem = 32px page padding)
---
## Typography
| Role | Size | Weight | Line Height |
|------|------|--------|-------------|
| Body | 14px (0.875rem) | 400 | 1.5 |
| Label / caption | 12px (0.75rem) | 400 | 1.4 |
| Section heading | 14px (0.875rem) | 600 | 1.3 |
| Page heading | 24px (1.5rem) | 700 | 1.2 |
Source: `ClientsPageClient.tsx` (h1: 1.5rem/700), `PreparePanel.tsx` (h2: font-semibold/text-gray-900, body: 0.875rem/400), `PortalNav.tsx` (links: 0.8125rem/500 uppercase)
Weights used: regular (400) and semibold/bold (600/700). No third weight.
Letter-spacing exceptions:
- Nav links: `letter-spacing: 0.08em; text-transform: uppercase` — preserved exactly, no change
- Wordmark: `letter-spacing: 0.02em` — no change
---
## Color
| Role | Value | Usage |
|------|-------|-------|
| Dominant (60%) | `#FAF9F7` (cream) | Page background, panel backgrounds, modal backgrounds |
| Secondary (30%) | `#F9FAFB` / `#F0EDE8` (cream-mid) | Panel interior fill, list row backgrounds, right panel |
| Accent (10%) | `#C9A84C` (gold) | Primary CTA buttons only, active nav underline |
| Navy | `#1B2B4B` | Page headings, nav background, secondary button fill |
| Destructive | `#DC2626` (red-600) | Delete role action button only |
Accent (`#C9A84C`) reserved for:
- "Save Template" button (primary CTA)
- "Add Role" button (secondary CTA within TemplatePanel)
- Active nav link underline (existing pattern, unchanged)
Navy (`#1B2B4B`) used for:
- "AI Auto-place" button fill (matches PreparePanel AI button pattern)
- Page heading text
- Wordmark
Semantic colors:
- Success inline: `#059669` (green — "Saved" confirmation inline text, same as PreparePanel "Sent ✓")
- Error inline: `#DC2626` (red — error messages)
- Muted text: `#6B7280` (gray-500 — counts, timestamps, placeholder copy)
- Border default: `#E5E7EB` (gray-200)
Source: `globals.css`, `PreparePanel.tsx`, `ClientsPageClient.tsx`, `ConfirmDialog.tsx`
---
## Component Inventory
Components this phase must produce (from CONTEXT.md D-12/D-13):
### 1. TemplatePanel (new)
Right panel, 280px wide, sticky within the editor view.
Sections (top to bottom):
1. **Template name** — editable inline `<input>` (14px, border-bottom-only style when focused, gray-200 border at rest). Displays current name from DB.
2. **Roles section** — heading "Signers / Roles" (12px uppercase label), list of role pills, "Add role" button.
3. **Role pill** — colored dot (8px, `background: role.color`) + role label string + rename icon (pencil, text "Edit") + remove button (`×`). Free-form rename via inline edit on click.
4. **Add role** — text input + "Add" button. Preset suggestions: "Buyer", "Co-Buyer", "Seller", "Co-Seller" as clickable chips below the input.
5. **AI Auto-place button** — full-width, navy fill (`#1B2B4B`), white text, 36px height. Label: "AI Auto-place Fields". Shows spinner + "Placing…" during loading.
6. **Save button** — full-width, gold fill (`#C9A84C`), white text, 36px height. Label: "Save Template". Shows "Saving…" during loading. Shows inline "Saved" (green) or error message after.
### 2. TemplatePageClient (new)
State owner. Layout: CSS flexbox row — left: `PdfViewerWrapper` + `FieldPlacer` (flex: 1, min-width: 0), right: `TemplatePanel` (280px, flex-shrink: 0). Gap: 24px. Pattern mirrors `DocumentPageClient`.
### 3. Templates list page (new)
- Page heading: "Templates" (24px/700, navy)
- Subtitle: "{N} template{s}" (14px/400, gray-500)
- "New Template" button: top-right, gold fill (primary CTA). Label: "+ New Template"
- Table/list rows: each row shows template name (14px/600, navy), form name (14px/400, gray-500), field count ("N fields", 12px/400, gray-500), last updated (12px/400, gray-500). Click row → navigate to editor.
- Row hover: `background: #F0EDE8` (cream-mid), cursor pointer.
### 4. PortalNav update
Add "Templates" link between "Clients" and "Profile" in `navLinks` array. No visual change — same style as existing links.
### 5. FieldPlacer update (non-breaking)
Add optional `onPersist?: (fields: SignatureFieldData[]) => Promise<void> | void` prop. When provided, replaces the 4 internal `persistFields(docId, fields)` call sites. Existing callers unaffected.
---
## Interaction States
### AI Auto-place button
| State | Visual |
|-------|--------|
| Idle | Navy fill, "AI Auto-place Fields", full opacity |
| Loading | Navy fill, "Placing…" + spinner (CSS border-radius spin), disabled |
| Success | Button returns to idle; FieldPlacer reloads via key increment |
| Error | Inline error message below button in red-600 text (14px), button returns to idle |
### Save button
| State | Visual |
|-------|--------|
| Idle | Gold fill, "Save Template", full opacity |
| Loading | Gold fill at 0.7 opacity, "Saving…", disabled |
| Success | Inline "Saved" text in green (#059669) below button, fades after 3s |
| Error | Inline error in red below button |
### Role pill inline rename
- Click role label → label becomes a text input (same font, border-bottom style)
- Enter or blur → commits rename, calls PATCH /api/templates/[id] with updated `signatureFields` role strings
- Escape → cancels rename, reverts to original label
### Remove role
- Click `×` on role pill
- If no fields are assigned to that role: remove immediately
- If fields are assigned to that role: show ConfirmDialog — "Remove role?" / "Removing 'Buyer' will unassign {N} field(s). This cannot be undone." / Confirm: "Remove Role" (red-600), Cancel: "Cancel"
### New Template modal (from list page)
- Reuses AddDocumentModal pattern: overlay, white rounded card, form fields.
- Fields: "Template name" (text input), "Select form" (PDF form picker from library).
- CTA: "Create Template" (gold fill). On success: navigate to `/portal/templates/[id]`.
---
## Layout
### Template editor page (`/portal/templates/[id]`)
```
[PortalNav — 64px sticky top]
[main — max-width 1200px, margin: 0 auto, padding: 32px]
[page header — flex row]
[h1 "Edit Template: {name}"] [field count badge "{N} fields placed" — optional, muted]
[editor body — flex row, gap: 24px]
[left: PdfViewerWrapper + FieldPlacer — flex:1]
[right: TemplatePanel — width: 280px, flex-shrink: 0]
```
### Templates list page (`/portal/templates`)
```
[PortalNav — 64px sticky top]
[main — max-width 1200px, margin: 0 auto, padding: 32px]
[page header — flex row space-between]
[h1 "Templates"] [subtitle "{N} templates"] [CTA "+ New Template"]
[list — flex column, gap: 8px]
[template row × N]
```
---
## Copywriting Contract
| Element | Copy |
|---------|------|
| Primary CTA (list page) | "+ New Template" |
| Primary CTA (editor) | "Save Template" |
| Secondary CTA (editor) | "AI Auto-place Fields" |
| Nav link | "Templates" |
| Page heading (list) | "Templates" |
| Page heading (editor) | "Edit Template: {name}" |
| Empty state heading (list) | "No templates yet" |
| Empty state body (list) | "Create a template to reuse field placements across documents." |
| Empty state CTA (list) | "+ Create your first template" |
| Error — AI auto-place | "AI placement failed. Check that the form PDF is accessible and try again." |
| Error — save | "Save failed. Please try again." |
| Loading — AI | "Placing…" |
| Loading — save | "Saving…" |
| Success — save | "Saved" |
| Panel section label — roles | "Signers / Roles" |
| Role add placeholder | "Role label (e.g. Buyer)" |
| Role remove confirm title | "Remove role?" |
| Role remove confirm body | "Removing '{role}' will unassign {N} field(s). This cannot be undone." |
| Role remove confirm CTA | "Remove Role" |
| Role remove cancel | "Cancel" |
| Field count badge | "{N} field{s} placed" |
Source: CONTEXT.md decisions D-04, D-05, D-12, D-13; modeled on PreparePanel copy patterns.
---
## Registry Safety
| Registry | Blocks Used | Safety Gate |
|----------|-------------|-------------|
| shadcn official | none — shadcn not installed | not applicable |
| Third-party | none declared | not applicable |
No third-party registry blocks in scope for this phase.
---
## Checker Sign-Off
- [ ] Dimension 1 Copywriting: PASS
- [ ] Dimension 2 Visuals: PASS
- [ ] Dimension 3 Color: PASS
- [ ] Dimension 4 Typography: PASS
- [ ] Dimension 5 Spacing: PASS
- [ ] Dimension 6 Registry Safety: PASS
**Approval:** pending