FactuGES_ERP/docs/frontend/ui-patterns.md

188 lines
7.9 KiB
Markdown
Raw Normal View History

# Patrones de UI
> **Propósito:** explicar cómo implementar pantallas concretas. Este documento, junto con [Arquitectura frontend](./frontend-architecture.md) y [UI system](./ui-system.md), forma la fuente de verdad (SSOT) de frontend/UI.
Los componentes aquí citados se definen en `ui-system.md`; este documento explica cómo componerlos.
## 1. Listados
```tsx
<RdxPageFrame>
<RdxPageContainer width="full">
<RdxPageContent>
<RdxPageHeader
title={title}
description={description}
actions={<RdxPageActions primary={primaryAction} />}
/>
{refreshError}
<DataTable topContent={toolbarAndFilters} {...tableProps} />
</RdxPageContent>
</RdxPageContainer>
</RdxPageFrame>
```
- El CTA de creación permanece durante loading, empty y errores de refresh; puede desaparecer ante error inicial bloqueante.
- Búsqueda, filtros y controles viven en `DataTable.topContent`.
- TanStack Query conserva los datos remotos y la paginación server-side.
- Búsqueda, filtros, sorting y paginación reflejan su estado relevante en `searchParams` y reinician página cuando corresponda.
- No se calculan contadores globales a partir de una página parcial.
- El click de fila recibe la fila; la página/controller usa explícitamente `row.id`.
- Una vista de composición de dominio puede encapsular el shell si permanece pura.
El controller de página concentra URL, filtros, consulta, estados y navegación. La Page define columnas y conecta callbacks; no reproduce reglas de dominio.
## 2. Create / Update
```tsx
<RdxPageFrame
header={<RdxEditorHeader />}
footer={<RdxEditorActionBar />}
>
<RdxPageContainer width="content">
<RdxPageContent>
{pageError ? <ErrorAlert /> : null}
<Editor />
</RdxPageContent>
</RdxPageContainer>
</RdxPageFrame>
```
Los errores inline no definen su propio spacing estructural. Participan del spacing estándar de RdxPageContent igual que el resto de hijos principales.
Footer y aside son opcionales. `content` es adecuado para formularios focalizados; `full` para editores documentales amplios. Create y Update deben compartir geometría cuando solo cambian defaults, submit o navegación.
Loading y error bloqueante usan también `RdxPageFrame` y `RdxPageContainer`; no reconstruyen el viewport.
## 3. Detail
Un detalle normal usa `RdxPageFrame`, `RdxEditorHeader` cuando es fullscreen y el container apropiado.
Un detalle documental especializado puede usar:
```text
RdxPageFrame content overflow-hidden
├── document pane → scroll interno
└── inspector → scroll interno
```
Solo se usa esta excepción cuando los panes necesitan permanecer visibles simultáneamente. No se crea un layout genérico de detalle si no existe repetición real.
## 4. Settings
Settings posee un layout funcional bajo un único frame:
```text
RdxPageFrame
└── SettingsLayout
├── header
├── navigation
└── Outlet
```
Las páginas hijas no añaden otro `RdxPageFrame` cuando el padre ya controla shell y scroll. El layout de Settings se conserva porque aporta navegación y composición funcional.
## 5. Dialogs
Usar `Dialog` para una tarea breve y focalizada que debe volver al contexto actual. Usar `AlertDialog` para confirmaciones, especialmente destructivas. Una edición prolongada o documental necesita una ruta propia.
Orden recomendado:
```text
DialogContent
├── DialogHeader: título y descripción
├── campo de identidad, si existe
├── secciones agrupadas por intención
└── DialogFooter: cancelar y enviar
```
- La cabecera y el footer se separan visualmente del formulario.
- Los bloques usan `section` y `aria-labelledby` cuando tienen título.
- Campos equivalentes pueden usar dos columnas desde `sm`; en móvil se apilan.
- El botón submit puede apuntar al formulario mediante `form` si el footer está fuera de `<form>`.
- Durante submit se deshabilitan controles y acciones de cierre que puedan interrumpir la operación.
- El controller posee defaults, validación, mutation, reset, feedback y navegación posterior.
- Todo texto visible usa i18n en `es` y `en`.
## 6. Acciones de fila
`RdxRowActions` recibe acciones ya resueltas:
- los listados usan `RdxRowActions` para mantener una presentación uniforme;
- las frecuentes van en `primaryActions` como botones de icono con tooltip; no se usan botones de texto en la columna de acciones;
- las secundarias y destructivas van en `overflowActions`, accesibles desde el menú de más acciones;
- orden, `disabled`, `disabledReason`, permisos y callbacks pertenecen al consumidor;
- una acción deshabilitada puede seguir visible para explicar por qué no está disponible.
No se trasladan entidades, navegación, dialogs ni permisos a `rdx-ui`.
## 7. Loading, error y empty
| Estado | Patrón |
| --- | --- |
| Loading inicial de tabla | `DataTable loading` dentro del shell estable |
| Loading de editor | skeleton con el mismo frame/container que success |
| Error inicial bloqueante | `PageErrorState` dentro de frame y container |
| Error de refresh | `ErrorAlert` inline; mantiene datos y controles |
| Empty | mensaje/estado vacío en el stack del contenido |
No se crean sistemas alternativos de errores o skeletons cuando los componentes existentes cubren el caso.
## 8. Formularios
- React Hook Form y `FormProvider` pertenecen al consumidor.
- `RdxFormGrid` organiza únicamente campos.
- Errores de campo permanecen junto al control; errores de operación usan `ErrorAlert`.
- Dirty state, bloqueo de navegación y submit pertenecen al controller/provider correspondiente.
- La acción principal comunica loading y evita doble submit.
- Una única superficie editable es preferible a cards anidadas.
## 9. Main + aside
El aside es opcional y contiene metadata, resumen o contexto secundario. En desktop acompaña al main y en pantallas menores se apila debajo.
No usar el aside para equilibrar visualmente columnas, trasladar totales o fragmentar arbitrariamente el flujo del formulario. Tablas y line editors mantienen su layout especializado dentro del main.
## 10. Responsive
- Validar móvil, tablet y desktop.
- Evitar widths fijos en campos y controles localizados.
- Mantener acciones utilizables cuando se apilan.
- Confirmar `min-w-0`, truncado intencional y ausencia de overflow horizontal.
- En layouts especializados, comprobar pane sizes y scroll independiente.
## 11. Accesibilidad
- Controles nativos o primitives accesibles; nunca `div` clicables.
- Labels asociadas mediante `htmlFor`/`id`.
- Icon buttons con `aria-label` y tooltips cuando corresponda.
- Activación por teclado en filas y acciones.
- Foco visible, orden DOM lógico y errores anunciables.
- El color no sustituye texto, icono o estado semántico.
## 12. Testing
Usar tests de comportamiento con el runner vigente del workspace y Testing Library cuando existe interacción DOM. No probar detalles internos del controller ni clases visuales incidentales.
Cobertura mínima según patrón:
- listado: CTA, search, filtros, paginación, estados y navegación de fila;
- editor: header, contenido, acciones, dirty/submit y loading/error;
- dialog: título, campos, condicionales, cancelar/enviar y mutation;
- UI pública: contrato observable, accesibilidad y responsabilidades estructurales.
Las traducciones productivas deben probarse sin sustituirlas por un diccionario que oculte claves ausentes.
## 13. Checklist
- [ ] La ruta usa el shell real declarado en `handle.layout`.
- [ ] Existe un único `RdxPageFrame` propietario del scroll.
- [ ] Header editorial o fullscreen elegido por responsabilidad.
- [ ] Width y padding pertenecen a `RdxPageContainer`.
- [ ] El aside es funcional y opcional.
- [ ] No hay viewport o scroll principal manual en la Page.
- [ ] Loading, error, empty y success conservan estructura coherente.
- [ ] Acciones, permisos y navegación permanecen en el consumidor.
- [ ] Responsive, teclado, foco, i18n y light/dark están validados.
- [ ] Implementación, documentación, UI Kit y tests siguen sincronizados.