Files
resolutionflow/docs/plans/archive/ResolutionFlow_UX_Deep_Dive_Final_Plan.md
chihlasm 932927b9df chore: archive old plan docs + add survey foundation files
Move completed plan docs to docs/plans/archive/. Add survey migration 046
and reference HTML/plan files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 02:03:38 -05:00

16 KiB

ResolutionFlow UX Deep Dive — Final Merged Implementation Plan

Date: 2026-02-19
Author: Michael Chihlas
Purpose: Comprehensive frontend UX sweep merging the original 35-issue audit with Codex-revised plan. Ordered by user impact, covering broken functionality, navigation, shared components, visual consistency, and backend alignment.


Locked Decisions

These decisions are finalized and should not be revisited:

  1. Canonical post-editor/post-session destination: /trees (the full flow library). /my-trees remains available for creator-specific management but is not a navigation target from editors or sessions.
  2. Step Library handling: Add a placeholder route now. Not hidden, not fully built.
  3. Cleanup strategy: Immediate removal of dead code and unused types. No staged deprecation. One final grep sweep before each deletion to confirm zero references.

Phase 0 — Tree Editor Authoring Blockers (Immediate)

Goal: Remove "can't reach bottom / too busy form" friction before the broader UX sweep. This phase was absent from the original audit and added by the Codex review.

0.1 Fix node editor scroll trap and bottom clipping

File: NodeEditorPanel.tsx

  • Replace fixed viewport math (h-[calc(100vh-105px)]) with h-full min-h-0
  • Keep body as the only scroll container (min-h-0 flex-1 overflow-y-auto)
  • Make footer sticky (sticky bottom-0) so Save/Cancel are always reachable
  • Add scroll-pb-24 on form body to prevent bottom fields hiding behind footer

0.2 Reduce instruction density in decision/action/resolution forms

Files: NodeFormDecision.tsx, NodeFormAction.tsx, NodeFormResolution.tsx

  • Convert long instructional copy to compact labels + InfoTip tooltips
  • Keep one short contextual hint per form section
  • Remove large always-visible prose blocks

0.3 Keep answer-first branching flow explicit

File: NodeEditorPanel.tsx

  • Preserve existing behavior: saving decision options without next_node_id auto-creates answer stubs
  • Add UI hint in decision form: "Options become answer placeholders you type later."

Phase 0 Verification

  • Open a long decision form → verify full vertical scroll to footer fields/buttons
  • Focus bottom fields → confirm they are visible (not clipped behind footer)
  • Decision instructions are compact with info tooltips (no walls of text)
  • Save a decision with two new options → two answer placeholders are auto-created

Phase 1 — Broken Functionality (Critical)

Goal: Fix features that are actively broken or silently wrong right now.

1.1 Register /step-library route

Sidebar links to /step-library but no route exists — users hit a blank page.

Changes:

  • router.tsx — add route with lazy-loaded StepLibraryPage.tsx placeholder shell
  • StepLibraryPage.tsx — create placeholder page (e.g., "Step Library — Coming Soon" with consistent layout)
  • Sidebar.tsx — remove badge="dot" while placeholder is live (no false "new feature" signal)

1.2 Preserve backend auth error detail in login/register flows

Login failures show "Request failed with status code 401" instead of "Invalid credentials."

File: authStore.ts (lines ~50-54, 63-67)

  • Extract error.response?.data?.detail before falling back to generic message
  • Apply to both login and register error paths

1.3 Fix inverted 4xx toast logic and add 429 handling

File: client.ts (lines ~36-43)

4xx errors with a detail message currently suppress the toast entirely (inverted condition). No 429 handler exists.

Behavior after fix:

Status Action
401 Suppressed (handled by token refresh flow)
429 Always toast: backend detail or "Rate limit exceeded, please retry shortly."
Other 4xx Toast detail if present, else "Invalid request."
5xx Existing generic server error toast (unchanged)

1.4 Fix role update payload contract mismatch

File: accounts.ts (line ~28)

Sends { role } but backend schema expects { account_role }. Role changes silently fail with 422.

  • Change to { account_role: role }

