250 lines
10 KiB
Markdown
250 lines
10 KiB
Markdown
|
|
---
|
|||
|
|
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
|