From d941c68f58959eff7e682e2cfb4ab41bc79fbebb Mon Sep 17 00:00:00 2001 From: Chandler Copeland Date: Mon, 6 Apr 2026 12:31:10 -0600 Subject: [PATCH] docs(19): UI design contract --- .../19-template-editor-ui/19-UI-SPEC.md | 249 ++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 .planning/phases/19-template-editor-ui/19-UI-SPEC.md diff --git a/.planning/phases/19-template-editor-ui/19-UI-SPEC.md b/.planning/phases/19-template-editor-ui/19-UI-SPEC.md new file mode 100644 index 0000000..b99e990 --- /dev/null +++ b/.planning/phases/19-template-editor-ui/19-UI-SPEC.md @@ -0,0 +1,249 @@ +--- +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 `` (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` 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