import { AxiosError } from 'axios'; import type { ApiError } from '@/types/api'; /** * Helper de gestion d'erreurs API * Transforme les erreurs Axios brutes en objets ApiError standardisés * selon le format défini dans FRONTEND_INTEGRATION.md */ /** * Parse une erreur Axios en ApiError standardisé * @param error - Erreur Axios ou Error * @returns ApiError formaté selon le contrat backend */ export function parseApiError(error: unknown): ApiError { // Si c'est déjà une ApiError, la retourner telle quelle if (isApiError(error)) { return error; } // Si c'est une erreur Axios if (isAxiosError(error)) { const axiosError = error as AxiosError; // Si le backend retourne le format standardisé { success: false, error: {...} } if ( axiosError.response?.data && typeof axiosError.response.data === 'object' && 'success' in axiosError.response.data && axiosError.response.data.success === false && 'error' in axiosError.response.data ) { const backendError = (axiosError.response.data as { error: any }).error; return normalizeApiError(backendError); } // Si le backend retourne directement un objet error if ( axiosError.response?.data && typeof axiosError.response.data === 'object' && 'code' in axiosError.response.data && 'message' in axiosError.response.data ) { return normalizeApiError(axiosError.response.data); } // Erreur réseau (pas de réponse) if (axiosError.request && !axiosError.response) { return { code: 0, message: 'Network error: Unable to connect to server', timestamp: new Date().toISOString(), }; } // Erreur HTTP sans format standardisé return { code: axiosError.response?.status || 0, message: (axiosError.response?.data as any)?.message || axiosError.message || 'An unexpected error occurred', timestamp: new Date().toISOString(), }; } // Erreur JavaScript standard if (error instanceof Error) { return { code: 0, message: error.message || 'An unexpected error occurred', timestamp: new Date().toISOString(), }; } // Erreur inconnue return { code: 0, message: 'An unexpected error occurred', timestamp: new Date().toISOString(), }; } /** * Normalise un objet d'erreur backend en ApiError standardisé */ function normalizeApiError(error: any): ApiError { return { code: typeof error.code === 'number' ? error.code : parseInt(String(error.code || 0), 10), message: error.message || 'An error occurred', details: error.details || (Array.isArray(error.details) ? error.details : undefined), request_id: error.request_id, timestamp: error.timestamp || new Date().toISOString(), context: error.context, }; } /** * Formate un message d'erreur pour l'affichage dans l'UI * @param error - ApiError * @returns Message formaté pour l'utilisateur */ export function formatErrorMessage(error: ApiError): string { // Si l'erreur a des détails de validation, les inclure if (error.details && Array.isArray(error.details) && error.details.length > 0) { const detailsMessages = error.details .map((detail) => `${detail.field}: ${detail.message}`) .join(', '); return `${error.message} (${detailsMessages})`; } return error.message; } /** * Extrait les erreurs de validation par champ * @param error - ApiError * @returns Record avec les erreurs par champ (field -> message) */ export function getValidationErrors( error: ApiError, ): Record { if (!error.details || !Array.isArray(error.details)) { return {}; } const errors: Record = {}; for (const detail of error.details) { if (detail.field && detail.message) { errors[detail.field] = detail.message; } } return errors; } /** * Vérifie si une erreur est une ApiError */ function isApiError(error: unknown): error is ApiError { return ( typeof error === 'object' && error !== null && 'code' in error && 'message' in error && typeof (error as any).code === 'number' && typeof (error as any).message === 'string' ); } /** * Vérifie si une erreur est une AxiosError */ function isAxiosError(error: unknown): error is AxiosError { return ( typeof error === 'object' && error !== null && 'isAxiosError' in error && (error as any).isAxiosError === true ); }