# Tratamiento de errores HTTP en Frontend ERP ## 1. Objetivo Cualquier error que cruce `IDataSource` desde el frontend ERP queda normalizado como `ApiClientError`. Esto evita que los módulos consumidores dependan de `AxiosError` o inspeccionen `response.data` directamente. ## 2. Flujo ```txt Axios → interceptor → IDataSource → normalizeAxiosError → ApiClientError → TanStack Query → controller → UI ``` ## 3. Responsabilidades ```txt setupInterceptors → auth, headers y refresh normalizeAxiosError → normalización HTTP/transporte ApiClientError → contrato transversal TanStack Query → transporte del error al consumidor Controller/Page Controller → decisión UX UI → render ``` ## 4. Clasificación | Caso | kind | | ---- | ---- | | 400 | `badRequest` | | 401 | `unauthorized` | | 403 | `forbidden` | | 404 | `notFound` | | 409 | `conflict` | | 422 | `validation` | | 429 | `rateLimited` | | 500-599 | `server` / `unavailable` | | red | `network` | | timeout | `timeout` | | cancelación | `cancelled` | | resto | `unknown` | ## 5. Problem Details El frontend soporta Problem Details parciales y completos. Campos reconocidos: ```txt type title status detail instance method correlationId code source errors ``` `errors` puede estar ausente, vacío o contener elementos parciales válidos. ## 6. Mensajes ```txt ApiClientError.message → diagnóstico técnico estable getUserErrorMessage → mensaje seguro genérico controller/page controller → mensaje específico del caso de uso ``` `ApiClientError.message` no debe tratarse como texto UX por defecto. ## 7. Reglas para nuevos módulos ```txt No comprobar AxiosError en controllers. No leer error.response.data en UI. No normalizar errores dentro de hooks. No mostrar problem.detail automáticamente. No crear clases de error HTTP por módulo. No usar error.message como mensaje UX por defecto. ``` ## 8. Ejemplo de consumo ```ts if (isApiClientError(error) && error.kind === "notFound") { // El controller decide el estado específico del recurso. } ``` ## 9. Niveles de error La presentación distingue cuatro niveles: ```txt 1. Error de validación/formulario 2. Error inline o de mutation 3. Error bloqueante de página 4. Error inesperado de routing/render ``` Regla crítica: ```txt PageErrorState no conoce Axios no conoce TanStack Query no conoce React Router no conoce ErrorBoundary ``` ## 10. Decisiones no adoptadas En esta fase no se introduce: ```txt ErrorProvider global toasts globales automáticos framework de errores no HTTP registro dinámico de errores ``` ## 11. Estados visuales bloqueantes Para errores que impiden renderizar una página completa, la UI debe usar un view model explícito y un componente dedicado de página. ```txt ApiClientError → builder específico del caso de uso → PageErrorViewModel → Page Controller → PageErrorState ``` Reglas: ```txt ErrorAlert → error inline y no bloqueante PageErrorState → error que impide renderizar la página componentes locales → errores embebidos en una subárea, como PDFViewer ``` `NotFoundCard` queda retirado. Su sustitución es: ```txt 404 real → PageErrorState variant="notFound" success sin entidad válida → PageErrorState variant="unknown" colección vacía → empty state ``` ## 12. Listados paginados En listados con `placeholderData`, el estado visual debe distinguir entre fallo bloqueante y error de refetch: ```txt error inicial + sin data utilizable → PageErrorState error durante refetch + data previa utilizable → mantener tabla → ErrorAlert inline ``` Reglas aplicadas en proformas, customers e issued-invoices: ```txt cancelled → sin feedback visual notFound en listados → no se interpreta como empty → se trata como error remoto anómalo → mapeado a unknown en esta fase ``` ## 13. Detalle y edición de recursos En pantallas de detalle o edición de una entidad: ```txt ApiClientError.kind === notFound → ausencia real del recurso → PageErrorState específico de notFound respuesta exitosa sin entidad válida → inconsistencia del contrato → variant unknown cancelled → no renderiza pantalla bloqueante ``` La decisión no debe derivarse de `!entity` a secas. El page controller debe distinguir entre: ```txt error HTTP real inconsistencia de datos cancelación ``` El patrón se aplica ya en: ```txt proformas/detail proformas/list proformas/update customers/list customers/view customers/update issued-invoices/list ``` ## 14. Queries HTTP Para queries normales, el flujo estable es: ```txt ApiClientError → builder específico del caso de uso → PageErrorState / ErrorAlert ``` Reglas: ```txt error inicial sin datos → PageErrorState error de refetch con datos previos → conservar contenido → ErrorAlert inline mutation error → ErrorAlert o feedback local cancelled → sin pantalla de error ``` En `issued-invoices` solo existe actualmente el listado principal y la descarga de PDF. No hay detalle, create ni update cableados en web; por eso la migración de esta fase cubre: ```txt issued-invoices/list → PageErrorState para fallo bloqueante → ErrorAlert para refetch con datos previos issued-invoices/download-pdf → feedback local mediante toast → no promueve el error a pantalla completa ``` ## 15. Routing Los errores del router no se interpretan como `ApiClientError`. Flujo: ```txt Route error → clasificador de app → RouteErrorPage → PageErrorState ``` Cobertura actual: ```txt 404 403 500 503 unknown ``` La clasificación vive en `apps/web` y usa `errorElement` en las ramas del router que montan shell autenticado, fullscreen y auth. Las rutas placeholder antiguas (`dashboard`, `settings`, `catalog`, `quotes`) pasan a reutilizar la misma base visual de `PageErrorState`. ## 16. Render inesperado Los errores inesperados de render de React quedan separados de Query y Routing. Flujo: ```txt React render error → ErrorBoundary → RenderErrorFallback → PageErrorState ``` Reglas: ```txt resetErrorBoundary() → retry real del boundary Ir al inicio → acción explícita no stack no error.message no problem.detail ``` ## 17. Legacy Estado final de piezas legacy relacionadas: ```txt ErrorPage → eliminado y sustituido por RouteErrorPage / StaticRouteErrorPage error-fallbacks → eliminado ErrorOverlay → eliminado por ausencia de consumidores NotFoundCard → eliminado globalmente ErrorState → se mantiene como compatibilidad sobre PageErrorState ErrorAlert → se mantiene para feedback inline no destructivo ```