FactuGES_ERP/docs/architecture/http-error-handling-frontend.md

362 lines
6.5 KiB
Markdown
Raw Normal View History

# 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
```