1.5 Fix "Repeat Last Session" broken for non-troubleshooting flows

File: TreeLibraryPage.tsx (line ~453)

Currently hardcodes /trees/:id/navigate — loses prefill state via safety redirect for procedural/maintenance flows.

  • Use getSessionResumePath() instead of hardcoded path

Phase 1 Verification

  • Step Library nav opens placeholder page from Sidebar, AppLayout mobile nav, and Quick Launch
  • Login with bad credentials shows backend detail string (e.g., "Invalid credentials"), not axios error
  • Force a 429 response and verify toast appears
  • Change a team member's role in Account Settings → confirm it actually saves (requires team_admin user)
  • Repeat Last Session works correctly for troubleshooting, procedural, and maintenance flow types

Phase 2 — Navigation Correctness (Medium Scope)

Goal: Fix cases where users end up at unexpected pages or lack navigation affordances.

2.1 Standardize editor/session back and exit targets to /trees

Files:

  • ProceduralEditorPage.tsx (lines ~86, 92, 157) — change /my-trees/trees
  • ProceduralNavigationPage.tsx (lines ~120, 180, 304, 330) — error, cancel, completion, and intake cancel paths all → /trees

2.2 Add explicit exit affordance during procedural session execution

Currently there is no way to leave a procedural session mid-execution except the browser back button.

File: ProceduralNavigationPage.tsx (top bar, ~line 345)

  • Add an Exit button to the top bar
  • If session has progress, exit opens a ConfirmDialog before navigating to /trees
  • If no progress, navigate directly

"Team" and "Settings" both point to /account. Same duplication in TopBar dropdown.

Files:

  • Sidebar.tsx (lines ~206-207) — consolidate footer to single "Account" item
  • TopBar.tsx — consolidate dropdown to single "Account" entry

2.4 Improve analytics routing for non-owners

Non-owners click Analytics, hit an access-denied wall, then must click through to personal stats.

File: TeamAnalyticsPage.tsx (lines ~48-65)

  • Auto-redirect non-owner/non-admin users to /analytics/me
  • Show an informational toast explaining the redirect (e.g., "Viewing your personal analytics")

2.5 Add feedback before permission redirects in tree editor

TreeEditorPage silently redirects when users lack permission — no feedback shown.

File: TreeEditorPage.tsx (lines ~143-146, 157-159)

  • Add toast.error("You don't have permission to edit this flow") before each navigate() call

2.6 Delete orphaned AdminCategoriesPage

Not connected to any route, superseded by admin/GlobalCategoriesPage.tsx.

  • Delete AdminCategoriesPage.tsx
  • Remove its export from index.ts if present

Phase 2 Verification

  • Procedural editor Back button → /trees (not /my-trees)
  • Procedural session cancel, exit, error, and completion → all route to /trees
  • Exit button is visible during procedural execution and prompts confirmation if session has progress
  • Sidebar footer shows one "Account" item (not "Team" + "Settings")
  • Non-owner clicks Analytics → auto-redirects to /analytics/me with toast
  • Unauthorized tree edit attempt → toast shown, then redirect

Phase 3 — Shared Components & Quick Consistency Wins (Medium Scope)

Goal: Build reusable infrastructure and fix small but important consistency issues.

3.1 Create shared Spinner component

Currently 4 different spinner implementations across 20+ files.

Create: Spinner.tsx with sm | md | lg sizes, default border-t-primary

Migrate page-level loading states in:

  • ProceduralNavigationPage.tsx
  • ProceduralEditorPage.tsx
  • TreeEditorPage.tsx
  • TreeNavigationPage.tsx
  • SessionHistoryPage.tsx
  • SessionDetailPage.tsx
  • MySharesPage.tsx
  • MyTreesPage.tsx
  • AccountSettingsPage.tsx
  • SharedSessionPage.tsx
  • PageLoader.tsx

Deferred: Leave tiny inline button spinners for later to avoid churn.

3.2 Promote EmptyState to shared component

