# Streamlined Error & Load State Handling - Quick Reference ## 🎯 Goal Standardize error and loading state handling across all GridPilot website pages using user-friendly, accessible, and consistent shared components. ## 📁 File Structure ``` apps/website/components/shared/ ├── state/ │ ├── LoadingWrapper.tsx # Loading states │ ├── ErrorDisplay.tsx # Error states │ ├── EmptyState.tsx # Empty states │ └── StateContainer.tsx # Combined wrapper ├── hooks/ │ └── useDataFetching.ts # Unified data fetching └── types/ └── state.types.ts # TypeScript interfaces ``` ## 🚀 Quick Start Examples ### 1. Basic Page Implementation ```typescript import { useDataFetching } from '@/components/shared/hooks/useDataFetching'; import { LoadingWrapper } from '@/components/shared/state/LoadingWrapper'; import { ErrorDisplay } from '@/components/shared/state/ErrorDisplay'; import { EmptyState } from '@/components/shared/state/EmptyState'; function MyPage() { const { data, isLoading, error, retry } = useDataFetching({ queryKey: ['myData'], queryFn: () => myService.getData(), }); if (isLoading) return ; if (error) return ; if (!data) return ; return ; } ``` ### 2. Using StateContainer (Recommended) ```typescript import { StateContainer } from '@/components/shared/state/StateContainer'; function MyPage() { const { data, isLoading, error, retry } = useDataFetching({ queryKey: ['myData'], queryFn: () => myService.getData(), }); return ( {(content) => } ); } ``` ## 🎨 Component Variants ### LoadingWrapper - `spinner` - Traditional spinner (default) - `skeleton` - Skeleton screens - `full-screen` - Centered in viewport - `inline` - Compact inline - `card` - Card placeholders ### ErrorDisplay - `full-screen` - Full page error - `inline` - Inline error message - `card` - Error card - `toast` - Toast notification ### EmptyState - `default` - Standard empty state - `minimal` - Simple version - `full-page` - Full page empty state ## 🔧 useDataFetching Hook ```typescript const { data, // The fetched data isLoading, // Initial load isFetching, // Any fetch (including refetch) error, // ApiError or null retry, // Retry failed request refetch, // Manual refetch lastUpdated, // Timestamp isStale // Cache status } = useDataFetching({ queryKey: ['uniqueKey', id], queryFn: () => apiService.getData(id), enabled: true, // Enable/disable query retryOnMount: true, // Auto-retry on mount cacheTime: 5 * 60 * 1000, // 5 minutes staleTime: 1 * 60 * 1000, // 1 minute maxRetries: 3, retryDelay: 1000, onSuccess: (data) => { /* ... */ }, onError: (error) => { /* ... */ }, }); ``` ## 📋 Migration Checklist ### Phase 1: Foundation - [ ] Create `components/shared/state/` directory - [ ] Implement `LoadingWrapper.tsx` - [ ] Implement `ErrorDisplay.tsx` - [ ] Implement `EmptyState.tsx` - [ ] Implement `StateContainer.tsx` - [ ] Implement `useDataFetching.ts` hook - [ ] Create TypeScript interfaces - [ ] Add comprehensive tests ### Phase 2: Core Pages (High Priority) - [ ] `app/dashboard/page.tsx` - [ ] `app/leagues/[id]/LeagueDetailInteractive.tsx` - [ ] `app/races/[id]/RaceDetailInteractive.tsx` ### Phase 3: Additional Pages - [ ] `app/teams/[id]/TeamDetailInteractive.tsx` - [ ] `app/leagues/[id]/schedule/page.tsx` - [ ] `app/races/[id]/results/RaceResultsInteractive.tsx` - [ ] `app/races/[id]/stewarding/RaceStewardingInteractive.tsx` - [ ] Sponsor pages - [ ] Profile pages ### Phase 4: Cleanup - [ ] Remove old loading components - [ ] Remove old error components - [ ] Update documentation - [ ] Team training ## 🔄 Before & After ### Before (Inconsistent) ```typescript function DashboardPage() { const { data, isLoading, error } = useDashboardOverview(); if (isLoading) { return (
Loading dashboard...
); } if (error || !dashboardData) { return (
Failed to load dashboard
); } return ; } ``` ### After (Standardized) ```typescript function DashboardPage() { const { data, isLoading, error, retry } = useDataFetching({ queryKey: ['dashboardOverview'], queryFn: () => dashboardService.getDashboardOverview(), }); return ( {(content) => } ); } ``` ## 🎯 Key Benefits 1. **Consistency**: Same patterns across all pages 2. **User-Friendly**: Clear messages and helpful actions 3. **Accessible**: ARIA labels, keyboard navigation 4. **Developer-Friendly**: Simple API, less boilerplate 5. **Maintainable**: Single source of truth 6. **Flexible**: Multiple variants for different needs 7. **Type-Safe**: Full TypeScript support ## 📊 Success Metrics - ✅ 100% page coverage - ✅ 60% less error handling code - ✅ Consistent UX across app - ✅ Better error recovery - ✅ Faster development ## 🔗 Related Files - Design Document: `STREAMLINED_STATE_HANDLING_DESIGN.md` - TypeScript Interfaces: `components/shared/state/types.ts` - Component Tests: `components/shared/state/*.test.tsx` - Hook Tests: `components/shared/hooks/useDataFetching.test.ts` ## 💡 Tips 1. **Always use `useDataFetching`** instead of manual state management 2. **Prefer `StateContainer`** for complex pages 3. **Use `skeleton` variant** for better perceived performance 4. **Enable `retryOnMount`** for recoverable errors 5. **Customize config** per page needs 6. **Test all states**: loading, error, empty, success ## 🆘 Troubleshooting **Q: Error not showing?** A: Check if error is instance of `ApiError` **Q: Loading not visible?** A: Verify `isLoading` is true and component is rendered **Q: Retry not working?** A: Ensure `onRetry` prop is passed to ErrorDisplay **Q: Empty state not showing?** A: Check if data is null/undefined and `showEmpty` is true --- For detailed implementation guide, see: `STREAMLINED_STATE_HANDLING_DESIGN.md`