FactuGES_ERP/docs/frontend/ui-patterns.md

7.9 KiB

Patrones de UI

Propósito: explicar cómo implementar pantallas concretas. Este documento, junto con Arquitectura frontend y UI system, 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

<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

<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:

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:

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:

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.