FactuGES_ERP/docs/architecture/http-error-handling-frontend.md
david 26e2ee59bd feat: add error handling and feedback state management for customer and proforma updates
- Implemented feedback state resolution for proforma list and update pages.
- Created error view models for proforma and customer updates, including handling for various error types.
- Added tests for error handling in proforma and customer list updates.
- Introduced utility functions for building error view models and resolving feedback states.
- Ensured translation support for error messages in Spanish.
- Removed duplicate error alert components in customer module.
2026-08-06 13:03:48 +02:00

6.5 KiB

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

Axios
→ interceptor
→ IDataSource
→ normalizeAxiosError
→ ApiClientError
→ TanStack Query
→ controller
→ UI

3. Responsabilidades

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:

type
title
status
detail
instance
method
correlationId
code
source
errors

errors puede estar ausente, vacío o contener elementos parciales válidos.

6. Mensajes

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

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

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:

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:

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:

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.

ApiClientError
→ builder específico del caso de uso
→ PageErrorViewModel
→ Page Controller
→ PageErrorState

Reglas:

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:

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:

error inicial + sin data utilizable
  → PageErrorState

error durante refetch + data previa utilizable
  → mantener tabla
  → ErrorAlert inline

Reglas aplicadas en proformas, customers e issued-invoices:

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:

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:

error HTTP real
inconsistencia de datos
cancelación

El patrón se aplica ya en:

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:

ApiClientError
→ builder específico del caso de uso
→ PageErrorState / ErrorAlert

Reglas:

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:

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:

Route error
→ clasificador de app
→ RouteErrorPage
→ PageErrorState

Cobertura actual:

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:

React render error
→ ErrorBoundary
→ RenderErrorFallback
→ PageErrorState

Reglas:

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:

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