/** * Standardized Error Handling for Portal * Provides consistent error handling and user-friendly messages */ export interface ApiError { success: false; error: { code: string; message: string; details?: Record; }; timestamp: string; path: string; } export interface ApiErrorInfo { code: string; message: string; shouldLogout?: boolean; shouldRetry?: boolean; } /** * Extract error information from various error types */ export function getErrorInfo(error: unknown): ApiErrorInfo { // Handle API errors with structured format if (isApiError(error)) { return { code: error.error.code, message: error.error.message, shouldLogout: shouldLogoutForError(error.error.code), shouldRetry: shouldRetryForError(error.error.code), }; } // Handle fetch/network errors if (error instanceof Error) { if (error.name === 'TypeError' && error.message.includes('fetch')) { return { code: 'NETWORK_ERROR', message: 'Unable to connect to the server. Please check your internet connection and try again.', shouldRetry: true, }; } if (error.name === 'AbortError') { return { code: 'REQUEST_TIMEOUT', message: 'The request timed out. Please try again.', shouldRetry: true, }; } return { code: 'UNKNOWN_ERROR', message: 'An unexpected error occurred. Please try again.', shouldRetry: true, }; } // Fallback for unknown error types return { code: 'UNKNOWN_ERROR', message: 'An unexpected error occurred. Please try again.', shouldRetry: true, }; } export function isApiError(error: unknown): error is ApiError { return ( typeof error === 'object' && error !== null && 'success' in error && error.success === false && 'error' in error && typeof (error as any).error === 'object' && 'code' in (error as any).error && 'message' in (error as any).error ); } /** * Determine if the user should be logged out for this error */ function shouldLogoutForError(code: string): boolean { const logoutCodes = [ 'TOKEN_REVOKED', 'INVALID_REFRESH_TOKEN', 'UNAUTHORIZED', 'SESSION_EXPIRED', ]; return logoutCodes.includes(code); } /** * Determine if the request should be retried for this error */ function shouldRetryForError(code: string): boolean { const noRetryCodes = [ 'INVALID_CREDENTIALS', 'FORBIDDEN', 'ADMIN_REQUIRED', 'ACCOUNT_LOCKED', 'VALIDATION_ERROR', 'ACCOUNT_ALREADY_LINKED', 'ACCOUNT_EXISTS', 'CUSTOMER_NOT_FOUND', 'INVALID_REQUEST', ]; return !noRetryCodes.includes(code); } /** * Get a user-friendly error message for display in UI */ export function getUserFriendlyMessage(error: unknown): string { const errorInfo = getErrorInfo(error); return errorInfo.message; } /** * Handle authentication errors consistently */ export function handleAuthError(error: unknown, logout: () => void): void { const errorInfo = getErrorInfo(error); if (errorInfo.shouldLogout) { logout(); } } /** * Create a standardized error for logging */ export function createErrorLog(error: unknown, context: string): { context: string; code: string; message: string; timestamp: string; } { const errorInfo = getErrorInfo(error); return { context, code: errorInfo.code, message: errorInfo.message, timestamp: new Date().toISOString(), }; }