- Standardize all procedural back/exit paths to /trees (not /my-trees) - Add exit button with ConfirmDialog to procedural session top bar - Consolidate duplicate account links in sidebar and topbar - Auto-redirect non-owners to personal analytics - Add toast feedback before silent permission redirects in tree editor - Delete orphaned AdminCategoriesPage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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:
- Canonical post-editor/post-session destination:
/trees(the full flow library)./my-treesremains available for creator-specific management but is not a navigation target from editors or sessions. - Step Library handling: Add a placeholder route now. Not hidden, not fully built.
- Cleanup strategy: Immediate removal of dead code and unused types. No staged deprecation. One final
grepsweep 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)]) withh-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-24on 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_idauto-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-loadedStepLibraryPage.tsxplaceholder shellStepLibraryPage.tsx— create placeholder page (e.g., "Step Library — Coming Soon" with consistent layout)Sidebar.tsx— removebadge="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?.detailbefore 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_adminuser) - 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→/treesProceduralNavigationPage.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
ConfirmDialogbefore navigating to/trees - If no progress, navigate directly
2.3 Remove duplicate account links in global nav
"Team" and "Settings" both point to /account. Same duplication in TopBar dropdown.
Files:
Sidebar.tsx(lines ~206-207) — consolidate footer to single "Account" itemTopBar.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 eachnavigate()call
2.6 Delete orphaned AdminCategoriesPage
Not connected to any route, superseded by admin/GlobalCategoriesPage.tsx.
- Delete
AdminCategoriesPage.tsx - Remove its export from
index.tsif 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/mewith 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.tsxProceduralEditorPage.tsxTreeEditorPage.tsxTreeNavigationPage.tsxSessionHistoryPage.tsxSessionDetailPage.tsxMySharesPage.tsxMyTreesPage.tsxAccountSettingsPage.tsxSharedSessionPage.tsxPageLoader.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.tsxfor 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
setPinnedFlowsupdate to after successfulawait(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 thePinnedFlow.tree_typeunion typePinnedFlowsSection.tsx— validate icon/path logic viagetTreeNavigatePathhandles maintenance correctly
Phase 3 Verification
- All page-level loading states use the shared
Spinnercomponent (visual consistency) - Empty states in MyShares, SessionHistory, TreeLibrary use the shared
EmptyStatecomponent - 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
richColorsprop - Add
visibleToasts={3}andgap={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.tsxTeamAnalyticsPage.tsxMyAnalyticsPage.tsxFeedbackPage.tsxAccountSettingsPage.tsxTeamCategoriesPage.tsxadmin/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-ring→focus:ring-primary/20
4.6 Replace deprecated glass-stat style
File: AccountSettingsPage.tsx (line ~588)
- Replace
glass-statwithbg-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/20glow - No
glass-statclass 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— removeshowDraftsstate and drafts filter UItree.ts(TreeFilterstype) — removeinclude_draftsfield
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=51from 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 resultsgrep -r "invited_by_id\|accepted_by_id" frontend/src/returns zero results forAccountInviteusagegrepfor 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 buildpasses 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-libraryinrouter.tsx
Type/interface updates:
TreeFiltersintree.ts: removeinclude_draftsAccountInviteinaccount.ts: removeinvited_by_id,accepted_by_idPinnedFlow.tree_typeinpinnedFlows.ts: add'maintenance'RatingCreateinstep.ts: removeis_verified_use- Remove unused
SessionListResponseinsessions.ts
Shared component surface:
- Add
Spinnercomponent inSpinner.tsx - Add common
EmptyStatecomponent inEmptyState.tsx
Cross-Phase Verification Checklist
Run after each phase is complete:
- Build:
cd frontend && npm run build— must pass with zero errors - Navigation: Test all sidebar items, back buttons, editor → library flow, procedural navigation exit
- Auth: Intentional wrong password → shows backend error detail
- Role change: Test in Account Settings (requires
team_adminuser) - Visual spot-check: H1 fonts, spinner consistency, empty states, toast styling, dark mode
- Grep sweep: Before merging each phase, confirm no dead references remain for removed items
Assumptions
/treesis the canonical destination for broad flow browsing and all post-task returns/my-treesremains 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