* fix: tree editor authoring blockers - scroll trap, form density, branching hint - Replace fixed viewport height with flex layout in NodeEditorPanel - Make footer sticky so Save/Cancel always reachable - Compact root node banner to single-line with InfoTip tooltip - Reduce resolution note from callout box to inline text - Add answer-first branching hint below options label Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: broken functionality - auth errors, toast logic, role update, routing, step library - Extract backend error detail in auth store login/register - Fix inverted 4xx toast logic and add 429 rate limit handling - Send account_role field to match backend schema in role update - Use type-aware routing for Repeat Last Session button - Add step library placeholder page and route, remove dot badge Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: navigation correctness - back buttons, exit dialog, dedup nav, redirects - 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> * refactor: shared components, ConfirmDialog migration, pinned flow fixes - Create shared Spinner component with sm/md/lg sizes - Migrate 13 page-level spinners to shared Spinner - Promote EmptyState to shared component, adopt in MyShares and SessionHistory - Replace window.confirm with ConfirmDialog in 3 files - Fix PinnedFlow.tree_type to include maintenance, update emoji display - Verify sidebar unpin handler already correct (no-op) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: visual consistency - toasts, typography, focus rings, container padding - Remove richColors from Sonner toasts, limit stacking to 3 - Add font-heading to all page H1s (7 files) - Add font-label (Outfit) to TagBadges component - Fix focus ring tokens on analytics pages - Replace deprecated glass-stat with design system tokens - Standardize container padding on analytics pages Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: backend alignment - remove drafts toggle, clean dead code, truncation indicator - Remove non-functional drafts toggle and clean TreeFilters type - Fix AccountInvite type to match backend schema - Remove dead API methods: pinnedFlows.pin/reorder, trees.getSharedTree - Remove unused types: SessionListResponse, RatingCreate.is_verified_use - Add session list truncation indicator with size=51 lookahead Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove bg-black from PageLoader and RouteError, fix PageLoader height PageLoader used h-screen inside a grid cell, causing it to overflow. Changed to h-full so it fits within the main-content area. Removed bg-black from both PageLoader and RouteError in favor of theme-aware bg-background to prevent black flash during lazy loading. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: guard against Pydantic validation error objects in toast/error messages FastAPI returns `detail` as an array of objects for 422 validation errors, not a string. Passing these objects to toast.error() or rendering them in JSX crashes React with Error #31 ("Objects are not valid as a React child"). Now checks typeof detail === 'string' before using it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: toast styling, node editor first-click, action node placeholder pattern 1. Toast fixes: Add theme="dark" to Sonner, use !important CSS overrides instead of zero-specificity :where() selectors, suppress noisy 4xx global toasts (pages handle their own errors) 2. Node editor first-click: Add node.type to draft initialization useEffect deps so draft resets when answer stub converts to real type 3. Action node redesign: Remove NodePicker dropdown, auto-create answer placeholder on save (matching decision node pattern). Users click the placeholder on canvas to choose type and fill in details. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: auto-seed test users when release command fails on PR envs The background seeder now creates users directly via DB if login fails, instead of silently aborting. This handles Railway PR environments where the releaseCommand may not execute properly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove categories/tags from sidebar to prevent footer clipping Categories and Tags sections were pushing Feedback, Account, and Collapse off-screen when All Flows expanded its children. These filters already exist on the TreeLibraryPage, so the sidebar duplicates were removed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- 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