Adds a new "procedural" tree type for linear step-by-step project workflows (domain controller setup, M365 onboarding, VPN config, etc). Includes intake form builder, two-panel step navigation, variable resolution, procedural exports, 3 seed templates, and UI rename from "Trees" to "Flows". Also archives 19 implemented plan docs and creates deferred features backlog. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
428 lines
16 KiB
Markdown
428 lines
16 KiB
Markdown
# 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 <App> with <Toaster /> 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:
|
|
<React.StrictMode>
|
|
<Toaster
|
|
position="top-right"
|
|
richColors
|
|
closeButton
|
|
theme={themeStore.theme === 'dark' ? 'dark' : 'light'}
|
|
/>
|
|
<App />
|
|
</React.StrictMode>
|
|
```
|
|
|
|
**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 `<Toaster>` 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)
|
|
|