Admin has a well-designed EmptyState; main app uses 3+ ad hoc patterns.

  • Move/create common/EmptyState.tsx
  • Re-export from admin's EmptyState.tsx for backward compatibility
  • Adopt in: MySharesPage.tsx, SessionHistoryPage.tsx, TreeLibraryPage.tsx

3.3 Replace native window.confirm() with design-system ConfirmDialog

3 places use native browser dialogs that break the design system.

Files:

  • MySharesPage.tsx (line ~81)
  • NodeEditorPanel.tsx (line ~87)
  • FolderSidebar.tsx (line ~286)

3.4 Fix sidebar optimistic unpin bug

State is removed immediately even if API call fails — the flow disappears permanently until page refresh.

File: Sidebar.tsx (lines ~105-113)

  • Move setPinnedFlows update to after successful await (not before)

3.5 Fix PinnedFlow.tree_type missing 'maintenance'

Maintenance flows can be pinned but navigation will use the wrong path.

Files:

  • pinnedFlows.ts (line ~7) — add 'maintenance' to the PinnedFlow.tree_type union type
  • PinnedFlowsSection.tsx — validate icon/path logic via getTreeNavigatePath handles maintenance correctly

Phase 3 Verification

  • All page-level loading states use the shared Spinner component (visual consistency)
  • Empty states in MyShares, SessionHistory, TreeLibrary use the shared EmptyState component
  • Deleting a share, removing a node, removing a folder → all show styled dialog (not browser native)
  • Unpin a flow while network is down → flow should NOT disappear (reverts on failure)
  • Pin a maintenance flow → clicking it navigates to the correct path

Phase 4 — Visual Consistency Sweep (Large Scope — Many Small Changes)

Goal: Design system compliance fixes. Each change is small but there are many files.

4.1 Fix Sonner toast styling

The richColors prop overrides custom CSS with garish built-in green/red backgrounds. Existing custom CSS in index.css (lines ~201-251) already defines themed toasts (bg-card, border-border, colored border accents) but richColors overrides them.

File: main.tsx

  • Remove richColors prop
  • Add visibleToasts={3} and gap={8}
  • Keep existing custom toast CSS in index.css; patch only if needed to ensure themed styles fully apply

4.2 Typography: Add font-heading to all page H1s

Missing from approximately half the pages.

Files to update:

  • MyTreesPage.tsx
  • TeamAnalyticsPage.tsx
  • MyAnalyticsPage.tsx
  • FeedbackPage.tsx
  • AccountSettingsPage.tsx
  • TeamCategoriesPage.tsx
  • admin/PageHeader.tsx

4.3 Typography: Add font-label to TagBadges component

Design system requires Outfit font for tags/badges.

File: TagBadges.tsx

4.4 Fix hardcoded light-mode button in TeamAnalyticsPage

Only hardcoded bg-white text-black button in the app — breaks in dark mode.

File: TeamAnalyticsPage.tsx (line ~59)

  • Replace with bg-gradient-brand text-white

4.5 Fix non-standard focus ring tokens

Analytics selects use focus:ring-ring instead of the standard token.

Files: TeamAnalyticsPage.tsx, MyAnalyticsPage.tsx

  • Change focus:ring-ringfocus:ring-primary/20

4.6 Replace deprecated glass-stat style

File: AccountSettingsPage.tsx (line ~588)

  • Replace glass-stat with bg-card border border-border

4.7 Standardize container/padding on analytics pages

Missing responsive padding.

Files: TeamAnalyticsPage.tsx, MyAnalyticsPage.tsx

  • Add container mx-auto px-4 py-6 sm:px-6 sm:py-8

Phase 4 Verification

  • Toast colors/borders follow custom theme (not Sonner rich presets) — check success, error, and info toasts
  • All page H1s use font-heading (Plus Jakarta Sans)
  • Tag badges use font-label (Outfit)
  • TeamAnalytics CTA button renders correctly in both light and dark mode
  • Focus rings on analytics selects use subtle primary/20 glow
  • No glass-stat class remains in the codebase
  • Analytics pages have consistent container spacing matching rest of app

