# Errors Store The errors store provides comprehensive error handling and management for the entire application. It centralizes error tracking, automatic retry logic, and integration with the notification system to provide users with consistent error feedback. ## Overview - **Location**: `src/stores/errors.js` - **Type**: Pinia Store - **Purpose**: Centralized error state management and handling - **Integration**: Works with notification store for user feedback ## Installation & Setup ```javascript // Import in your component import { useErrorStore } from "@/stores/errors"; // Use in component const errorStore = useErrorStore(); ``` ## State Structure ### Core State Properties ```javascript state: { hasError: false, // Global error flag lastError: null, // Most recent global error apiErrors: new Map(), // API-specific errors by key componentErrors: { // Component-specific errors dataTable: null, form: null, clients: null, jobs: null, timesheets: null, warranties: null, routes: null }, errorHistory: [], // Historical error log maxHistorySize: 50, // Maximum history entries autoNotifyErrors: true // Auto-show notifications for errors } ``` ### Error Object Structure ```javascript { message: 'Error description', // Human-readable error message type: 'api_error', // Error classification timestamp: '2025-11-12T10:30:00Z', // When error occurred name: 'ValidationError', // Error name (if available) stack: 'Error stack trace...', // Stack trace (if available) status: 404, // HTTP status (for API errors) statusText: 'Not Found', // HTTP status text data: {...} // Additional error data } ``` ## Getters ### `hasAnyError` Check if there are any errors in the application. ```javascript const hasErrors = errorStore.hasAnyError; ``` ### `getComponentError(componentName)` Get error for a specific component. ```javascript const formError = errorStore.getComponentError("form"); ``` ### `getApiError(apiKey)` Get error for a specific API operation. ```javascript const loginError = errorStore.getApiError("user-login"); ``` ### `getRecentErrors(limit)` Get recent errors from history. ```javascript const recentErrors = errorStore.getRecentErrors(10); ``` ## Actions ### Global Error Management #### `setGlobalError(error, showNotification)` Set a global application error. ```javascript errorStore.setGlobalError( new Error("Critical system failure"), true, // Show notification ); // With custom error object errorStore.setGlobalError({ message: "Database connection lost", type: "connection_error", recoverable: true, }); ``` #### `clearGlobalError()` Clear the global error state. ```javascript errorStore.clearGlobalError(); ``` ### Component-Specific Error Management #### `setComponentError(componentName, error, showNotification)` Set an error for a specific component. ```javascript // Set error for form component errorStore.setComponentError("form", new Error("Validation failed"), true); // Clear error (pass null) errorStore.setComponentError("form", null); ``` #### `clearComponentError(componentName)` Clear error for a specific component. ```javascript errorStore.clearComponentError("form"); ``` ### API Error Management #### `setApiError(apiKey, error, showNotification)` Set an error for a specific API operation. ```javascript // Set API error errorStore.setApiError("user-login", apiError, true); // Clear API error (pass null) errorStore.setApiError("user-login", null); ``` #### `clearApiError(apiKey)` Clear error for a specific API operation. ```javascript errorStore.clearApiError("user-login"); ``` ### Bulk Operations #### `clearAllErrors()` Clear all errors from the store. ```javascript errorStore.clearAllErrors(); ``` ### Advanced Error Handling #### `handleApiCall(apiKey, apiFunction, options)` Handle an API call with automatic error management and retry logic. ```javascript const result = await errorStore.handleApiCall( "fetch-users", async () => { return await api.getUsers(); }, { showNotification: true, retryCount: 2, retryDelay: 1000, onSuccess: (result) => console.log("Success:", result), onError: (error) => console.log("Failed:", error), }, ); ``` #### `withErrorHandling(operationKey, asyncOperation, options)` Wrap an async operation with comprehensive error handling. ```javascript const result = await errorStore.withErrorHandling( "save-data", async () => { return await saveUserData(); }, { componentName: "userForm", showNotification: true, rethrow: false, // Don't re-throw errors }, ); ``` ## Error Types The store automatically categorizes errors into different types: ### `string_error` Simple string errors. ```javascript errorStore.setGlobalError("Something went wrong"); ``` ### `javascript_error` Standard JavaScript Error objects. ```javascript errorStore.setGlobalError(new Error("Validation failed")); ``` ### `api_error` HTTP/API response errors with status codes. ```javascript // Automatically detected from axios-style error objects { message: 'Not Found', status: 404, statusText: 'Not Found', type: 'api_error', data: {...} } ``` ### `network_error` Network connectivity errors. ```javascript { message: 'Network error - please check your connection', type: 'network_error' } ``` ### `unknown_error` Unrecognized error formats. ```javascript { message: 'An unknown error occurred', type: 'unknown_error', originalError: {...} } ``` ## Configuration Methods ### `setAutoNotifyErrors(enabled)` Enable/disable automatic error notifications. ```javascript errorStore.setAutoNotifyErrors(false); // Disable auto-notifications ``` ### `setMaxHistorySize(size)` Set maximum number of errors to keep in history. ```javascript errorStore.setMaxHistorySize(100); ``` ## Usage Patterns ### Basic Error Handling ```javascript const errorStore = useErrorStore(); // Simple error setting try { await riskyOperation(); } catch (error) { errorStore.setGlobalError(error); } // Component-specific error try { await validateForm(); } catch (validationError) { errorStore.setComponentError("form", validationError); } ``` ### API Error Handling with Retry ```javascript // Automatic retry logic const userData = await errorStore.handleApiCall( "fetch-user-data", async () => { const response = await fetch("/api/users"); if (!response.ok) throw new Error("Failed to fetch users"); return response.json(); }, { retryCount: 3, retryDelay: 1000, showNotification: true, }, ); ``` ### Comprehensive Operation Wrapping ```javascript // Wrap complex operations const result = await errorStore.withErrorHandling( "complex-workflow", async () => { // Step 1: Validate data await validateInputData(); // Step 2: Save to database const saveResult = await saveToDatabase(); // Step 3: Send notification email await sendNotificationEmail(); return saveResult; }, { componentName: "workflow", showNotification: true, rethrow: false, }, ); if (result) { // Success - result contains the return value console.log("Workflow completed:", result); } else { // Error occurred - check component error for details const workflowError = errorStore.getComponentError("workflow"); console.log("Workflow failed:", workflowError?.message); } ``` ### Integration with Vue Components ```javascript // In a Vue component import { useErrorStore } from "@/stores/errors"; import { useNotificationStore } from "@/stores/notifications"; export default { setup() { const errorStore = useErrorStore(); const notificationStore = useNotificationStore(); const submitForm = async (formData) => { // Clear any previous errors errorStore.clearComponentError("form"); try { await errorStore.withErrorHandling( "form-submit", async () => { const result = await api.submitForm(formData); notificationStore.addSuccess("Form submitted successfully!"); return result; }, { componentName: "form", showNotification: true, }, ); // Reset form on success resetForm(); } catch (error) { // Error handling is automatic, but you can add custom logic here console.log("Form submission failed"); } }; return { submitForm, formError: computed(() => errorStore.getComponentError("form")), }; }, }; ``` ## Integration with Enhanced API The errors store works seamlessly with the enhanced API wrapper: ```javascript import { ApiWithErrorHandling } from "@/api-enhanced"; // The enhanced API automatically uses the error store try { const clients = await ApiWithErrorHandling.getPaginatedClientDetails( pagination, filters, [], { componentName: "clients", retryCount: 2, showErrorNotifications: true, }, ); } catch (error) { // Error is automatically handled by the error store // Check component error for details const clientError = errorStore.getComponentError("clients"); } ``` ## Error Recovery Patterns ### Graceful Degradation ```javascript const loadCriticalData = async () => { let primaryData = null; let fallbackData = null; // Try primary data source try { primaryData = await errorStore.withErrorHandling( "primary-data", () => api.getPrimaryData(), { showNotification: false, rethrow: true }, ); } catch (error) { console.log("Primary data failed, trying fallback..."); // Try fallback data source try { fallbackData = await errorStore.withErrorHandling( "fallback-data", () => api.getFallbackData(), { showNotification: false, rethrow: true }, ); notificationStore.addWarning( "Using cached data due to connectivity issues", ); } catch (fallbackError) { errorStore.setGlobalError("Unable to load data from any source"); return null; } } return primaryData || fallbackData; }; ``` ### User-Driven Error Recovery ```javascript const handleApiError = async (operation) => { try { return await operation(); } catch (error) { // Show error with recovery options notificationStore.addNotification({ type: "error", title: "Operation Failed", message: "Would you like to try again or continue with cached data?", persistent: true, actions: [ { label: "Retry", variant: "primary", handler: () => handleApiError(operation), // Recursive retry }, { label: "Use Cached Data", variant: "secondary", handler: () => loadCachedData(), }, { label: "Cancel", variant: "secondary", }, ], }); throw error; // Let caller handle as needed } }; ``` ## Debugging & Monitoring ### Error History Access ```javascript // Get all error history for debugging const allErrors = errorStore.errorHistory; // Get recent errors with details const recent = errorStore.getRecentErrors(5); recent.forEach((error) => { console.log(`[${error.timestamp}] ${error.source}: ${error.message}`); }); // Filter errors by type const apiErrors = errorStore.errorHistory.filter((e) => e.type === "api_error"); ``` ### Error Reporting ```javascript // Send error reports to monitoring service const reportErrors = () => { const recentErrors = errorStore.getRecentErrors(10); recentErrors.forEach((error) => { if (error.type === "api_error" && error.status >= 500) { // Report server errors analyticsService.reportError({ message: error.message, url: window.location.href, userAgent: navigator.userAgent, timestamp: error.timestamp, stack: error.stack, }); } }); }; ``` ## Testing ### Unit Testing ```javascript import { setActivePinia, createPinia } from "pinia"; import { useErrorStore } from "@/stores/errors"; describe("Errors Store", () => { beforeEach(() => { setActivePinia(createPinia()); }); it("sets and clears global errors", () => { const store = useErrorStore(); store.setGlobalError("Test error"); expect(store.hasError).toBe(true); expect(store.lastError.message).toBe("Test error"); store.clearGlobalError(); expect(store.hasError).toBe(false); }); it("handles component errors", () => { const store = useErrorStore(); store.setComponentError("form", new Error("Validation failed")); expect(store.getComponentError("form").message).toBe("Validation failed"); store.clearComponentError("form"); expect(store.getComponentError("form")).toBeNull(); }); it("tracks error history", () => { const store = useErrorStore(); store.setGlobalError("Error 1"); store.setGlobalError("Error 2"); expect(store.errorHistory).toHaveLength(2); expect(store.getRecentErrors(1)[0].message).toBe("Error 2"); }); }); ``` ### Integration Testing ```javascript // Test error handling with API calls it("handles API errors correctly", async () => { const store = useErrorStore(); const mockApi = jest.fn().mockRejectedValue(new Error("API Error")); const result = await store.withErrorHandling("test-api", mockApi, { componentName: "test", showNotification: false, rethrow: false, }); expect(result).toBeNull(); expect(store.getComponentError("test").message).toBe("API Error"); }); ``` ## Best Practices ### Do's - ✅ Use component-specific errors for UI validation - ✅ Use API-specific errors for network operations - ✅ Enable auto-notifications for user-facing errors - ✅ Use retry logic for transient failures - ✅ Clear errors when operations succeed - ✅ Keep error messages user-friendly and actionable ### Don'ts - ❌ Don't set global errors for minor validation issues - ❌ Don't ignore error context (component/API source) - ❌ Don't let error history grow indefinitely - ❌ Don't show technical stack traces to end users - ❌ Don't retry operations that will consistently fail ### Performance Considerations - Error history is automatically trimmed to prevent memory leaks - Use component-specific errors to isolate issues - Clear errors promptly when no longer relevant - Consider disabling auto-notifications for high-frequency operations ## Common Patterns ### Form Validation Errors ```javascript const validateAndSubmit = async (formData) => { errorStore.clearComponentError("form"); try { await errorStore.withErrorHandling( "form-validation", async () => { validateFormData(formData); await submitForm(formData); }, { componentName: "form", showNotification: true, }, ); } catch (error) { // Validation errors are now stored in component error // and automatically displayed to user } }; ``` ### Background Task Monitoring ```javascript const monitorBackgroundTask = async (taskId) => { await errorStore.handleApiCall( `task-${taskId}`, async () => { const status = await api.getTaskStatus(taskId); if (status.failed) { throw new Error(`Task failed: ${status.error}`); } return status; }, { retryCount: 5, retryDelay: 2000, showNotification: status.failed, // Only notify on final failure }, ); }; ```