diff --git a/docs/plans/2026-02-20-frontend-standardization-prompt.md b/docs/plans/2026-02-20-frontend-standardization-prompt.md new file mode 100644 index 00000000..012f5f4d --- /dev/null +++ b/docs/plans/2026-02-20-frontend-standardization-prompt.md @@ -0,0 +1,263 @@ +# Frontend Standardization: Full Audit & Fix + +## Objective + +Perform a comprehensive audit of the entire ResolutionFlow frontend and standardize four UI patterns that are currently inconsistent across the application. Create reusable components where they don't exist, then retrofit every page and component to use them. When done, the entire app should feel like one developer built every page. + +Use every tool, agent, and skill at your disposal. Search every file. Don't ask — fix. + +--- + +## Pattern 1: Loading States → Replace All Spinners with Skeleton Components + +### The Problem + +Loading states are implemented differently across the app. Some pages use a centered spinning circle, others use pulse-animated rectangles, and some use nothing. There is no shared reusable skeleton component. + +### Current Inconsistencies to Find and Fix + +**Spinner pattern (replace everywhere you find it):** +```tsx +// THIS PATTERN — find every instance and replace with skeletons +
+
+
+``` + +**Known locations (search for more):** +- `frontend/src/pages/SessionHistoryPage.tsx` — uses spinner +- `frontend/src/pages/TreeLibraryPage.tsx` — uses spinner +- `frontend/src/components/step-library/StepLibraryBrowser.tsx` — uses spinner or loading boolean +- `frontend/src/pages/QuickStartPage.tsx` — uses spinner or loading state +- `frontend/src/pages/TreeEditorPage.tsx` — check for loading state +- `frontend/src/pages/SessionDetailPage.tsx` — check for loading state +- `frontend/src/pages/TreeNavigationPage.tsx` — check for loading state +- `frontend/src/pages/MyTreesPage.tsx` — check for loading state +- Any other page or component with `isLoading` state + +### What to Build + +**Create `frontend/src/components/common/Skeleton.tsx`:** + +A set of composable skeleton primitives: + +```tsx +// Base skeleton block with pulse animation +export function Skeleton({ className }: { className?: string }) { + return
+} + +// Pre-built skeleton layouts for common patterns: +export function SkeletonCard() { /* Card-shaped skeleton matching TreeGridView card dimensions */ } +export function SkeletonRow() { /* Row-shaped skeleton matching TreeListView row dimensions */ } +export function SkeletonTableRow() { /* Table row skeleton matching TreeTableView row */ } +export function SkeletonText({ lines = 3 }: { lines?: number }) { /* Text block skeleton */ } +``` + +### The Standard (enforce everywhere) + +- **Page-level data loading:** Show skeleton placeholders that match the shape of the content that will appear. Grid pages get skeleton cards. List pages get skeleton rows. Detail pages get skeleton text blocks. +- **Component-level loading:** Small inline skeletons (e.g., a single line for a name loading). +- **Never show a centered spinner.** The only acceptable spinner is on a button that is performing an action (e.g., "Saving..." with a small inline spinner). Page/section loading always uses skeletons. +- **Skeleton count should match expected content.** If a page typically shows 6 cards, show 6 skeleton cards. If a list shows 10 rows, show 10 skeleton rows. + +### Audit Instructions + +1. Search the entire `frontend/src/` directory for: `animate-spin`, `border-t-transparent`, `Loader2` (Lucide spinner icon), and any `isLoading` state variable. +2. For every match, determine if it's a page/section loading state or a button action state. +3. Replace all page/section loading states with appropriate skeleton components. +4. Leave button-level spinners alone (e.g., "Saving..." on submit buttons is fine). + +--- + +## Pattern 2: Empty States → Add Meaningful Messages + CTAs Everywhere + +### The Problem + +Empty states across the app are bare text with no guidance and no call to action. A new user sees "No sessions found." or "No trees found." and has no idea what to do next. + +### Current Inconsistencies to Find and Fix + +**Known bare empty states:** +- `SessionHistoryPage.tsx`: `"No sessions found."` — no CTA +- `TreeLibraryPage.tsx`: `"No trees found. Try adjusting your filters."` — has filter hint but no create CTA +- `StepLibraryBrowser.tsx`: check for empty state pattern +- `QuickStartPage.tsx`: check for empty state pattern +- `MyTreesPage.tsx`: check for empty state pattern +- Any page that renders a list and has a `length === 0` check + +### What to Build + +**Create `frontend/src/components/common/EmptyState.tsx`:** + +```tsx +interface EmptyStateProps { + icon?: React.ReactNode // Optional icon above the message + title: string // e.g., "No sessions yet" + description?: string // e.g., "Start a troubleshooting session to see it here" + action?: { + label: string // e.g., "Start a Session" + onClick: () => void + } + secondaryAction?: { + label: string // e.g., "Clear filters" + onClick: () => void + } +} +``` + +### The Standard (enforce everywhere) + +Every empty state must have: +1. **A clear title** that says what's empty (not just "No results") +2. **A description** that tells the user why it's empty or what to do +3. **A primary CTA** (when applicable) that lets them take the obvious next action +4. **A secondary action** (when applicable) for filter/search scenarios: "Clear filters" or "Try a different search" + +**Context-specific empty states:** + +| Page / Section | Title | Description | CTA | +|---|---|---|---| +| Session History (no sessions at all) | "No sessions yet" | "Start a troubleshooting session to see your history here" | "Browse Flows" → navigate to /trees | +| Session History (filter returns empty) | "No matching sessions" | "Try adjusting your filters" | "Clear filters" → reset filter | +| Tree Library (no trees at all) | "No flows available" | "Create your first troubleshooting flow to get started" | "Create Flow" → open create dropdown/navigate | +| Tree Library (search returns empty) | "No flows match your search" | "Try different keywords or clear your filters" | "Clear search" → reset search | +| My Trees (no authored trees) | "You haven't created any flows yet" | "Build a troubleshooting flow to guide your team" | "Create Flow" → open create dropdown | +| Step Library (no steps) | "No steps found" | "Create your first reusable step" | "Create Step" → open create form | +| Dashboard Favorites (no pins) | "No favorites yet" | "Star a flow to pin it here for quick access" | No button (action is contextual) | +| Dashboard My Flows (no authored flows) | "You haven't created any flows yet" | "Build your first troubleshooting flow" | "Create your first flow" → open create dropdown | + +### Audit Instructions + +1. Search `frontend/src/` for: `length === 0`, `.length === 0`, `sessions.length`, `trees.length`, and any conditional rendering that shows text when a list is empty. +2. For every match, check if the empty state has a title, description, and CTA. +3. Replace bare text empty states with the `EmptyState` component using the appropriate context from the table above. +4. If you find an empty state not listed in the table, follow the same pattern: clear title, helpful description, obvious next action. + +--- + +## Pattern 3: Accessibility — Aria Labels on All Icon-Only Buttons + +### The Problem + +Some icon-only buttons have proper `aria-label` attributes (e.g., `ThemeToggle` uses `aria-label={`Switch to ${label} theme`}`), but most don't. Icon buttons without aria-labels are invisible to screen readers. + +### The Gold Standard (already in the codebase) + +From `ThemeToggle.tsx`: +```tsx + +``` + +From `StepDetailModal.tsx`: +```tsx + +``` + +### The Standard (enforce everywhere) + +Every `