Phase 5 — Backend Alignment & Cleanup (Immediate Removals)

Goal: API contract fixes and dead code removal. All removals are immediate (per locked decision #3), with a final grep sweep before each deletion.

5.1 Remove non-functional drafts toggle from library UI

Backend has no include_drafts parameter — the toggle does nothing.

Files:

  • TreeLibraryPage.tsx — remove showDrafts state and drafts filter UI
  • tree.ts (TreeFilters type) — remove include_drafts field

5.2 Align invite types with backend schema

invited_by_id and accepted_by_id not in backend response — always undefined.

File: account.ts — remove invited_by_id and accepted_by_id from AccountInvite type

5.3 Remove dead/unused client code

Before deleting each item, run a global grep to confirm zero consumers:

Item File What to Remove
pinnedFlowsApi.pin() pinnedFlows.ts Dead method (never called)
pinnedFlowsApi.reorder() pinnedFlows.ts Dead method (never called)
treesApi.getSharedTree() trees.ts Dead method (no route/consumer)
SessionListResponse sessions.ts Unused type
RatingCreate.is_verified_use step.ts Field ignored by backend
AdminCategoriesPage.tsx Orphaned file + export (if not already deleted in Phase 2)

5.4 Add session list truncation indicator

Session history silently truncates at the backend limit with no indication to the user.

File: SessionHistoryPage.tsx

  • Request size=51 from backend
  • If result length is 51: show "Showing first 50 sessions" indicator, render only first 50
  • If result length ≤ 50: show "Showing X sessions"
  • No backend API contract changes required (frontend lookahead strategy)

Phase 5 Verification

  • No drafts toggle visible in TreeLibrary UI
  • grep -r "include_drafts" frontend/src/ returns zero results
  • grep -r "invited_by_id\|accepted_by_id" frontend/src/ returns zero results for AccountInvite usage
  • grep for each removed method/type confirms zero references
  • Session history shows truncation indicator at 50+ results
  • Session history shows "Showing X sessions" at fewer than 50 results
  • cd frontend && npm run build passes with zero errors

Items Explicitly Deferred

Item Reason
Migrate 14+ custom modals to shared Modal component Very high effort, low breakage risk. Migrate incrementally on new work.
Standardize input border radius app-wide Cosmetic, low user impact
Standardize icon sizing method (className vs size prop) No visual difference
Session pagination (full load-more) Larger feature, beyond UX sweep scope
Inline button spinners Leave for later to avoid churn (Phase 3 note)
Full Step Library feature build Intentionally placeholder-only this cycle

Public APIs / Interfaces / Types Changed

Frontend routing:

  • Add new route /step-library in router.tsx

Type/interface updates:

  • TreeFilters in tree.ts: remove include_drafts
  • AccountInvite in account.ts: remove invited_by_id, accepted_by_id
  • PinnedFlow.tree_type in pinnedFlows.ts: add 'maintenance'
  • RatingCreate in step.ts: remove is_verified_use
  • Remove unused SessionListResponse in sessions.ts

Shared component surface:

  • Add Spinner component in Spinner.tsx
  • Add common EmptyState component in EmptyState.tsx

Cross-Phase Verification Checklist

Run after each phase is complete:

  1. Build: cd frontend && npm run build — must pass with zero errors
  2. Navigation: Test all sidebar items, back buttons, editor → library flow, procedural navigation exit
  3. Auth: Intentional wrong password → shows backend error detail
  4. Role change: Test in Account Settings (requires team_admin user)
  5. Visual spot-check: H1 fonts, spinner consistency, empty states, toast styling, dark mode
  6. Grep sweep: Before merging each phase, confirm no dead references remain for removed items

Assumptions

  • /trees is the canonical destination for broad flow browsing and all post-task returns
  • /my-trees remains available but is not a default navigation target
  • Step Library is placeholder-only this cycle — no full feature work
  • No backend API contract changes are required for any item in this plan
  • Session truncation uses the frontend lookahead strategy (size=51)
  • All cleanup is immediate with pre-deletion reference verification