adobe-to-docusign-migrator/docs/UI-REDESIGN-PLAN.md

574 lines
18 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.

# 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 813, 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
<body>
<nav id="app-nav"> <!-- left sidebar, 220px, Inkwell bg -->
<div id="nav-logo"></div> <!-- docusign SVG logo, white wordmark -->
<ul id="nav-links"></ul> <!-- 7 nav links with icons -->
<div id="nav-project"></div> <!-- project switcher footer -->
</nav>
<div id="app-body">
<header id="top-bar"></header> <!-- breadcrumb + auth chips -->
<main id="router-outlet"></main> <!-- views injected here -->
</div>
<!-- modal containers -->
<div id="modal-overlay" hidden></div>
</body>
```
### 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 1422 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 1719.
---
## 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