# UI Redesign — Implementation Plan *Branch: `ui-redesign` | Last updated: 2026-04-21* --- ## Overview Replace the basic Phase 6 single-page app (`web/static/`) with the enterprise-grade migration console designed in `docs/ui-mockup/mockup.html`. The backend is complete (Phases 8–13, 108/108 tests passing). All new UI phases are **frontend-only** unless noted. Existing FastAPI routes do not change except where noted under Phase 16 (readiness data) and Phase 19 (Verification API). ### Design reference Open `docs/ui-mockup/mockup.html` in a browser to see all 8 screens before starting. ### Docusign 2024 brand tokens | Token | Value | Usage | |---|---|---| | Cobalt | `#4C00FF` | Primary CTA, active nav highlight | | Inkwell | `#130032` | Left nav background | | Ecru | `#F8F3F0` | Page background | | Poppy | `#FF5252` | Error / Blocked badge | | Slate | `#6B6B9A` | Secondary text, muted labels | | White | `#FFFFFF` | Card surfaces | Typography: `Inter` (Google Fonts), fallback `-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif`. --- ## Current state `web/static/` — three files, ~600 lines total: - `index.html` — 79 lines, single-page layout (header, two panels, history table) - `app.js` — 343 lines, vanilla JS (auth, template list, migrate, history) - `style.css` — 186 lines, basic styles, non-Docusign colours --- ## File structure after redesign Keep no-build-step approach (vanilla JS ES modules, no bundler). Split monolith into logical files served statically by FastAPI. ``` web/static/ index.html # app shell (nav, router outlet, modals) css/ tokens.css # CSS custom properties (brand colours, spacing) base.css # reset, typography, utility classes nav.css # left sidebar nav + top bar cards.css # template cards, readiness badges modals.css # dialog / modal styles tables.css # history and audit tables forms.css # settings form inputs js/ state.js # global app state (project, auth, templates) router.js # hash-based client-side router api.js # thin fetch wrappers for all backend endpoints auth.js # auth status, connect/disconnect, Adobe dialog project.js # project switcher modal, project CRUD (localStorage) templates.js # template list view, readiness badges, filters migration.js # options modal, progress polling, results view verification.js # send test envelope, poll status history.js # history & audit view settings.js # settings screen utils.js # escHtml, formatDate, debounce, etc. ``` `app.js` and `style.css` are **deleted** (replaced by the above). `index.html` is **rewritten** as the app shell. --- ## Phase 14 — App Shell & Navigation **Goal:** Branded shell that all other views live inside. No functional logic yet — just the frame, router, and state container. ### index.html structure ```html
``` ### css/tokens.css ```css :root { --cobalt: #4C00FF; --inkwell: #130032; --ecru: #F8F3F0; --poppy: #FF5252; --slate: #6B6B9A; --white: #FFFFFF; --success: #28A745; --warning: #F0A500; --border: #E0DCF8; --radius-sm: 4px; --radius-md: 8px; --shadow-sm: 0 1px 4px rgba(0,0,0,0.08); --shadow-md: 0 4px 16px rgba(0,0,0,0.12); } ``` ### js/router.js ```js const ROUTES = { '#/dashboard': () => import('./templates.js').then(m => m.renderDashboard()), '#/templates': () => import('./templates.js').then(m => m.renderTemplates()), '#/results': () => import('./migration.js').then(m => m.renderResults()), '#/issues': () => import('./issues.js').then(m => m.renderIssues()), '#/verify': () => import('./verification.js').then(m => m.renderVerification()), '#/history': () => import('./history.js').then(m => m.renderHistory()), '#/settings': () => import('./settings.js').then(m => m.renderSettings()), }; // Default route: #/templates ``` ### js/state.js ```js export const state = { project: null, // { id, name } — loaded from localStorage auth: { adobe: false, docusign: false }, templates: [], // array from /api/templates/status selectedIds: new Set(), lastMigrationResults: null, // results from most recent batch job }; // Simple pub/sub: subscribe(key, fn) / publish(key) ``` ### js/api.js — endpoint wrappers All existing endpoints wrapped: ```js export const api = { auth: { status: () => GET('/api/auth/status'), connectAdobe: () => POST('/api/auth/adobe/connect'), connectDocusign: () => POST('/api/auth/docusign/connect'), exchangeAdobe: (url) => POST('/api/auth/adobe/exchange', { redirect_url: url }), disconnect: (p) => POST(`/api/auth/${p}/disconnect`), }, templates: { status: () => GET('/api/templates/status'), adobe: () => GET('/api/templates/adobe'), docusign: () => GET('/api/templates/docusign'), }, migrate: { run: (body) => POST('/api/migrate', body), batch: (body) => POST('/api/migrate/batch', body), batchStatus: (id) => GET(`/api/migrate/batch/${id}`), history: () => GET('/api/migrate/history'), }, }; ``` ### js/utils.js ```js export const escHtml = str => String(str).replace(/[&<>"]/g, c => …); export const formatDate = iso => new Date(iso).toLocaleDateString(…); export const formatRelative = iso => …; export const debounce = (fn, ms) => { … }; export const uuid = () => crypto.randomUUID(); ``` ### Commit `feat(ui-phase-14): app shell — nav, router, state, brand tokens` --- ## Phase 15 — Project / Customer Context **Goal:** Project switcher so the same installation can manage migrations for multiple customers without mixing history or credentials. ### Data model (localStorage only — no backend) ```js // Stored in localStorage key: 'migrator_projects' { active: "uuid-1", projects: [ { id: "uuid-1", name: "Acme Corp", createdAt: "2026-04-21T…" }, { id: "uuid-2", name: "Globex Inc", createdAt: "2026-04-22T…" }, ] } ``` Credentials remain in the server-side signed cookie session. Switching projects triggers a fresh `/api/auth/status` check (session may still be valid if the user didn't disconnect). ### js/project.js ```js export function listProjects() { … } // returns projects array export function createProject(name) { … } // generates uuid, saves, returns project export function deleteProject(id) { … } export function getActive() { … } // returns active project or null export function setActive(id) { … } // updates localStorage + triggers nav refresh ``` ### Project switcher modal - Opened by clicking project name in nav footer - Lists projects: name + creation date + "Activate" button - "New Project" inline form (name field + Create button) - Deleting a project requires confirmation ("Delete Acme Corp? This cannot be undone.") - First run: modal opens automatically with welcome copy ### Nav footer display Shows `▸ Acme Corp` (truncated to 18 chars). Clicking opens switcher modal. No project → shows `▸ New Project` in amber. ### Commit `feat(ui-phase-15): project switcher — localStorage CRUD, switcher modal` --- ## Phase 16 — Templates View with Readiness Badges **Goal:** Replace the two-panel list with a filterable, sortable single table. Each row shows a readiness badge computed from validation results. ### Readiness badge system | Badge | Colour | Condition | |---|---|---| | Ready | `--success` green | `blockers=[]`, `warnings=[]` | | Caveats | `--warning` amber | `blockers=[]`, `warnings.length > 0` | | Blocked | `--poppy` red | `blockers.length > 0` | | Migrated | `--cobalt` | `status=migrated` and no blockers | | Needs Update | `--warning` amber | `status=needs_update` | | Verified | green + ✓ | post-migration verification passed (Phase 19) | ### Backend update required: `web/routers/templates.py` Add `blockers: list[str]` and `warnings: list[str]` to each template object in `GET /api/templates/status`. Run `validate_template()` on the normalized form if the template has been downloaded; otherwise return empty lists. ```python # In templates.py status endpoint, for each adobe template: normalized_dir = Path(settings.downloads_dir) / f"{template['name']}__{template['id']}" if normalized_dir.exists(): normalized = adobe_folder_to_normalized(str(normalized_dir)) result = validate_template(normalized) blockers = result.blockers warnings = result.warnings else: blockers, warnings = [], [] ``` Add 3 backend tests to `tests/test_api_templates.py`: - `test_status_includes_blockers_and_warnings_fields` - `test_status_blockers_populated_when_template_downloaded` - `test_status_empty_when_not_downloaded` ### js/templates.js ```js export function renderTemplates() { // Fetches state.templates (or refreshes via api.templates.status()) // Renders filterable table into #router-outlet // Columns: ☐ | Name | Readiness | Fields | Last Modified | DS Status | Actions // Filter bar: search input + status dropdown + readiness dropdown // Bulk toolbar (hidden until ≥1 selected): "Migrate X selected" button } export function renderTemplateDetail(adobeId) { // 4-tab layout: Overview | Fields | Issues | Migration History } ``` ### Template detail view (`#/templates/:id`) - **Overview tab:** name, description, roles, document count, last modified date - **Fields tab:** table of fields — type, label, page, role, required, conditional - **Issues tab:** blockers (red cards) + warnings (amber cards) from validation - **Migration History tab:** records from `/api/migrate/history` filtered to this template ### Commit `feat(ui-phase-16): templates view — readiness badges, filters, detail tabs, backend blockers/warnings` --- ## Phase 17 — Migration Workflow UI **Goal:** Options modal → progress view → results view as a cohesive flow. ### Flow ``` Templates view → select ≥1 template → "Migrate Selected" button → Options modal → "Run Migration" → Progress view (replaces modal) → Results view (#/results) ``` ### js/migration.js ```js export function showOptionsModal(selectedIds) { // Renders modal with: // - Dry run toggle (default: off) // - Overwrite existing toggle (default: off, from settings) // - Include documents toggle (default: on, from settings) // - Target folder text input (optional) // - Selected count display // - "Run Migration" button } export async function runMigration(ids, options) { // Calls POST /api/migrate/batch // Returns job_id } export async function pollJob(jobId, onProgress, onComplete) { // Polls GET /api/migrate/batch/{jobId} every 2s // Calls onProgress({ completed, total, results }) // Calls onComplete(finalResults) when status === 'done' } export function renderResults(jobResults) { // Navigates to #/results and renders: // - Summary row: X Created | Y Updated | Z Skipped | W Blocked | V Errors // - Per-template result table // - "Verify Templates" button (pre-loads migrated IDs) // - "Back to Templates" button // - "Export CSV" button (client-side Blob download) } ``` ### Progress view (inline, inside modal) After "Run Migration" is clicked: - Modal content replaces with: progress bar + per-template status list - Each template row: name → ⏳ spinning → ✅ success or ❌ error - "View Results" button appears when job status === 'done' ### Commit `feat(ui-phase-17): migration workflow — options modal, progress polling, results view` --- ## Phase 18 — Issues & Warnings View **Goal:** A dedicated screen to review all validation problems before migrating. ### js/issues.js ```js export function renderIssues() { // Reads state.templates (already has blockers/warnings from Phase 16) // Renders two sections: // BLOCKERS — templates that will fail migration // WARNINGS — templates that will migrate with caveats // Each item: template name | issue message | suggested action link // "Migrate Anyway" button on warning items → showOptionsModal([id]) // "View Template" link → #/templates/:id } ``` ### Nav badge Left nav Issues link shows a red badge with count of blocked templates. Updates whenever `state.templates` changes. ### Commit `feat(ui-phase-18): issues view — blocked and warning templates, nav badge` --- ## Phase 19 — Verification View **Goal:** Send test envelopes to confirm migrated templates work end-to-end. ### New backend: `web/routers/verify.py` ```python POST /api/verify/send body: { template_id: str, recipient_name: str, recipient_email: str } action: GET /v2.1/accounts/{id}/envelopes (create via template) returns: { envelope_id: str } GET /api/verify/status/{envelope_id} action: GET /v2.1/accounts/{id}/envelopes/{envelopeId} returns: { status: str, completed_at: str | null } POST /api/verify/void/{envelope_id} body: { reason: str } action: PUT envelope status to "voided" returns: { voided: true } ``` Register router in `web/app.py`: `app.include_router(verify_router, prefix="/api/verify")`. ### tests/test_api_verify.py Four tests (all mock DocuSign calls with respx): - `test_send_requires_auth` - `test_send_returns_envelope_id` - `test_status_returns_envelope_state` - `test_void_calls_docusign` ### js/verification.js ```js export function renderVerification(preloadedTemplateIds = []) { // Shows list of migrated templates (from history or passed-in IDs) // Per-template row: // - Template name + DS template ID // - "Send Test Envelope" button → opens send dialog // - Status chip (Not Tested | Sent | Delivered | Completed = Verified | Voided) // Send dialog: recipient name + email (pre-filled from settings), "Send" button // After send: row updates with status, "Void" button, polling every 5s } ``` ### Commit `feat(ui-phase-19): verification view + verify API endpoints (send/status/void)` --- ## Phase 20 — History & Audit View **Goal:** Filterable, exportable migration history. ### js/history.js ```js export function renderHistory() { // Calls GET /api/migrate/history // Renders: // - Filter bar: date range, template name search, status filter // - Table: timestamp | template | action | status | DS ID | warnings | checksum // - Expandable row: full blockers/warnings list, field count diff // - "Export CSV" button (client-side) } ``` SHA-256 checksum: first 8 chars displayed, full value in title attribute (tooltip). ### Commit `feat(ui-phase-20): history & audit view — filters, export, checksum display` --- ## Phase 21 — Settings View **Goal:** Central config screen for verification defaults and migration defaults. ### Settings (localStorage key: `migrator_settings`) | Key | Default | UI control | |---|---|---| | `testRecipientName` | `""` | Text input | | `testRecipientEmail` | `""` | Email input | | `autoVoidHours` | `24` | Number input | | `defaultOverwrite` | `false` | Toggle | | `defaultIncludeDocs` | `true` | Toggle | ### js/settings.js ```js export function renderSettings() { // 3 sections: // 1. Verification defaults (name, email, auto-void timer) // 2. Migration defaults (overwrite, include documents) // 3. Connection info (read-only: connected accounts, base URLs) // Save button writes to localStorage // Values pre-loaded into options modal (Phase 17) and send dialog (Phase 19) } ``` ### Commit `feat(ui-phase-21): settings view — verification defaults, migration defaults` --- ## Phase 22 — Smoke Test Checklist & Cleanup **Goal:** Validate the full redesigned UI works end-to-end, update docs. ### tests/UI-SMOKE-TEST.md Manual checklist: - [ ] First run: project switcher opens automatically - [ ] Create project "Test Customer", verify it appears in nav footer - [ ] Connect Adobe Sign via `.env` path → badge turns green - [ ] Connect DocuSign via JWT path → badge turns green - [ ] Templates view loads ≥1 template with correct readiness badge - [ ] Select 2 templates → options modal opens → dry run → results show `dry_run` status - [ ] Select 2 templates → real migration → progress bar counts up → results view - [ ] Navigate to Verification → Send Test → status updates to Completed - [ ] History view shows all migrations with correct counts and checksums - [ ] Issues view shows blocked templates (use a fixture template with no recipients) - [ ] Settings: save test recipient → reopen Settings → values persist ### Final tasks - Run `pytest tests/ -v` — confirm all tests still pass (≥108 + new verify tests) - Update `README.md` — new UI navigation guide section - Update `docs/agent-harness/EXECUTION-BOARD.md` — Phases 14–22 complete - Push `ui-redesign` branch to Gitea - Open PR to `master` ### Commit `feat(ui-phase-22): smoke test checklist, README update, final cleanup` --- ## Dependency order ``` Phase 14 (Shell) └── Phase 15 (Project) └── Phase 16 (Templates + backend readiness data) ├── Phase 17 (Migration workflow) │ └── Phase 18 (Issues view) └── Phase 19 (Verification + verify API) Phase 20 (History) ← depends on Phase 14 only, can run after Phase 14 Phase 21 (Settings) ← depends on Phase 14 only, can run after Phase 14 Phase 22 (Cleanup) ← depends on all phases complete ``` Phases 20 and 21 can be implemented in parallel with Phases 17–19. --- ## What does NOT change - All existing FastAPI routes (`auth.py`, `templates.py`, `migrate.py`) - All backend Python source (`src/`) - All 108 existing tests - `.env` / credential handling - The CLI pipeline (`src/migrate_template.py`) Only backend additions: 1. **Phase 16:** `blockers` + `warnings` fields added to `GET /api/templates/status` 2. **Phase 19:** New `web/routers/verify.py` with 3 envelope endpoints