From ff7894b97780b0cb571300a991ad1738bc3cc87c Mon Sep 17 00:00:00 2001 From: chihlasm Date: Sat, 7 Feb 2026 13:53:14 -0500 Subject: [PATCH] Add UX enhancement design doc: notification system Design document for Tier 1 UX enhancement implementing toast notifications using sonner library. - Comprehensive context on current state problems - Phase-by-phase implementation plan - Design patterns and best practices - Complete verification checklist - Risk assessment and mitigation strategies Related: #33, #34, #35 Co-Authored-By: Claude Sonnet 4.5 --- .../2026-02-07-notification-system-design.md | 427 ++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 docs/plans/2026-02-07-notification-system-design.md diff --git a/docs/plans/2026-02-07-notification-system-design.md b/docs/plans/2026-02-07-notification-system-design.md new file mode 100644 index 00000000..15973216 --- /dev/null +++ b/docs/plans/2026-02-07-notification-system-design.md @@ -0,0 +1,427 @@ +# Tier 1 UX Enhancement: Consistent Notification System + +## Context + +ResolutionFlow (Patherly) currently lacks a consistent feedback system for user actions. MSP engineers switching between multiple contexts need immediate confirmation that actions succeeded to reduce cognitive load and prevent errors. Research shows that 200-300ms feedback timing dramatically improves perceived reliability and user confidence. + +### Current State Problems +- **No success notifications**: Users don't get confirmation when trees/folders/sessions are saved or deleted +- **Inconsistent patterns**: Mix of button state changes, modal errors, and page-level banners +- **Silent operations**: "Add to Folder" silently succeeds with no feedback +- **No dismissible errors**: Error messages persist until page reload or modal close +- **No notification history**: Users can't review recent actions + +### User Impact +Engineers experience cognitive overload troubleshooting client issues. Without clear feedback: +- They second-guess themselves and repeat actions +- They abandon tasks when uncertain if changes saved +- They miss critical errors that appear briefly +- They waste time verifying actions succeeded + +### Why This Feature First +1. **Foundational infrastructure**: Toast system benefits all future features +2. **Immediate wins**: Every save/delete/export action gets better UX +3. **Low risk**: Additive feature, doesn't break existing flows +4. **Quick implementation**: 1-2 days to full deployment +5. **High perceived value**: Users notice and appreciate immediate feedback + +--- + +## Design Overview + +We'll integrate **sonner** (by shadcn creator) - a modern, accessible toast notification library that: +- Works perfectly with Tailwind CSS and our dark mode +- Provides beautiful default styling matching our design system +- Supports promise tracking (show loading → success/error automatically) +- Handles stacking, positioning, and auto-dismiss elegantly +- Only ~10KB gzipped +- Fully accessible (ARIA attributes, keyboard navigation) + +### Architecture: Toast as Global Service + +``` +frontend/src/ + ├── main.tsx + │ └── Wrap with provider + │ + ├── lib/ + │ └── toast.ts (re-export sonner's toast with custom defaults) + │ + ├── components/ + │ ├── library/TreeLibraryPage.tsx (add toast.success on delete) + │ ├── library/FolderEditModal.tsx (add toast.success on save) + │ ├── tree-editor/TreeEditorPage.tsx (replace error banner with toast) + │ ├── session/SessionDetailPage.tsx (add toast on export) + │ └── ...other components using toast + │ + └── api/ + └── client.ts (optional: global error toast interceptor) +``` + +**Key Design Decisions:** + +1. **Single import point**: `import { toast } from '@/lib/toast'` everywhere +2. **Custom defaults**: Pre-configured duration, position, dark mode sync +3. **Promise pattern**: Use `toast.promise()` for async operations +4. **Consistent vocabulary**: "Saved", "Deleted", "Exported" (not "Success!") +5. **Error details**: Show action + reason ("Failed to delete tree: Network error") + +--- + +## Implementation Plan + +### Phase 1: Install and Configure Sonner + +**1.1 Install dependencies** +```bash +cd patherly/frontend +npm install sonner +``` + +**1.2 Create toast utility wrapper** (`frontend/src/lib/toast.ts`): +```typescript +import { toast as sonnerToast } from 'sonner'; + +// Re-export with custom defaults +export const toast = { + success: (message: string, options?: any) => + sonnerToast.success(message, { duration: 4000, ...options }), + + error: (message: string, options?: any) => + sonnerToast.error(message, { duration: 6000, ...options }), + + info: (message: string, options?: any) => + sonnerToast.info(message, { duration: 4000, ...options }), + + loading: (message: string, options?: any) => + sonnerToast.loading(message, { ...options }), + + promise: sonnerToast.promise, + dismiss: sonnerToast.dismiss +}; +``` + +**1.3 Add Toaster provider** to `frontend/src/main.tsx`: +```typescript +import { Toaster } from 'sonner'; + +// Inside root render, wrap App: + + + + +``` + +**1.4 Sync theme with Toaster**: Update `themeStore.ts` to re-render Toaster on theme change + +--- + +### Phase 2: Add Notifications to Core Actions + +**Success notifications to add:** + +| Component | Action | Toast Message | Type | +|-----------|--------|---------------|------| +| TreeLibraryPage | Delete tree | "Tree deleted" | success | +| FolderEditModal | Save folder | "Folder saved" | success | +| FolderSidebar | Delete folder | "Folder deleted" | success | +| AddToFolderMenu | Add tree to folder | "Added to {folderName}" | success | +| AddToFolderMenu | Remove from folder | "Removed from {folderName}" | success | +| TreeEditorPage | Save tree | "Tree saved" | success | +| TreeEditorPage | Publish tree | "Tree published" | success | +| SessionDetailPage | Export session | "Session exported" | success | +| SettingsPage | Save preferences | "Settings saved" | success | +| AccountSettingsPage | Update account | "Account updated" | success | + +**2.1 Tree Library Actions** ([TreeLibraryPage.tsx](patherly/frontend/src/pages/TreeLibraryPage.tsx)): +- Remove confirmation dialog result logging +- Add `toast.success('Tree deleted')` after successful delete +- Replace inline error state with `toast.error(error)` + +**2.2 Folder Management** ([FolderEditModal.tsx](patherly/frontend/src/components/library/FolderEditModal.tsx), [FolderSidebar.tsx](patherly/frontend/src/components/library/FolderSidebar.tsx)): +- Add success toast on folder create/update/delete +- Remove inline error messages (use toast.error instead) +- Keep loading state but add toast feedback on completion + +**2.3 Tree Editor** ([TreeEditorPage.tsx](patherly/frontend/src/pages/TreeEditorPage.tsx)): +- Replace current error banner with toast notifications +- Add `toast.promise()` for autosave operations: + ```typescript + toast.promise(saveTree(), { + loading: 'Saving tree...', + success: 'Tree saved', + error: 'Failed to save tree' + }); + ``` +- Remove `saveError` state and error banner div + +**2.4 Session Export** ([SessionDetailPage.tsx](patherly/frontend/src/pages/SessionDetailPage.tsx)): +- Add toast on successful export: `toast.success('Session exported')` +- Add toast on copy to clipboard: `toast.success('Copied to clipboard')` +- Replace current inline copy feedback with toast + +**2.5 Settings Pages**: +- Add success toasts on settings save +- Remove inline success messages if any exist + +--- + +### Phase 3: Standardize Error Handling + +**3.1 Global API Error Interceptor** ([client.ts](patherly/frontend/src/api/client.ts)): +```typescript +apiClient.interceptors.response.use( + response => response, + error => { + // Show toast for non-form errors (4xx/5xx) + const message = error.response?.data?.detail || 'An error occurred'; + + // Don't toast validation errors (handled inline) + if (error.response?.status !== 422) { + toast.error(message); + } + + return Promise.reject(error); + } +); +``` + +**3.2 Remove redundant error handling**: +- Keep form validation errors inline (modal errors) +- Use toast for unexpected/network errors +- Remove page-level error banner states where replaced by toast + +--- + +### Phase 4: Add Copy Feedback Consistency + +**4.1 Standardize clipboard operations**: +All "Copy to Clipboard" buttons should: +1. Use `navigator.clipboard.writeText()` +2. Show toast on success: `toast.success('Copied to clipboard')` +3. Show toast on error: `toast.error('Failed to copy')` +4. Remove button state toggle (icon + text change) + +**Components to update:** +- [SessionDetailPage.tsx](patherly/frontend/src/pages/SessionDetailPage.tsx) (export copy button) +- [ExportPreviewModal.tsx](patherly/frontend/src/components/session/ExportPreviewModal.tsx) (copy button) +- [AddToFolderMenu.tsx](patherly/frontend/src/components/library/AddToFolderMenu.tsx) (if any copy functionality) + +--- + +## Critical Files to Modify + +| File Path | Changes | +|-----------|---------| +| `patherly/frontend/package.json` | Add `sonner` dependency | +| `patherly/frontend/src/main.tsx` | Add `` provider component | +| `patherly/frontend/src/lib/toast.ts` | **NEW FILE** - Toast utility wrapper | +| `patherly/frontend/src/store/themeStore.ts` | Sync theme changes to Toaster | +| `patherly/frontend/src/api/client.ts` | Add global error interceptor | +| `patherly/frontend/src/pages/TreeLibraryPage.tsx` | Add success/error toasts for delete | +| `patherly/frontend/src/pages/TreeEditorPage.tsx` | Replace error banner with toast.promise() | +| `patherly/frontend/src/pages/SessionDetailPage.tsx` | Add export/copy success toasts | +| `patherly/frontend/src/pages/SettingsPage.tsx` | Add settings save toast | +| `patherly/frontend/src/pages/AccountSettingsPage.tsx` | Add account update toast | +| `patherly/frontend/src/components/library/FolderEditModal.tsx` | Add folder save toast, remove inline errors | +| `patherly/frontend/src/components/library/FolderSidebar.tsx` | Add delete folder toast | +| `patherly/frontend/src/components/library/AddToFolderMenu.tsx` | Add add/remove from folder toasts | +| `patherly/frontend/src/components/session/ExportPreviewModal.tsx` | Standardize copy feedback | + +--- + +## Design Patterns and Best Practices + +### When to Use Each Toast Type + +**Success (green, 4s duration)**: +- Action completed successfully +- Examples: "Tree saved", "Folder deleted", "Exported" +- Only show for user-initiated actions, not automatic operations + +**Error (red, 6s duration)**: +- Action failed unexpectedly +- Examples: "Failed to save tree: Network error", "Could not delete folder" +- Include reason when available from API response + +**Info (blue, 4s duration)**: +- Neutral information +- Examples: "Session resumed", "Autosave enabled" +- Use sparingly - don't spam user with info toasts + +**Loading (infinite duration until dismissed)**: +- Long-running operations (>500ms expected) +- Examples: "Saving tree...", "Exporting session..." +- Use `toast.promise()` to automatically transition to success/error + +### Promise Pattern Best Practice + +For async operations with loading states: +```typescript +// DON'T do this (manual dismiss logic): +const toastId = toast.loading('Saving...'); +try { + await saveTree(); + toast.success('Saved', { id: toastId }); +} catch (error) { + toast.error('Failed', { id: toastId }); +} + +// DO this (automatic state transitions): +toast.promise(saveTree(), { + loading: 'Saving tree...', + success: 'Tree saved', + error: (err) => `Failed to save: ${err.message}` +}); +``` + +### Error Message Format + +Always provide context: +- ❌ "Error" (too vague) +- ❌ "Failed" (what failed?) +- ✅ "Failed to delete tree: Permission denied" +- ✅ "Could not load folders: Network error" + +### When NOT to Use Toasts + +Keep inline error messages for: +1. **Form validation errors**: Show next to invalid field +2. **Modal-contained errors**: Errors within a modal workflow +3. **Real-time feedback**: Input validation as user types +4. **Critical blocking errors**: Full-page error states + +--- + +## Verification Plan + +### Manual Testing Checklist + +**Tree Operations:** +- [ ] Create new tree → "Tree saved" toast appears +- [ ] Edit existing tree → "Tree saved" toast appears +- [ ] Delete tree → Confirm dialog → "Tree deleted" toast appears +- [ ] Delete fails (network off) → "Failed to delete tree" toast with reason +- [ ] Autosave triggers → "Tree saved" toast (via promise pattern) + +**Folder Operations:** +- [ ] Create folder → "Folder saved" toast appears +- [ ] Edit folder name → "Folder saved" toast appears +- [ ] Delete folder → "Folder deleted" toast appears +- [ ] Add tree to folder → "Added to [folder name]" toast appears +- [ ] Remove tree from folder → "Removed from [folder name]" toast appears + +**Session Operations:** +- [ ] Export session (any format) → "Session exported" toast appears +- [ ] Copy to clipboard → "Copied to clipboard" toast appears +- [ ] Copy fails (permissions) → "Failed to copy" toast appears +- [ ] Download session → "Session exported" toast appears + +**Settings:** +- [ ] Save user preferences → "Settings saved" toast appears +- [ ] Update account details → "Account updated" toast appears + +**Error Scenarios:** +- [ ] Network offline → API errors show toast with meaningful message +- [ ] Permission denied → Toast shows "Permission denied" reason +- [ ] 500 server error → Toast shows generic "Server error" message +- [ ] 422 validation error → NO toast (inline validation only) + +**Dark Mode:** +- [ ] Switch to dark mode → Toast background is dark themed +- [ ] Switch to light mode → Toast background is light themed +- [ ] Toast colors match theme (success green, error red, etc.) + +**Accessibility:** +- [ ] Screen reader announces toast messages (ARIA live region) +- [ ] Toasts can be dismissed with keyboard (close button focusable) +- [ ] Toast position doesn't obscure critical UI (top-right safe zone) +- [ ] Color contrast meets WCAG AA standards + +**Stacking Behavior:** +- [ ] Multiple toasts stack vertically without overlap +- [ ] Oldest toasts auto-dismiss while newer ones remain +- [ ] Maximum 3-4 toasts visible at once (sonner default) + +--- + +## Rollout Strategy + +### Phase 1: Core Infrastructure (Day 1, Morning) +1. Install sonner package +2. Create toast utility wrapper (`lib/toast.ts`) +3. Add Toaster provider to `main.tsx` +4. Sync with theme store +5. Test basic toast functionality in dev + +### Phase 2: High-Impact Actions (Day 1, Afternoon) +1. Tree save/delete operations +2. Folder CRUD operations +3. Session export/copy +4. Remove old error banners + +### Phase 3: Error Standardization (Day 2, Morning) +1. Add global API error interceptor +2. Clean up redundant error handling +3. Test error scenarios (network offline, permissions, etc.) + +### Phase 4: Refinement (Day 2, Afternoon) +1. Verify all toast messages use consistent vocabulary +2. Check dark mode appearance +3. Test accessibility with screen reader +4. Performance check (ensure no memory leaks from unmounted toasts) +5. Update user documentation if needed + +--- + +## Future Enhancements (Out of Scope) + +These can be added later if needed: +- **Undo actions**: Toast with "Undo" button for reversible operations +- **Progress toasts**: Show percentage for uploads/exports +- **Grouped toasts**: Collapse multiple similar actions ("3 trees deleted") +- **Persistent notifications**: Critical alerts that don't auto-dismiss +- **Sound effects**: Subtle audio feedback (accessibility feature) +- **Action buttons in toasts**: "View details" or "Retry" buttons + +--- + +## Risks and Mitigation + +| Risk | Impact | Mitigation | +|------|--------|------------| +| **Toast spam** | Users annoyed by too many notifications | Only toast user-initiated actions, not automatic events | +| **Theme sync issues** | Toast theme doesn't match app theme | Subscribe to themeStore changes, update Toaster theme prop | +| **Mobile viewport** | Toasts obscure content on small screens | Use `top-right` position (least obtrusive), verify on mobile | +| **Bundle size increase** | +10KB to frontend bundle | Acceptable for significant UX improvement; sonner is already optimized | +| **Breaking existing error handling** | Some errors go unnoticed | Keep inline validation errors; only replace page-level banners | + +--- + +## Success Metrics + +**Qualitative:** +- Users report feeling more confident about action completion +- Support tickets decrease for "Did my changes save?" questions +- User feedback mentions improved responsiveness + +**Quantitative:** +- 0 console errors related to toast implementation +- Toast render time <50ms (measured in React DevTools) +- No accessibility violations in Lighthouse audit +- Frontend bundle size increase <15KB gzipped + +--- + +## References + +- **Sonner Documentation**: https://sonner.emilkowal.ski/ +- **shadcn/ui Toast Component**: https://ui.shadcn.com/docs/components/sonner +- **UX Research on Feedback Timing**: Nielsen Norman Group - "Response Times: The 3 Important Limits" +- **Accessibility Guidelines**: WCAG 2.1 Success Criterion 4.1.3 (Status Messages) +