diff --git a/apps/web/README.md b/apps/web/README.md index abd81a32..8187402c 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -4,7 +4,7 @@ Aplicación frontend principal del ERP. ## Documentación canónica -- Flujo de autenticación y selección de empresa: [`../../docs/frontend/auth-and-company-selection.md`](../../docs/frontend/auth-and-company-selection.md) +- Arquitectura, autenticación y selección de empresa: [`../../docs/frontend/frontend-architecture.md`](../../docs/frontend/frontend-architecture.md) - Contexto tenant y reglas de `X-Company-Id`: [`../../docs/architecture/tenant-context.md`](../../docs/architecture/tenant-context.md) - Tratamiento de errores HTTP en frontend: [`../../docs/architecture/http-error-handling-frontend.md`](../../docs/architecture/http-error-handling-frontend.md) @@ -14,13 +14,13 @@ La app usa `@erp/identity/client` como runtime actual. ## Flujo actual -- La sesión y la empresa activa se resuelven según el flujo documentado en `docs/frontend/auth-and-company-selection.md`. +- La sesión y la empresa activa se resuelven según el flujo documentado en `docs/frontend/frontend-architecture.md`. - Las rutas tenant-scoped deben respetar las reglas de `X-Company-Id` documentadas en `docs/architecture/tenant-context.md`. ## Headers - No duplicar aquí la matriz de headers. -- Consultar `docs/frontend/auth-and-company-selection.md` para endpoints excluidos y `tenant-context.md` para reglas tenant-scoped. +- Consultar `docs/frontend/frontend-architecture.md` para endpoints excluidos y `tenant-context.md` para reglas tenant-scoped. ## App company switcher @@ -42,10 +42,10 @@ Reglas: - no llamar directamente a `GET /companies/available` - no usar `companySlug` para auth - no modificar el access token -- respetar el flujo canónico documentado en `docs/frontend/auth-and-company-selection.md` +- respetar el flujo canónico documentado en `docs/frontend/frontend-architecture.md` ## Refresh automático La app implementa refresh automático global sobre `401`. -La semántica completa de retry, exclusiones y limpieza de sesión se documenta en `docs/frontend/auth-and-company-selection.md`. +La semántica completa de retry, exclusiones y limpieza de sesión se documenta en `docs/frontend/frontend-architecture.md`. diff --git a/docs/API.md b/docs/API.md index 9da03e1f..855437dd 100644 --- a/docs/API.md +++ b/docs/API.md @@ -11,7 +11,7 @@ Los endpoints, payloads y rutas aquí recogidos pertenecen a una fase anterior d - Auth y sesión: [architecture/identity-auth-api.md](./architecture/identity-auth-api.md) - Contexto tenant: [architecture/tenant-context.md](./architecture/tenant-context.md) - Relación `identity` / `companies`: [architecture/identity-and-companies.md](./architecture/identity-and-companies.md) -- Frontend auth: [frontend/auth-and-company-selection.md](./frontend/auth-and-company-selection.md) +- Frontend auth: [frontend/frontend-architecture.md](./frontend/frontend-architecture.md) - Customer invoices y proformas: [customer-invoices/README.md](./customer-invoices/README.md) ## Motivo de conservación diff --git a/docs/README.md b/docs/README.md index 4869a5ce..2444cec5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -25,9 +25,9 @@ Este directorio reúne la documentación técnica, operativa y de apoyo del ERP. #### Frontend -- [Flujo de auth y selección de empresa](./frontend/auth-and-company-selection.md) -- [Diseño de pantallas de listado](./frontend/diseno-pantallas-listado.md) -- [Diseño de cuadros de diálogo de formulario](./frontend/diseno-cuadros-dialogo.md) +- [Arquitectura frontend](./frontend/frontend-architecture.md) +- [Sistema UI Rdx](./frontend/ui-system.md) +- [Patrones de UI](./frontend/ui-patterns.md) - [Sistema modular de Settings](./settings/frontend-erp-settings-system-design.md) - [Integración de módulos con Settings](./settings/frontend-erp-settings-module-integration-design.md) - [README de `apps/web`](../apps/web/README.md) diff --git a/docs/REQUISITOS GENERALES.md b/docs/REQUISITOS GENERALES.md index 50791ba6..39efe786 100644 --- a/docs/REQUISITOS GENERALES.md +++ b/docs/REQUISITOS GENERALES.md @@ -20,7 +20,7 @@ Este documento resume requisitos funcionales tempranos del ERP. No define el run - Arquitectura y tenant context: [architecture/README.md](./architecture/README.md) - Auth actual: [architecture/identity-auth-api.md](./architecture/identity-auth-api.md) -- Flujo frontend: [frontend/auth-and-company-selection.md](./frontend/auth-and-company-selection.md) +- Arquitectura y flujo frontend: [frontend/frontend-architecture.md](./frontend/frontend-architecture.md) - Módulos documentales: [customer-invoices/README.md](./customer-invoices/README.md) y [document-series/README.md](./document-series/README.md) ## Nota diff --git a/docs/architecture/identity-migration-status.md b/docs/architecture/identity-migration-status.md index 9f600a9c..0ad12afa 100644 --- a/docs/architecture/identity-migration-status.md +++ b/docs/architecture/identity-migration-status.md @@ -3,7 +3,7 @@ ## Referencias canónicas - Reglas de contexto tenant y middlewares públicos: [tenant-context.md](./tenant-context.md) -- Flujo frontend de autenticación y selección de empresa: [../frontend/auth-and-company-selection.md](../frontend/auth-and-company-selection.md) +- Flujo frontend de autenticación y selección de empresa: [../frontend/frontend-architecture.md](../frontend/frontend-architecture.md) - Runtime y responsabilidades del módulo `identity`: [../../modules/identity/README.md](../../modules/identity/README.md) ## Migrado a `identity` @@ -69,7 +69,7 @@ No existe todavía enforcement real de `identity:companies:update`. El runtime nuevo ya usa `@erp/identity/client`. -Los detalles del flujo implementado, headers y refresh automático se mantienen en `frontend/auth-and-company-selection.md`. +Los detalles del flujo implementado, headers y refresh automático se mantienen en `frontend/frontend-architecture.md`. ## Legacy pendiente diff --git a/docs/frontend/UI/contributing-ui.md b/docs/frontend/UI/contributing-ui.md deleted file mode 100644 index d3c88595..00000000 --- a/docs/frontend/UI/contributing-ui.md +++ /dev/null @@ -1,103 +0,0 @@ -# Contributing UI - -## Reglas base - -- todos los componentes públicos propios deben usar prefijo `Rdx`; -- el código y los identificadores deben estar en inglés; -- la documentación y los comentarios deben estar en español; -- usar TypeScript estricto; -- usar imports de tipos explícitos en cabecera; -- no usar tipos inline tipo `import("react").ReactNode`. - -## Responsabilidad de `packages/rdx-ui` - -Los componentes de `rdx-ui`: - -- no conocen React Query; -- no conocen routing; -- no conocen DTOs; -- no conocen entities; -- no conocen controllers funcionales; -- no contienen lógica de negocio. - -Deben recibir props ya preparadas por la capa consumidora. - -## Tema y color - -- todo debe funcionar en `light` y `dark` desde el inicio; -- usar tokens semánticos; -- no introducir colores físicos sin justificación fuerte y documentada; -- no usar el color como único indicador de estado. - -## Accesibilidad - -- foco visible obligatorio; -- botones de icono con nombre accesible; -- no crear `div` clicables; -- mantener orden DOM coherente; -- usar controles reales proporcionados por el consumidor; -- evitar wrappers vacíos que compliquen la semántica. - -## API pública - -- mantener una API pequeña y explícita; -- no crear abstracciones genéricas sin repetición real; -- `className` es un escape controlado, no la API principal; -- no usar `transition-all` en nuevos componentes salvo caso excepcional documentado. - -## Composición de paneles - -- no duplicar sombras entre una primitive y su composición; -- no duplicar el control de cierre entre capas; -- las acciones de contexto deben llegar ya construidas desde el consumidor; -- los labels accesibles de acciones de dominio pertenecen al consumidor; -- una primitive reutilizable puede resolver superficie e infraestructura, mientras la composición resuelve jerarquía visual y accesibilidad contextual. - -## Acciones de fila - -- `RdxRowActions` solo renderiza acciones primarias y overflow; -- el consumidor decide qué acciones existen y en qué orden aparecen; -- las acciones destructivas deben ir en overflow salvo excepción justificada por código real; -- `disabledReason` pertenece al consumidor y debe ser comprensible sin conocimiento de dominio en `rdx-ui`; -- no mover permisos, navegación, dialogs ni entidades dentro de `packages/rdx-ui`; -- los icon buttons deben mantener `aria-label` y tooltip accesible. - -## Editor simple - -- por defecto, las pantallas cuya finalidad principal sea crear, editar o configurar mediante formulario deben usar shell `app-fullscreen` y no el shell con sidebar; -- usar `PageFormHeader` o el header de dominio ya existente para la navegación mínima de salida, sin duplicar una segunda navegación lateral; -- `RdxPanel` con `variant="plain"` es el patrón por defecto del editor simple; -- usar una única superficie editable cuando sea útil separar claramente la página del área de formulario; -- no envolver cada sección en `Card` por defecto; -- no duplicar padding entre shell, layout y superficie editable; -- `RdxFormGrid` debe concentrar la retícula del editor sin acoplarse a RHF; -- las diferencias entre create y update no justifican nuevos componentes si solo cambian defaults o wiring. - -## Editor complejo - -- `RdxEditorPageLayout` debe reservar el `aside` para resumen, metadata o contexto de apoyo; -- no mover campos editables al `aside` solo para equilibrar visualmente columnas; -- en desktop el layout objetivo del editor complejo es `minmax(0, 1fr) ~22rem`; -- el `aside` debe apilarse debajo del `main` cuando no haya ancho suficiente; -- si no existe `aside`, no debe reservarse hueco lateral; -- mantener una única superficie principal para el formulario, también en editores complejos; -- `RdxPanel` puede usar `variant="card"` dentro del `aside` cuando el bloque necesite superficie independiente; -- `RdxFormGrid` debe quedarse en grupos de campos y no envolver tablas, line editors ni layouts funcionales especializados; -- no mover los totales al `aside` por defecto; -- si el editor usa footer sticky inferior, no duplicar `Guardar` y `Cancelar` completos en el header; -- `RdxEditorStickyFooter` no conoce RHF, submit, routing ni dominio; solo compone superficie, orden, separación y responsive; -- la región `leading` del footer puede alojar resumen o contexto, pero ese contenido sigue perteneciendo al dominio consumidor; -- coordinar cualquier `sticky` lateral con el offset del header para evitar clipping y foco oculto. - -## Sincronización obligatoria - -Todo cambio futuro que afecte a la API pública o al comportamiento visual debe actualizar en el mismo cambio: - -```txt -1. implementación -2. documentación Markdown -3. página interactiva UI Kit -4. tests aplicables -``` - -Un cambio queda incompleto si cualquiera de esas cuatro piezas queda desincronizada. diff --git a/docs/frontend/UI/foundations.md b/docs/frontend/UI/foundations.md deleted file mode 100644 index 17d273b5..00000000 --- a/docs/frontend/UI/foundations.md +++ /dev/null @@ -1,134 +0,0 @@ -# Foundations Rdx - -## Estado - -Este documento define las foundations estructurales iniciales del sistema visual Rdx. - -El documento canónico de contexto global es: - -- [visual-system-design.md](./visual-system-design.md) - -## Espaciado de página - -```txt -mobile: - padding 4 - -desktop: - padding 6 -``` - -Referencia inicial: - -```txt -px-4 py-4 -md:px-6 md:py-6 -``` - -Se aplica en `RdxPageContainer`. - -Uso correcto: - -- usarlo como padding principal de página; -- no anidarlo dentro de otro contenedor que ya aporte el mismo padding estructural; -- si el shell ya aplica padding equivalente, el consumidor debe evitar duplicarlo. - -## Ritmo vertical - -```txt -compact: - gap 4 - -default: - gap 6 - -relaxed: - gap 8 -``` - -No se fija `gap-12` como foundation general. - -Se aplica en `RdxPageStack`. - -## Anchuras - -```txt -full: - w-full - -wide: - max-w-screen-2xl mx-auto - -content: - max-w-5xl mx-auto -``` - -Uso recomendado: - -- `full`: listados, tablas, editores complejos, pantallas documentales; -- `wide`: detalles o páginas amplias centradas; -- `content`: editores simples y formularios de una columna lógica. - -## Superficies - -```txt -contenido principal: - plano - -secciones: - separadas por espacio o divisores - -aside: - puede usar paneles - -tabla/editor de líneas: - puede usar superficie independiente - -card: - excepción, no default -``` - -Reglas: - -- no usar card como solución por defecto para toda la página; -- evitar card dentro de card; -- preferir espacio, jerarquía tipográfica y divisores. - -## Sombras - -```txt -página: - sin sombra - -secciones: - sin sombra - -panel contextual: - sombra suave opcional - -sticky header/footer: - preferir borde - usar sombra mínima solo cuando sea necesaria -``` - -## Tema - -Uso obligatorio de tokens semánticos: - -```txt -bg-background -text-foreground -bg-muted -text-muted-foreground -border-border -bg-primary -text-primary-foreground -bg-destructive -text-destructive-foreground -``` - -Reglas: - -- no introducir colores físicos para resolver superficies o estados; -- no crear una nueva paleta de `success`, `warning` o `info` en esta fase si no existe convención central consolidada; -- validar cualquier foundation nueva en `light` y `dark`. diff --git a/docs/frontend/UI/page-layouts.md b/docs/frontend/UI/page-layouts.md deleted file mode 100644 index 3d4a2f91..00000000 --- a/docs/frontend/UI/page-layouts.md +++ /dev/null @@ -1,260 +0,0 @@ -# Page Layouts Rdx - -## Estado - -Este documento describe los patrones conceptuales de página aprobados para adopción progresiva. - -El documento canónico de contexto global es: - -- [visual-system-design.md](./visual-system-design.md) - -## Listado - -```txt -RdxPageContainer -└── RdxPageStack - ├── RdxPageHeader - ├── notices opcionales - └── contenido del listado -``` - -Reglas: - -- anchura por defecto `full`; -- acciones principales en cabecera; -- acciones de fila frecuentes visibles y acciones destructivas en overflow mediante `RdxRowActions`; -- panel contextual solo cuando aporte contexto real; -- la composición del listado no debe acoplarse a React Query, routing ni dominio. - -Ejemplo de integración real validado en `proformas/list` y `customers/list`: - -```tsx - - - } - /> - - {notice} - - {content} - - - {dialogs} - -``` - -Aclaraciones: - -- `RdxPageContainer` resuelve el padding exterior y la anchura de página; -- `RdxPageStack` resuelve el ritmo vertical principal entre cabecera, aviso y contenido; -- `RdxPageHeader` no conoce navegación ni callbacks de React Router; -- la tabla, los filtros, los panels, los dialogs y los controllers permanecen fuera de las foundations; -- `RdxRowActions` puede vivir dentro de las columnas de tabla, pero recibe acciones ya decididas por el consumidor; -- en ambos pilotos el panel contextual sigue viviendo en composición local mediante `ResizablePanelGroup`; -- no documentar todavía `RdxListPageLayout` ni `RdxResponsivePageGrid` mientras el resize y la apertura del aside dependan del caso de uso. - -### Evidencia de pilotos de listado - -```txt -DECISIÓN_CONFIRMADA - Container + Stack + Header + Actions cubren la capa exterior del listado. - -DECISIÓN_PROVISIONAL - La Page puede extraer una vista pura si mejora el test y reduce wiring. - -NO_ABSTRAER_TODAVÍA - main + aside redimensionable - shell específico del panel contextual - toolbars -``` - -## Listado con panel contextual - -```txt -ResizablePanelGroup -├── main -└── RightPanel - └── RdxContextPanel -``` - -Reglas: - -- `ResizablePanelGroup` gestiona la geometría, el resize y la persistencia local del split; -- `RightPanel` gestiona la superficie lateral, la altura y el overflow base; -- `RdxContextPanel` gestiona cabecera, cierre, body y footer visual del panel; -- el contenido interno del panel sigue siendo específico del dominio; -- no documentar todavía un layout genérico de listado a partir de este patrón. - -## Editor simple - -```txt -scroll container -└── RdxEditorPageLayout complexity="simple" - └── form stack - ├── RdxPanel - ├── RdxPanel - └── ... -``` - -Reglas: - -- las rutas cuya tarea principal sea crear, editar o configurar mediante un formulario focalizado deben entrar por el shell `app-fullscreen`; -- listados, dashboards, detalle y navegación de settings mantienen `app-sidebar` salvo necesidad funcional explícita; -- `RdxEditorPageLayout` resuelve padding interior, ancho centrado y `min-w-0`; -- el editor simple puede apoyarse en una única superficie contenedora para diferenciar la página del área editable, sin volver a cards por sección; -- `RdxPanel` expresa jerarquía por defecto con spacing y divisor inferior; -- `RdxFormGrid` mantiene la retícula del formulario sin conocer RHF ni dominio; -- priorizar lectura y velocidad de escaneo; -- usar secciones planas con espacio o divisores; -- reservar sticky footer solo cuando el scroll lo justifique. - -Ejemplo validado en `customers/update`, `customers/create` y `/account`: - -```tsx - - -
- -
- {notice} - -
- - {fields} - -
-
-
-
-
-``` - -Deuda registrada: - -- `PageFormHeader` sigue siendo full-width mientras el contenido del editor queda centrado en `max-w-5xl`; -- no modificar todavía el header legacy sin validación visual manual real. - -Evidencia adicional de create/update: - -```txt -DECISIÓN_CONFIRMADA - ambos flujos reutilizan el mismo layout, superficie y secciones. - -ESPECÍFICO - defaults iniciales - wiring de submit - feedback de error - navegación de retorno concreta -``` - -Candidatos explícitos para auditoría posterior: - -- `customers/create` -- `customers/update` -- `/account` -- `proformas/update` - -## Editor complejo - -```txt -shell -├── PageFormHeader -├── scroll container -│ └── RdxEditorPageLayout complexity="complex" -│ ├── main -│ │ └── única superficie editable -│ │ ├── RdxPanel -│ │ ├── RdxPanel -│ │ ├── bloque de líneas con layout propio -│ │ └── ... -│ └── aside -│ └── controles secundarios o contexto de apoyo -└── RdxEditorStickyFooter - ├── summary/context region - └── action region -``` - -Reglas: - -- `RdxEditorPageLayout` resuelve padding exterior, `min-w-0` y la geometría `main + aside`; -- el `main` debe alojar los campos editables y sus bloques funcionales; -- el `aside` debe reservarse para metadata, configuración o contexto de apoyo ya existente en la pantalla; -- el `aside` del editor complejo usa una geometría aproximada `minmax(0, 1fr) 22rem` en desktop y se apila debajo del `main` cuando no hay ancho suficiente; -- no mover campos editables al `aside` solo por estética si eso empeora el flujo; -- no mover los totales al `aside`; -- los bloques con layout interno propio, como una tabla de líneas, no deben forzarse dentro de `RdxFormGrid`; -- `RdxEditorStickyFooter` resuelve una única superficie sticky con una región contextual flexible y una región de acciones; -- los totales siguen perteneciendo al dominio de Proformas aunque se rendericen dentro de la región contextual del footer; -- `PageFormHeader` puede seguir existiendo, pero no debe duplicar `Guardar` y `Cancelar` cuando el footer sticky ya los contiene. - -Ejemplo validado en `proformas/update`: - -```tsx -
- - -
-
- {notice} - - -
-
- - {documentFields} - - - {lineEditor} -
-
-
-
- - } - secondary={cancelButton} - primary={saveButton} - status={status} - /> -
-
-``` - -Evidencia del piloto: - -```txt -DECISIÓN_CONFIRMADA - main contiene la única superficie editable - aside contiene bloques secundarios ya existentes del editor - la sticky zone inferior pasa a una única superficie visual - los totales permanecen como dominio de Proformas dentro de la región contextual - las acciones principales comparten el mismo footer sin una segunda franja - RdxFormGrid se mantiene solo para campos - -DECISIÓN_PROVISIONAL - visual exacta del header legacy frente al ancho full del editor complejo - -NO_ABSTRAER_TODAVÍA - layout específico del bloque de líneas - layout específico del footer de totales -``` - -## Detalle - -```txt -width="wide" o "full" -main + aside opcional -aside para metadata, actividad, resumen o acciones -``` - -Reglas: - -- mantener patrón consistente de label/value; -- usar aside solo cuando el contenido realmente gane claridad; -- no implementar todavía `RdxDetailPageLayout` en esta fase. diff --git a/docs/frontend/UI/visual-system-design.md b/docs/frontend/UI/visual-system-design.md deleted file mode 100644 index 0fb641db..00000000 --- a/docs/frontend/UI/visual-system-design.md +++ /dev/null @@ -1,1225 +0,0 @@ -# Frontend ERP — Sistema visual Rdx para páginas y patrones UI/UX - -## 1. Estado - -**DISEÑO APROBADO PARA IMPLEMENTACIÓN PROGRESIVA** - -Este documento consolida las decisiones visuales, arquitectónicas y de adopción para construir un sistema UI/UX uniforme en el frontend del ERP. - -El sistema se implementará dentro de: - -```txt -packages/rdx-ui -``` - -Todos los componentes públicos propios del sistema deberán usar el prefijo: - -```txt -Rdx -``` - -Ejemplos: - -```txt -RdxPageContainer -RdxPageHeader -RdxPageActions -RdxContextPanel -RdxRowActions -RdxListPageLayout -RdxEditorPageLayout -RdxPanel -RdxFormGrid -RdxEditorStickyFooter -RdxDetailPageLayout -``` - -El prefijo distingue los componentes propios de la aplicación frente a primitives de terceros como Shadcn/UI y evita colisiones de naming. - ---- - -## 2. Contexto - -El frontend ERP está basado en: - -- React 19 -- TypeScript estricto -- Shadcn/UI -- Tailwind CSS v4 -- React Hook Form -- TanStack Query -- TanStack Table -- React Router - -La arquitectura general mantiene el flujo: - -```txt -API → DTO → adapter → Entity → hook → controller → page controller → UI -``` - -Separación de responsabilidades: - -```txt -shared/api → transporte remoto -shared/adapters → DTO → Entity -shared/hooks → React Query y entities -controllers → orquestación del caso de uso -page controllers → composición e integración externa -Page → wiring y composición -UI → render puro -``` - -El sistema visual Rdx debe respetar estrictamente esta arquitectura. - ---- - -## 3. Objetivo - -Crear un estándar visual y estructural común para los principales tipos de pantalla del ERP: - -```txt -Listados -Vistas de detalle -Pantallas de creación -Pantallas de edición -``` - -El sistema debe unificar: - -- márgenes; -- padding; -- separación entre elementos; -- anchuras; -- jerarquía tipográfica; -- disposición de acciones; -- iconografía; -- estados visuales; -- superficies; -- responsive; -- accesibilidad; -- soporte claro/oscuro; -- documentación y ejemplos interactivos. - -El objetivo no es crear un framework CRUD universal, sino una biblioteca pequeña de primitives y composiciones visuales explícitas. - ---- - -## 4. Principios rectores - -### 4.1 Claridad antes que abstracción - -```txt -Primero claridad local. -Después repetición real. -Solo entonces abstraer. -``` - -No se crearán componentes genéricos sin evidencia suficiente de repetición. - -### 4.2 UI pura - -Los componentes Rdx no deben conocer: - -- React Query; -- DTOs; -- APIs; -- controllers; -- routing; -- permisos de negocio; -- estados de dominio; -- errores de transporte. - -Deben recibir props ya preparadas. - -### 4.3 Composición, no configuración excesiva - -Preferir: - -```tsx - - - - - - -``` - -Evitar: - -```tsx - -``` - -### 4.4 Diseño compacto y ordenado - -La interfaz debe: - -- evitar espacio vacío que no aporte jerarquía; -- mantener alta densidad informativa; -- conservar suficiente separación para lectura; -- evitar ruido visual; -- evitar card dentro de card; -- usar superficies solo cuando tengan una función clara. - -### 4.5 Tema claro y oscuro desde el inicio - -Todo componente debe diseñarse y validarse en: - -```txt -Tema claro -Tema oscuro -``` - -No se aceptará una implementación diseñada solo para tema claro y corregida posteriormente. - ---- - -## 5. Referencias visuales - -### 5.1 Pantalla de referencia principal - -La pantalla de listado de proformas es la referencia visual inicial para: - -- densidad; -- toolbar; -- espaciado; -- jerarquía de acciones; -- tratamiento de tabla; -- iconografía; -- filtros; -- composición general. - -Ruta: - -```txt -modules/customer-invoices/src/web/proformas/list -``` - -Estado de adopción a 6 de agosto de 2026: - -- primera pantalla piloto migrada a `RdxPageContainer`, `RdxPageStack`, `RdxPageHeader` y `RdxPageActions`; -- se ha sustituido el header legacy solo en la Page de listado, sin tocar controllers, grid, filtros, panel lateral ni dialogs; -- el piloto confirma que las foundations actuales cubren la geometría exterior de la página sin justificar todavía `RdxListPageLayout`, `RdxResponsivePageGrid` ni `RdxContextPanel`. -- segundo piloto migrado en `modules/customers/src/web/list` con el mismo perímetro de cambio; -- ambos listados confirman la reutilización de container, stack, header y CTA principal, pero todavía no justifican abstraer la geometría interior del listado; -- los dos paneles contextuales reales confirman una separación válida entre `RightPanel` como primitive de superficie y `RdxContextPanel` como composición visual contextual. - -### 5.2 Pantallas de contraste - -Se usarán además: - -```txt -modules/customers/src/web/list -modules/customers/src/web/create -modules/customers/src/web/update -modules/customers/src/web/view -modules/customer-invoices/src/web/proformas/create -modules/customer-invoices/src/web/proformas/update -modules/customer-invoices/src/web/proformas/detail -``` - -Estas pantallas permiten validar listados, editor simple, editor complejo y detalle. - ---- - -## 6. Tipos de pantalla - -## 6.1 Listados - -Reglas: - -```txt -Anchura completa -Toolbar compacta -Tabla densa -Acción principal en cabecera -Panel lateral opcional -Paginación integrada -Estados vacíos y errores consistentes -``` - -El listado no debe estar obligado a usar filtros, panel lateral o tabs. - -## 6.2 Editor simple - -Reglas: - -```txt -Una columna por defecto -Ancho máximo centrado -Lateral opcional -No reservar espacio si el lateral no existe -Secciones planas con divisores -``` - -Ejemplos: - -```txt -customers/create -customers/update -suppliers/create -suppliers/update -``` - -## 6.3 Editor complejo - -Reglas: - -```txt -Todo el ancho disponible -Main flexible -Lateral derecho permitido como región estructural estándar -Footer sticky cuando exista scroll significativo -Paneles laterales para resumen, configuración o contexto -``` - -Ejemplos: - -```txt -proformas/update -issued-invoices/update, si existiera -``` - -## 6.4 Vista de detalle - -Reglas: - -```txt -Anchura wide o full -Main + lateral opcional -Lateral para metadata, actividad, resumen o acciones -Patrón label/value consistente -``` - ---- - -## 7. Política de anchuras - -Se define el siguiente contrato inicial: - -```ts -export type RdxPageWidth = "full" | "wide" | "content"; -``` - -### `full` - -Uso: - -- listados; -- editores complejos; -- tablas; -- pantallas documentales. - -### `wide` - -Uso: - -- vistas de detalle; -- pantallas con mucha información sin necesidad de ocupar todo el viewport. - -### `content` - -Uso: - -- formularios y editores simples. - -Referencia inicial: - -```txt -max-w-5xl o max-w-6xl -``` - -La decisión exacta se centraliza en `RdxPageContainer`. - ---- - -## 8. Foundations visuales - -## 8.1 Espaciado - -Escala inicial recomendada: - -```txt -compact → 1rem -normal → 1.5rem -relaxed → 2rem -``` - -Propuesta de uso: - -```txt -Page padding mobile: 1rem -Page padding desktop: 1.5rem -Main block gap: 1.5rem -Section internal gap: 1rem -Form grid horizontal: 1.5rem -Form grid vertical: 1.25rem -Compact element gap: 0.5–0.75rem -``` - -No se fija `gap-12` como foundation global. - -## 8.2 Tipografía - -Funciones tipográficas: - -```txt -Page title -Page description -Section title -Section description -Field label -Body -Metadata -Numeric value -Status -``` - -Referencia inicial: - -```txt -Page title: text-2xl font-semibold tracking-tight -Page description: text-sm text-muted-foreground -Section title: text-base font-semibold -Section description: text-sm text-muted-foreground -Metadata label: text-xs font-medium text-muted-foreground -Numeric value: font-medium tabular-nums -``` - -## 8.3 Superficies - -Política: - -```txt -Contenido principal → plano -Secciones → divisores y spacing -Aside → paneles permitidos -Tabla compleja → superficie independiente permitida -Notices → superficie semántica -Cards → excepción, no default -``` - -## 8.4 Bordes y radios - -Reglas: - -- evitar border en cada bloque; -- preferir `border-b` o `Separator` entre secciones; -- reservar radios para paneles o superficies independientes; -- los radios hijos no deben exceder los radios del contenedor padre. - -## 8.5 Sombras - -Reglas: - -```txt -Página y secciones → sin sombra -Panel contextual → sombra suave opcional -Header/footer sticky → borde como primera opción -``` - -Las sombras deben ser mínimas. - -## 8.6 Color - -Usar tokens semánticos: - -```txt -background -foreground -card -muted -muted-foreground -border -primary -primary-foreground -destructive -destructive-foreground -``` - -Se deberán consolidar también tokens para: - -```txt -success -warning -info -``` - -No usar directamente colores físicos cuando exista un token semántico adecuado. - -Evitar: - -```txt -text-green-600 -bg-yellow-100 -border-blue-300 -``` - ---- - -## 9. Arquitectura de componentes - -## 9.1 Ubicación - -```txt -packages/rdx-ui/src/components/ -``` - -Estructura objetivo progresiva: - -```txt -components/ -├── layout/ -├── page-states/ -├── list-page/ -├── detail-page/ -├── editor-page/ -├── form/ -└── right-panel/ -``` - -No es obligatorio crear toda la estructura desde la primera fase. - -## 9.2 Naming - -Todos los componentes públicos propios usan `Rdx`. - -Ejemplo: - -```ts -export interface RdxPageHeaderProps { - // ... -} - -export const RdxPageHeader = ({ - // ... -}: RdxPageHeaderProps) => { - // ... -}; -``` - -No es obligatorio usar `Rdx` en: - -- helpers internos; -- tipos privados; -- variantes locales; -- tokens CSS. - ---- - -## 10. Foundations implementadas y siguientes candidatos - -Fase 1 implementada: - -```txt -RdxPageContainer -RdxPageStack -RdxPageHeader -RdxPageActions -RdxContextPanel -RdxRowActions -RdxEditorPageLayout -RdxPanel -RdxFormGrid -RdxEditorStickyFooter -``` - -Candidatos posteriores a validar con pantallas piloto reales: - -```txt -RdxResponsivePageGrid -RdxListPageLayout -``` - -También deberán evaluarse y adaptar, no necesariamente reescribir: - -```txt -PageHeader -PageFormHeader -RightPanel -DataTable -``` - -No se implementarán todavía: - -```txt -RdxDetailPageLayout -RdxDetailField -RdxNotice -RdxEmptyState -``` - -Estos componentes se diseñarán en fases posteriores con pantallas piloto específicas. - ---- - -## 11. Contratos iniciales - -## 11.1 `RdxPageContainer` - -Responsabilidad: - -- ancho; -- padding exterior; -- centrado; -- comportamiento responsive. - -```ts -export interface RdxPageContainerProps { - children: React.ReactNode; - width?: RdxPageWidth; - className?: string; -} -``` - -No debe gestionar ritmo vertical. - -## 11.2 `RdxPageStack` - -Responsabilidad: - -- separación vertical entre bloques principales. - -```ts -export type RdxPageStackSpacing = "compact" | "default" | "relaxed"; - -export interface RdxPageStackProps { - children: React.ReactNode; - spacing?: RdxPageStackSpacing; - className?: string; -} -``` - -## 11.3 `RdxPageHeader` - -Responsabilidad: - -- título; -- descripción; -- leading opcional; -- metadata opcional; -- acciones; - -```ts -export interface RdxPageHeaderProps { - title: React.ReactNode; - description?: React.ReactNode; - leading?: React.ReactNode; - metadata?: React.ReactNode; - actions?: React.ReactNode; - className?: string; -} -``` - -No debe conocer routing. - -## 11.4 `RdxPageActions` - -Responsabilidad: - -- agrupar acciones; -- diferenciar primarias, secundarias y overflow; -- resolver layout responsive. - -```ts -export interface RdxPageActionsProps { - primary?: React.ReactNode; - secondary?: React.ReactNode; - overflow?: React.ReactNode; - className?: string; -} -``` - -No debe decidir permisos ni disponibilidad. - -## 11.5 `RdxResponsivePageGrid` - -Responsabilidad: - -- layout `main + aside`; -- stacking responsive; -- ancho limitado del aside. - -```ts -export type RdxPageAsideWidth = "sm" | "md" | "lg"; -export type RdxPageAsideBehavior = "static" | "sticky"; - -export interface RdxResponsivePageGridProps { - main: React.ReactNode; - aside?: React.ReactNode; - asideWidth?: RdxPageAsideWidth; - asideBehavior?: RdxPageAsideBehavior; - className?: string; - mainClassName?: string; - asideClassName?: string; -} -``` - -Reglas: - -```txt -Desktop → main + aside -Tablet/móvil → aside debajo de main -Sin aside → no reservar espacio -Sticky → variante explícita -``` - -## 11.6 `RdxListPageLayout` - -Responsabilidad: - -- geometría del área de listado; -- composición `main + aside`. - -```ts -export interface RdxListPageLayoutProps { - main: React.ReactNode; - aside?: React.ReactNode; - className?: string; -} -``` - -No debe conocer: - -- DataTable; -- búsqueda; -- filtros; -- selección; -- React Query; -- estado remoto; -- dominio. - -## 11.7 `RdxRowActions` - -Responsabilidad: - -- renderizar acciones visibles; -- renderizar acciones overflow; -- representar disabled y destructive; -- ofrecer tooltips y labels accesibles. - -```ts -export interface RdxRowAction { - id: string; - label: string; - icon: React.ReactNode; - onSelect: () => void; - disabled?: boolean; - disabledReason?: string; - destructive?: boolean; -} - -export interface RdxRowActionsProps { - primaryActions?: RdxRowAction[]; - overflowActions?: RdxRowAction[]; - overflowLabel: string; - className?: string; -} -``` - -El componente no decide qué acciones existen. -La UI solo renderiza; el consumidor conserva permisos, labels, disabled reasons y callbacks. - ---- - -## 12. Política de acciones - -## 12.1 Listados - -```txt -Acción principal → cabecera -Acciones frecuentes → visibles en fila -Acciones infrecuentes → overflow -Acciones destructivas → overflow por defecto -``` - -Orden recomendado: - -```txt -Ver -Editar -Más acciones -``` - -## 12.2 Formularios cortos - -Acciones en cabecera o al final del formulario. - -## 12.3 Formularios largos - -Acciones principales en footer sticky. - -## 12.4 Editores complejos - -```txt -Cabecera → navegación, estado y acciones documentales -Footer sticky → cancelar, guardar y submit principal -``` - -La ubicación depende de la distancia real del usuario a la acción primaria, no solo del tipo CRUD. - ---- - -## 13. Política de iconos - -Catálogo inicial: - -| Acción | Icono recomendado | -|---|---| -| Ver | `Eye` | -| Editar | `Pencil` | -| Crear | `Plus` | -| Eliminar | `Trash2` | -| Descargar | `Download` | -| Imprimir | `Printer` | -| Duplicar | `Copy` | -| Más acciones | `Ellipsis` | -| Volver | `ArrowLeft` | -| Guardar | `Save` | -| Filtrar | `ListFilter` | -| Buscar | `Search` | -| Configurar columnas | `Columns3` | -| Cerrar | `X` | - -Reglas: - -- iconos con tamaño consistente; -- botón solo icono con `aria-label`; -- tooltip cuando la acción no sea inequívoca; -- no usar iconos diferentes para la misma acción sin justificación. - ---- - -## 14. Responsive - -## 14.1 Listados - -```txt -Desktop → toolbar horizontal y tabla completa -Tablet → filtros colapsables cuando sea necesario -Mobile → scroll horizontal o adaptación específica -``` - -No se implementará todavía una vista móvil universal de cards para tablas. - -## 14.2 Editor simple - -```txt -Desktop → ancho máximo centrado -Mobile → una columna -``` - -## 14.3 Editor complejo - -```txt -Desktop → main + aside -Tablet/móvil → aside debajo de main -Footer sticky → debe respetar safe areas -``` - -## 14.4 Detalle - -```txt -Desktop → main + aside opcional -Tablet/móvil → aside debajo de main -``` - ---- - -## 15. Accesibilidad - -Reglas obligatorias: - -- un único `h1` por página; -- jerarquía correcta de headings; -- botones reales para acciones; -- links reales para navegación; -- foco visible; -- botones de icono con `aria-label`; -- estados no representados solo mediante color; -- `type="button"` en botones internos de formularios; -- targets táctiles suficientes; -- tooltips accesibles; -- dialogs con restauración de foco; -- loading que conserve el significado de la acción; -- acciones disabled con explicación cuando sea útil. - ---- - -## 16. Reutilización de componentes existentes - -Antes de crear un componente nuevo se debe comparar su contrato con: - -```txt -modules/core/src/web/components/page-header.tsx -modules/core/src/web/components/form/form-actions/page-form-header.tsx -modules/core/src/web/components/page-error-state.tsx -modules/core/src/web/components/error-alert.tsx -packages/rdx-ui/src/components/right-panel/right-panel.tsx -packages/rdx-ui/src/components/datatable/data-table.tsx -packages/rdx-ui/src/components/form/form-section-card.tsx -packages/rdx-ui/src/components/form/form-section-grid.tsx -``` - -Proceso obligatorio: - -```txt -1. Definir contrato objetivo Rdx. -2. Comparar con componente existente. -3. Adaptar si es viable. -4. Exportar con naming Rdx. -5. Mantener alias temporal si hay consumidores. -6. Migrar progresivamente. -7. Retirar alias cuando no se use. -``` - -No se harán renombrados globales sin estrategia de compatibilidad. - ---- - -## 17. Documentación - -## 17.1 Markdown - -Ubicación canónica: - -```txt -packages/rdx-ui/docs/ -├── visual-system-design.md -├── foundations.md -├── page-layouts.md -└── contributing-ui.md -``` - -## 17.2 UI Kit interactivo - -Se creará una ruta interna: - -```txt -/development/ui-kit -``` - -El UI Kit debe mostrar: - -- foundations; -- componentes aislados; -- variantes; -- estados; -- tema claro y oscuro; -- comportamiento responsive; -- composiciones completas; -- ejemplos correctos; -- anti-patterns relevantes. - -Ejemplos completos mínimos: - -```txt -Listado -Detalle -Editor simple -Editor complejo -Error -Vacío -Loading -``` - -## 17.3 Regla de sincronización - -Todo cambio que afecte a API pública, comportamiento o apariencia debe actualizar en la misma intervención: - -```txt -1. implementación; -2. documentación Markdown; -3. UI Kit interactivo; -4. tests aplicables. -``` - -Un cambio se considera incompleto si alguno de estos recursos queda desactualizado. - ---- - -## 18. Testing - -Los tests deben centrarse en comportamiento: - -- render de slots; -- variantes; -- accesibilidad; -- acciones disabled; -- overflow; -- responsive inferible por clases; -- ausencia de columna aside cuando no se proporciona; -- compatibilidad con tema. - -No testear detalles internos irrelevantes. - -Herramientas: - -```txt -Testing Library -Vitest -``` - ---- - -## 19. Plan de implementación - -## Fase 0 — Documentación y UI Kit base - -Crear o mantener sincronizados: - -```txt -packages/rdx-ui/docs/visual-system-design.md -packages/rdx-ui/docs/foundations.md -packages/rdx-ui/docs/page-layouts.md -packages/rdx-ui/docs/contributing-ui.md -``` - -Crear la infraestructura mínima de `/development/ui-kit`. - -## Fase 1 — Foundations de página - -Implementar: - -```txt -RdxPageContainer -RdxPageStack -RdxPageHeader -RdxPageActions -``` - -## Fase 2 — Piloto `proformas/list` - -Aplicar foundations sin rediseño funcional. - -Objetivos: - -```txt -Misma funcionalidad -Misma densidad -Menos clases estructurales locales -Contrato reusable validado -``` - -Resultado observado: - -- `RdxPageContainer` absorbe el padding exterior de página y evita mantener `p-6` local; -- `RdxPageStack` absorbe el ritmo vertical principal y evita mantener `space-y-6` local; -- `RdxPageHeader` cubre la cabecera de listado con composición directa; -- `RdxPageActions` cubre correctamente una acción primaria única; -- el layout interno del listado sigue necesitando composición específica local mientras no se compare este piloto con `customers/list`. - -## Fase 3 — Layout de listado y panel - -Implementar: - -```txt -RdxResponsivePageGrid -RdxListPageLayout -``` - -Validar con: - -```txt -proformas/list -customers/list -``` - -Estado tras comparar ambos pilotos: - -- `RdxListPageLayout`: prematuro; -- `RdxResponsivePageGrid`: prematuro en listados que dependen de `ResizablePanelGroup`; -- `RdxContextPanel`: candidato confirmado para una fase específica sobre `RightPanel`, `CustomerSummaryPanel` y `ProformaSummaryPanel`. - -## Fase 4 — Panel contextual - -Normalizar: - -```txt -RightPanel -RdxContextPanel -CustomerSummaryPanel -ProformaSummaryPanel -``` - -Resultado esperado: - -- `RightPanel` mantiene la superficie lateral reusable; -- `RdxContextPanel` concentra cabecera, cierre, jerarquía y footer contextual; -- no existe doble header; -- no existe doble cierre; -- no existe doble sombra. - -## Fase 5 — Acciones de fila - -Implementar `RdxRowActions` tomando proformas como referencia. - -Resultado observado a 6 de agosto de 2026: - -- `RdxRowActions` ya existe en `packages/rdx-ui` y se exporta públicamente; -- `proformas/list` y `customers/list` consumen el mismo contrato visual; -- las acciones destructivas quedan en overflow; -- el consumidor sigue decidiendo permisos, estado, callbacks y razones de disabled; -- en `customers/list` se ha corregido el bug que renderizaba delete cuando no existía `onDeleteClick`. - -## Fase 6 — Editor simple - -Usar `customers/update` como piloto para: - -```txt -RdxEditorPageLayout -RdxPanel -RdxFormGrid -``` - -Resultado observado a 6 de agosto de 2026: - -- `RdxEditorPageLayout` absorbe el ancho centrado y el padding interior del editor simple; -- `RdxPanel` sustituye en `customers/update` el uso sistemático de `FormSectionCard` cuando la superficie no necesita card; -- `RdxFormGrid` consolida la retícula ya existente sin mover lógica de formulario fuera del consumidor; -- `PageFormHeader` se mantiene sin cambios funcionales; -- queda pendiente validar visualmente si el header sticky full-width se percibe demasiado separado del ancho centrado del editor. - -## Fase 7 — Confirmación de editor simple - -Validar el mismo patrón en: - -```txt -customers/create -``` - -Resultado observado a 6 de agosto de 2026: - -- `customers/create` reutiliza `RdxEditorPageLayout`, `RdxPanel` y `RdxFormGrid` sin requerir nuevas primitives; -- create y update comparten shell, header legacy, superficie editable única y secciones planas internas; -- las diferencias reales quedan en defaults, submit y wiring del controller, no en la composición visual; -- `PageFormHeader` sigue siendo funcionalmente suficiente, aunque se mantiene una deuda visual menor por su anchura full-width frente al contenido centrado. - -## Fase 8 — Editor complejo - -Usar `proformas/update` como piloto para: - -```txt -main + aside -sticky footer -totales -acciones según scroll -``` - -Resultado observado a 10 de agosto de 2026: - -- `proformas/update` valida `RdxEditorPageLayout` con `complexity="complex"` a ancho completo, `main` flexible y `aside` estructural aproximado de `22rem`; -- el `main` concentra la única superficie editable real del documento y el bloque de líneas mantiene su layout propio; -- el `aside` conserva únicamente controles secundarios ya presentes en la pantalla, sin convertirlo en `RightPanel` ni mover allí los totales; -- el footer sticky de totales se mantiene como dominio visual y económico de Proformas; -- `RdxEditorStickyFooter` queda justificado para combinar en una única superficie sticky la región de totales/contexto y las acciones `Cancelar` / `Guardar`; -- `PageFormHeader` queda reducido a navegación y contexto solo en `proformas/update`, sin cambio global del componente legacy; -- `RdxPanel` y `RdxFormGrid` siguen siendo válidos para bloques de campos, pero no deben forzar el layout del editor de líneas ni el footer de totales. - -## Fase 8 — Detalle - -Usar primero `proformas/detail` y después `customers/view`. - ---- - -## 20. Anti-patterns prohibidos - -No crear: - -```txt -RdxCrudPage -RdxCrudEditor -RdxGenericEntityPage -RdxUniversalToolbar -RdxFormSectionEngine -GenericPageFactory -``` - -No hacer: - -- routing dentro de `packages/rdx-ui`; -- React Query dentro de componentes Rdx; -- interpretación de `ApiClientError` en UI; -- permisos dentro de `RdxRowActions`; -- hardcodes de dominio; -- colors físicos cuando exista token semántico; -- megacomponentes configurables; -- migraciones globales sin alias temporal; -- abstracciones genéricas antes de validarlas en varias pantallas. - ---- - -## 21. Evidencia de pilotos de listado - -### DECISIÓN_CONFIRMADA - -- `RdxPageContainer` resuelve de forma estable el padding exterior y la anchura de `proformas/list` y `customers/list`. -- `RdxPageStack` resuelve el ritmo vertical principal entre cabecera, error inline y contenido. -- `RdxPageHeader` sustituye correctamente al `PageHeader` legacy en ambos listados sin conocer navegación. -- `RdxPageActions` cubre la CTA principal de cabecera en ambos pilotos sin necesidad de variantes adicionales. -- `RightPanel` es suficiente como primitive de superficie lateral y no debe renombrarse globalmente. -- `RdxContextPanel` está justificado como composición visual común sobre `RightPanel`. -- `RdxRowActions` está justificado para normalizar icon buttons, overflow, disabled reason y destructivas en tablas. -- `RdxEditorPageLayout`, `RdxPanel` y `RdxFormGrid` quedan confirmados tras migrar `customers/update` y `customers/create`. -- `RdxEditorPageLayout` también es apto para `proformas/update` cuando el `aside` contiene bloques secundarios ya existentes y el `main` conserva la única superficie editable. -- `RdxPanel` soporta tanto secciones planas de formulario como grupos secundarios del `aside` sin volver a `Card` por defecto. -- `RdxFormGrid` queda confirmado para campos en editores complejos, pero no debe envolver tablas o editores de líneas. -- `RdxEditorStickyFooter` está justificado para el patrón de editor complejo con una sola zona inferior persistente y sin duplicidad de acciones con el header. - -### DECISIÓN_PROVISIONAL - -- La extracción de una Page View pura es útil cuando reduce wiring y facilita tests sin controllers reales, pero no debe convertirse todavía en convención global. -- El patrón `main + aside` existe en ambos listados, pero hoy depende de `ResizablePanelGroup`, tamaños y apertura específicos de cada pantalla. -- El contrato final del footer contextual puede seguir afinándose cuando aparezcan paneles con resúmenes o acciones más heterogéneas. -- `RdxEditorPageLayout` ya cubre el editor simple, pero la convivencia visual con `PageFormHeader` debe seguir validándose antes de tocar el header legacy. -- la densidad final de la sticky zone inferior en móvil sigue requiriendo validación visual manual en múltiples resoluciones y tema oscuro. - -### NO_ABSTRAER_TODAVÍA - -- `RdxListPageLayout`: la geometría exterior ya queda resuelta por `RdxPageContainer`, `RdxPageStack` y la propia Page. -- `RdxResponsivePageGrid`: no encaja todavía como abstracción para listados con resize persistente. -- layouts superiores de listado mientras `ResizablePanelGroup` siga gobernando el split real. -- un layout superior específico de editor complejo mientras `RdxEditorPageLayout` + `RdxEditorStickyFooter` sigan cubriendo la geometría exterior. - ---- - -## 22. Criterios de aceptación - -La fase inicial se considerará correcta si: - -- `packages/rdx-ui` contiene los primeros componentes Rdx; -- los contratos son puramente visuales; -- no existe dependencia de routing, datos o dominio; -- `proformas/list` puede adoptarlos sin perder comportamiento; -- la documentación Markdown está actualizada; -- el UI Kit muestra los componentes y sus variantes; -- tema claro y oscuro están validados; -- los tests aplicables pasan; -- no se ha creado un framework CRUD; -- las clases estructurales repetidas disminuyen; -- la Page sigue componiendo y haciendo wiring explícito. - ---- - -## 23. Regla mental final - -```txt -Rdx define geometría, jerarquía y comportamiento visual. -La Page compone. -Los controllers orquestan. -La UI no interpreta dominio ni transporte. -``` - -Y como criterio de calidad: - -```txt -Si una abstracción ahorra pocas líneas pero empeora la lectura, no se usa. -Si un componente necesita conocer el caso de uso, no pertenece a rdx-ui. -Si un cambio visual no actualiza documentación y UI Kit, está incompleto. -``` diff --git a/docs/frontend/auth-and-company-selection.md b/docs/frontend/auth-and-company-selection.md deleted file mode 100644 index 98818a3f..00000000 --- a/docs/frontend/auth-and-company-selection.md +++ /dev/null @@ -1,215 +0,0 @@ -# Frontend Auth And Company Selection - -## Runtime actual - -El frontend ERP usa el runtime nuevo: - -```txt -@erp/identity/client -IdentityAuthSessionProvider -DataSourceProvider -Axios compartido -``` - -No debe usarse `@erp/auth/client` como runtime nuevo. - -## Flujo actual - -```txt -login --> GET /identity/auth/session --> GET /companies/available --> resolver empresa activa --> auto-selección si hay una sola empresa --> selector si hay varias --> /no-companies si no hay empresas --> X-Company-Id en rutas tenant-scoped -``` - -## Login - -El login usa: - -```http -POST /identity/auth/login -``` - -Tras login correcto: - -- se guardan `accessToken` y `refreshToken` -- se limpia cualquier `activeCompanyId` previo -- se carga `GET /companies/available` -- se resuelve el redirect autenticado según companies disponibles - -## Session restore - -Si existe `accessToken` persistido: - -1. se llama `GET /identity/auth/session` -2. se hidrata la sesión autenticada -3. se llama `GET /companies/available` -4. se resuelve `activeCompanyId` persistido o auto-selección - -Si la restauración falla: - -- se limpia la sesión -- se limpia `activeCompanyId` - -## Selección de empresa - -### Cero empresas - -Si `companies.length === 0`: - -- se muestra `/no-companies` -- no existe `activeCompanyId` -- no se envía `X-Company-Id` -- sigue disponible el logout - -### Una empresa - -Si `companies.length === 1`: - -- se selecciona automáticamente -- se guarda `activeCompanyId` -- no se muestra `/company-selection` -- se continúa al área privada - -### Varias empresas - -Si `companies.length > 1`: - -- se muestra `/company-selection` -- no existe `activeCompanyId` hasta elegir -- el usuario selecciona company -- se guarda `activeCompanyId` -- se continúa al área privada - -### `activeCompanyId` persistido ya no disponible - -Si el `activeCompanyId` persistido ya no aparece en `GET /companies/available`: - -- con `0` companies -> `/no-companies` -- con `1` company -> auto-selección de la disponible -- con varias companies -> `/company-selection` - -## `GET /companies/available` - -Endpoint actual: - -```http -GET /companies/available -Authorization: Bearer -``` - -No requiere: - -- `X-Company-Id` - -No usa: - -- `requireIdentityTenant(params)` - -Devuelve: - -```ts -export type AvailableCompaniesResponseDTO = { - companies: AvailableCompanyDTO[]; -}; -``` - -## App company switcher - -Componente documentado: - -```txt -apps/web/src/layout/app-company-switcher.tsx -``` - -Propósito: - -- cambiar de empresa activa una vez iniciada la sesión -- mostrar la empresa activa actual dentro del layout autenticado - -Reglas: - -- consume `availableCompanies` y `activeCompany` desde `useIdentityAuthSession()` -- usa `changeActiveCompany()` o `selectActiveCompany()` del contexto, no requests directas -- no llama directamente a `GET /companies/available` si el contexto ya está hidratado -- no maneja tokens -- no usa `@erp/auth/client` -- no usa `companySlug` para autenticación -- no modifica el access token -- al cambiar empresa, las siguientes requests tenant-scoped deben salir con el nuevo `X-Company-Id` - -Comportamiento esperado: - -- `0` empresas: no rompe; render deshabilitado o equivalente -- `1` empresa: muestra la empresa activa; no hace falta selector operativo -- `N` empresas: permite cambiar entre las permitidas y marca la activa - -## Headers - -### No deben llevar `X-Company-Id` - -```txt -POST /identity/auth/login -POST /identity/auth/refresh -POST /identity/auth/logout -GET /identity/auth/session -GET /companies/available -``` - -### Sí deben llevar `X-Company-Id` - -Rutas tenant-scoped como: - -- `/customers` -- `/catalogs/*` -- `/customer-invoices/*` -- `/factuges` - -siempre que exista `activeCompanyId`. - -## Refresh automático sobre `401` - -El frontend implementa refresh automático global sobre Axios. - -Comportamiento: - -1. una request autenticada recibe `401` -2. si existe `refreshToken`, se llama `POST /identity/auth/refresh` -3. se guardan los nuevos tokens -4. se reintenta una única vez la request original -5. si refresh falla, se limpia la sesión y se redirige a `/login` - -## Reglas implementadas - -- retry único por request con `_retry` -- refresh concurrente único con `refreshPromise` compartida -- varias requests `401` esperan el mismo refresh -- login `401` no dispara refresh -- refresh `401` no intenta refrescarse a sí mismo -- `/identity/auth/refresh` no lleva `X-Company-Id` -- la request tenant-scoped reintentada conserva `X-Company-Id` - -## Endpoints excluidos de tenant header y refresh - -```txt -/identity/auth/login -/identity/auth/refresh -/identity/auth/logout -/identity/auth/session -/companies/available -``` - -## Pruebas manuales recomendadas - -1. Login con usuario sin empresas: debe ir a `/no-companies`. -2. Login con usuario con una empresa: debe auto-seleccionarse y entrar al área privada. -3. Login con usuario con varias empresas: debe mostrarse `/company-selection`. -4. Cambio de empresa desde `app-company-switcher`: la request tenant-scoped siguiente debe llevar el nuevo `X-Company-Id`. -5. Access token expirado con refresh válido: debe ejecutarse refresh + retry único. -6. Refresh token inválido: debe limpiarse la sesión y redirigir a `/login`. -7. Endpoints auth y `GET /companies/available`: no deben llevar `X-Company-Id`. -8. Rutas tenant-scoped: deben llevar `X-Company-Id` cuando exista `activeCompanyId`. diff --git a/docs/frontend/diseno-cuadros-dialogo.md b/docs/frontend/diseno-cuadros-dialogo.md deleted file mode 100644 index 35aecd00..00000000 --- a/docs/frontend/diseno-cuadros-dialogo.md +++ /dev/null @@ -1,194 +0,0 @@ -# Diseño de cuadros de diálogo de formulario - -## Estado - -Este documento es la fuente de verdad para diseñar, implementar y revisar cuadros de diálogo que recogen o modifican datos en el frontend del ERP. - -Todo diálogo nuevo de creación, edición o configuración debe seguir esta guía, salvo que una necesidad funcional justifique y documente una excepción. - -## Referencia canónica - -La referencia productiva es el diálogo para crear una proforma recurrente desde una proforma: - -- componente: `modules/customer-invoices/src/web/proformas/detail/ui/components/create-recurring-proforma-from-proforma-dialog.tsx`; -- controller: `modules/customer-invoices/src/web/proformas/detail/controllers/use-create-recurring-proforma-from-proforma-dialog-controller.ts`; -- prueba de renderizado: `modules/customer-invoices/src/web/proformas/detail/ui/components/create-recurring-proforma-from-proforma-dialog.test.tsx`. - -La guía no obliga a reutilizar ese componente. Obliga a mantener su jerarquía visual, separación de responsabilidades y comportamiento accesible. - -## Cuándo aplicar este patrón - -Usar `Dialog` para una tarea breve y focalizada que: - -- se inicia desde la pantalla actual; -- requiere pocos datos para completar una única acción; -- no necesita una ruta propia ni navegación lateral; -- permite volver al contexto sin perder orientación. - -No usarlo para: - -- confirmaciones o acciones destructivas sin formulario: usar `AlertDialog`; -- ediciones documentales complejas, varios pasos o trabajo prolongado: usar una página de edición; -- vistas de solo lectura: usar un diálogo de consulta o el patrón existente del área. - -## Estructura visual obligatoria - -Un diálogo de formulario se organiza de arriba abajo en cuatro zonas: - -1. Cabecera diferenciada. -2. Campo de identidad destacado cuando exista, normalmente el nombre. -3. Bloques de información agrupados por intención. -4. Pie de acciones separado del contenido. - -```tsx - - - {title} - {description} - - -
- - - - - - - - - -
-``` - -No añadir una zona si no aporta información o una acción real. La estructura debe mejorar la lectura, no convertir un formulario pequeño en una pantalla artificialmente compleja. - -## 1. Cabecera diferenciada - -La cabecera comunica qué se va a hacer antes de pedir datos. - -- Incluir título directo, orientado a la acción: `Crear plazo de pago`, `Modificar método de pago`. -- Incluir una descripción breve que explique el resultado o el contexto de origen. -- Separarla visualmente del formulario mediante borde inferior y fondo sutil del tema (`bg-muted/40`). -- Mantener los márgenes internos alineados con contenido y pie. -- No colocar acciones de formulario en la cabecera; el cierre estándar del diálogo se mantiene disponible. - -## 2. Campo de identidad destacado - -Cuando el recurso tenga un nombre, código o referencia que ayude a reconocerlo, ese campo va inmediatamente después de la cabecera. - -- Usar el nombre como primer campo si es obligatorio para crear o identificar el recurso. -- Dar al control una presencia moderada (`h-11` y fondo de formulario) sin crear un segundo título. -- Mantener el error de validación junto al campo. -- No destacar campos técnicos, identificadores internos ni valores que el usuario no pueda modificar. - -Si el diálogo no tiene un campo de identidad relevante, el primer bloque de información pasa a ocupar esa posición. - -## 3. Bloques de información - -Agrupar campos que responden a la misma pregunta del usuario. Por ejemplo: periodo de vigencia, recurrencia, condiciones comerciales o destinatario. - -Cada bloque debe: - -- usar un `section` con `aria-labelledby`; -- tener un título visible y conciso; -- mostrar un icono solo cuando ayude a identificar el tipo de información; -- usar borde, radio y fondo sutil para delimitarlo sin competir con la acción principal; -- contener entre uno y seis controles relacionados por norma general. - -Patrón base: - -```tsx -
-
- - - -

{sectionTitle}

-
- -
- - -
-
-``` - -Reglas de composición: - -- En escritorio, los campos de igual importancia pueden compartir dos columnas. -- En móvil, las columnas deben apilarse de forma natural. Usar `sm:grid-cols-2`, nunca una anchura fija. -- Añadir `min-w-0` a las columnas cuando puedan contener textos localizados, selectores o controles flexibles. -- Los selectores e inputs ocupan todo el ancho de su columna. -- Un campo condicional se muestra dentro del bloque al que pertenece y se separa con borde superior si necesita distinguirse del resto. -- Los interruptores y checkboxes que afectan a otro campo deben vivir junto a ese campo, no en una fila independiente sin contexto. - -## 4. Pie de acciones - -El pie mantiene las decisiones finales fuera de los datos. - -- Separarlo mediante borde superior y fondo sutil (`bg-muted/30`). -- Colocar `Cancelar` como acción secundaria y la acción de envío como botón principal. -- Durante el envío, deshabilitar ambos botones y todos los controles editables. -- El botón principal debe reflejar el estado de progreso con texto o icono accesible. -- Asociar el botón de envío al `form` mediante su atributo `form` cuando el pie quede fuera del elemento `form`. - -## Accesibilidad, validación e i18n - -- Usar `DialogTitle` y `DialogDescription` siempre que el diálogo tenga contenido operativo. -- Asociar cada `Label` a un control mediante `htmlFor` e `id`. -- Los títulos de bloque usan `section` y `aria-labelledby`. -- Mantener los errores junto al control que los genera; no depender únicamente de avisos globales. -- Todo texto visible procede de i18n y debe existir en `es` y `en`. -- Los iconos decorativos usan `aria-hidden`; nunca sustituyen una etiqueta textual. -- No bloquear el cierre por teclado salvo mientras exista una operación de envío en curso. - -## Responsabilidades del código - -El componente de UI compone el diálogo y recibe estado ya preparado. No debe contener navegación, peticiones HTTP ni reglas de negocio. - -El controller del diálogo concentra: - -- valores iniciales y reinicio del formulario; -- validación de formulario; -- llamada al hook de mutación; -- estado de envío; -- cierre, notificaciones y navegación tras un resultado correcto. - -Las operaciones remotas permanecen en hooks de TanStack Query. Los DTOs de petición siguen siendo la fuente de verdad del formulario y de la validación. - -## Pruebas mínimas - -Cada diálogo nuevo o modificado debe cubrir, como mínimo: - -- título, descripción y campo de identidad cuando exista; -- campos obligatorios y campos condicionales; -- acciones de cancelar y enviar; -- estado de envío; -- traducciones reales o una prueba que compruebe que las claves usadas existen en el catálogo; -- resultado correcto y error de la mutación en el controller. - -No usar un diccionario de traducciones manual en el test si el objetivo es verificar que el catálogo productivo contiene las claves consumidas por el componente. - -## Lista de comprobación - -Antes de cerrar un cambio, confirmar: - -- La tarea cabe realmente en un diálogo. -- La cabecera explica la acción y está diferenciada del formulario. -- El nombre o campo de identidad aparece antes de los bloques de configuración. -- Cada bloque agrupa una única intención comprensible. -- El layout se apila correctamente en móvil. -- Los textos, estados disabled, errores y carga son accesibles. -- Existen traducciones en `es` y `en`. -- Las pruebas y la validación de formato se han ejecutado desde Linux. - -## Excepciones - -Una excepción debe justificarse en la revisión del cambio indicando: - -- qué requisito funcional impide usar esta estructura; -- qué patrón alternativo se emplea; -- cómo se mantiene la accesibilidad y el comportamiento responsive; -- qué pruebas cubren la variante. - -La preferencia visual local, copiar un diálogo antiguo o ahorrar un bloque de composición no son motivos válidos para omitir esta guía. diff --git a/docs/frontend/diseno-pantallas-listado.md b/docs/frontend/diseno-pantallas-listado.md deleted file mode 100644 index cb71a299..00000000 --- a/docs/frontend/diseno-pantallas-listado.md +++ /dev/null @@ -1,166 +0,0 @@ -# Diseño de pantallas de listado - -## Estado - -Este documento es la fuente de verdad para diseñar, implementar y revisar pantallas de listado del frontend del ERP. - -Toda pantalla de listado nueva debe seguir esta guía salvo que exista una excepción funcional documentada antes de implementar el cambio. Las pantallas existentes se alinearán de forma incremental cuando se modifiquen. - -## Referencia canónica - -La referencia productiva es el listado de proformas: - -- página: `modules/customer-invoices/src/web/proformas/list/ui/pages/list-proformas-page.tsx`; -- controller de página: `modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts`; -- controller de datos: `modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts`; -- vista de shell: `modules/customer-invoices/src/web/proformas/list/ui/pages/scoped-list-proformas-page-view.tsx`. - -No se exige igualdad textual ni reutilización forzada de componentes de dominio. Se exige conservar la misma separación de responsabilidades y la misma jerarquía visual. - -## Estructura visual obligatoria - -Una página de listado se compone, por defecto, así: - -```tsx - - - } - /> - - {refreshError ? : null} - - - - {isEmpty ? : null} - - -``` - -Reglas: - -- `RdxPageHeader` vive dentro de `RdxPageStack`, no como un `header` HTML paralelo. -- Las acciones principales se presentan en el header mediante `RdxPageActions` o el mecanismo equivalente del shell existente. -- La búsqueda, filtros y controles de tabla viven en `DataTable.topContent` y preceden a la tabla. -- La tabla, el estado vacío y el error de refresco permanecen dentro del mismo stack para evitar saltos de layout. -- Debe respetarse el spacing del shell; no añadir wrappers de anchura, márgenes o stacks sin una necesidad de contenido demostrable. -- El CTA de creación se mantiene visible cuando hay datos, carga, vacío o un error de refresco. Sólo desaparece con un error inicial bloqueante. - -La pantalla puede usar una vista de composición específica del dominio, como `ScopedListProformasPageView`, cuando encapsule exclusivamente el shell anterior. No debe duplicar ese JSX en cada lista. - -## Ownership del controller de página - -Cada listado debe tener un controller de presentación cuando su página coordine consulta, URL, filtros, paginación o navegación. Su nombre sigue el patrón: - -```text -use-list--page-controller.ts -useListPageController -``` - -El controller de página concentra: - -- lectura y escritura de `searchParams` relevantes; -- estado de búsqueda y su debounce, si existe; -- filtros, orden y paginación server-side; -- composición del hook de datos existente de TanStack Query; -- adaptación mínima de resultados para la tabla; -- view state de carga, error inicial bloqueante y error durante refresco; -- callbacks de reintento, creación y navegación a detalle; -- inputs y handlers mínimos que necesita la página. - -La página queda declarativa: define columnas y compone el shell, el toolbar y la tabla con los datos y callbacks del controller. No debe tener `useSearchParams`, llamar directamente al hook de listado ni construir rutas de navegación desde eventos de la tabla. - -El controller no contiene reglas de dominio, cálculos de negocio, fetch manual, lógica de backend ni abstracciones genéricas de listados. Reutiliza los query hooks y query keys del dominio. - -## Consulta, filtros y paginación - -- Mantener la consulta remota en TanStack Query. -- Mantener paginación server-side mediante `manualPagination`, `pageIndex`, `pageSize`, `totalItems` y los callbacks correspondientes. -- Cambiar búsqueda, filtro, orden o tamaño de página actualiza la URL con la misma semántica del recurso y reinicia la página cuando corresponda. -- No duplicar datos remotos en estado local. -- No añadir filtros, columnas ni acciones por motivos visuales. -- Los filtros segmentados reutilizan el patrón visual de `ProformaStatusSegmentedFilter`, pero son componentes de dominio distintos si sus estados, traducciones o datos difieren. -- Nunca mostrar contadores globales de estado que el endpoint no proporciona. No se calculan a partir de una página parcial de resultados. - -## Tabla y navegación - -- Conservar columnas, orden, formato y acciones ya productivas salvo que la tarea cambie explícitamente su funcionalidad. -- El enlace de una celda y el click de fila deben llevar al mismo detalle cuando ambos existan. -- `DataTable.onRowClick` recibe el objeto de fila, no un identificador. La página lo adapta explícitamente: - -```tsx -onRowClick={(row) => controller.openDetailPage(row.id)} -``` - -- No pasar un callback que espera `id` directamente a `DataTable.onRowClick`; produciría rutas como `[object Object]`. -- La navegación de detalle y creación se define en el controller de página. La página sólo conecta el evento con el callback. -- No introducir N+1 para resolver nombres o datos de columnas. Si un dato no está en el contrato del listado, debe resolverse mediante una evolución explícita y justificada del contrato. - -## Estados de interfaz - -La lista distingue tres situaciones: - -| Situación | Comportamiento | -| --- | --- | -| Carga inicial | La tabla recibe su estado `loading`; se mantiene el shell de página. | -| Error inicial sin datos utilizables | Mostrar `PageErrorState` dentro de `RdxPageContainer`, con reintento. | -| Error al refrescar con datos utilizables | Mantener tabla y filtros; mostrar `ErrorAlert` inline en el stack. | - -Además: - -- el empty state se muestra cuando la respuesta utilizable tiene cero elementos; -- no crear un segundo sistema de skeleton, alertas o errores si ya existen `DataTable`, `PageErrorState` y `ErrorAlert`; -- las acciones deben reflejar loading y disabled cuando el contrato del componente lo permita; -- no ocultar acciones deshabilitadas si la UX requiere que sigan siendo visibles. - -## Accesibilidad e i18n - -- Los filtros tienen etiqueta accesible y controles navegables por teclado. -- El click de fila debe conservar activación con `Enter` y espacio provista por `DataTable`. -- No insertar textos visibles directamente: añadir y mantener traducciones en `es` y `en` cuando hagan falta. -- Mantener `tabular-nums` en valores monetarios y fechas numéricas cuando el patrón de tabla lo use. - -## Pruebas mínimas - -Al crear o refactorizar un listado, adaptar o añadir pruebas funcionales de página que cubran: - -- presencia y navegación del CTA de creación; -- búsqueda; -- filtro de estado u otros filtros ya existentes; -- paginación server-side; -- carga inicial, error bloqueante, error de refresco y vacío; -- renderizado de filas y formatos sensibles, como fechas sin zona horaria; -- navegación a detalle desde fila; -- que el callback de `DataTable.onRowClick` recibe una fila y usa su `id`. - -Los tests no deben depender de detalles internos irrelevantes del controller ni sustituir contratos de tabla por tipos incorrectos. - -## Validación obligatoria - -Antes de cerrar un cambio de listado, ejecutar en Linux y desde `/home/rodax/Documentos/uecko-erp`: - -```bash -pnpm test:frontend -pnpm --filter typecheck -pnpm --filter lint -git diff --check -git status --short -``` - -Si typecheck o lint fallan por problemas preexistentes ajenos, ejecutar también Biome sobre los archivos modificados y comunicar el fallo global sin atribuirlo al cambio. - -## Excepciones - -Una excepción requiere dejar constancia en la revisión o en documentación del área con: - -- el requisito funcional que impide seguir esta guía; -- el impacto visual o arquitectónico; -- el patrón alternativo elegido; -- las pruebas que lo cubren. - -No son excepciones válidas la preferencia local, la rapidez de implementación, copiar una pantalla antigua o evitar extraer lógica de una página monolítica. diff --git a/docs/frontend/editor-layout-audit.md b/docs/frontend/editor-layout-audit.md deleted file mode 100644 index 458255e8..00000000 --- a/docs/frontend/editor-layout-audit.md +++ /dev/null @@ -1,274 +0,0 @@ -# Auditoría De Layouts De Editores - -Fecha de auditoría: 18 de agosto de 2026. - -## Objetivo - -Inventariar los editores reales del frontend del ERP y clasificarlos respecto a la norma visual vigente de edición focalizada. - -Esta auditoría: - -- inventaría rutas, páginas y dialogs de edición activos; -- clasifica cada caso por tipo; -- evalúa el layout actual frente a la norma; -- detecta desviaciones y excepciones justificadas; -- propone una secuencia pequeña de migración. - -Esta auditoría no migra masivamente las pantallas. - -## Norma visual de referencia - -SSOT vigente: - -- `packages/rdx-ui/docs/page-layouts.md` -- `packages/rdx-ui/docs/contributing-ui.md` - -Resumen operativo: - -- los editores de página completa deben usar por defecto shell `app-fullscreen`; -- la navegación mínima sale por `PageFormHeader` o un header de dominio equivalente; -- el contenido del editor se centra mediante `RdxEditorPageLayout`; -- `complexity="simple"` aplica a formularios focalizados centrados; -- `complexity="complex"` aplica a documentos o editores con `main + aside`, bloques especializados y footer sticky; -- los dialogs e inline editors no deben migrarse automáticamente a fullscreen. - -## Método de investigación - -Búsquedas y fuentes revisadas: - -- rutas y layouts en `apps/web/src/routes/app-routes.tsx` y `modules/*/src/web/*routes.tsx` -- contratos de layout en `modules/core/src/web/lib/modules/module-client.interface.ts` -- primitivas Rdx en: - - `packages/rdx-ui/src/components/editor-page/rdx-editor-page-layout.tsx` - - `packages/rdx-ui/src/components/editor-page/rdx-editor-sticky-footer.tsx` - - `modules/core/src/web/components/form/form-actions/page-form-header.tsx` - - `apps/web/src/layout/app-fullscreen-layout.tsx` - - `apps/web/src/layout/app-sidebar-layout.tsx` -- módulos revisados: - - `identity` - - `customers` - - `customer-invoices` - - `companies` - - `catalogs` - - `document-series` - - `supplier` -- búsquedas textuales sobre: - - `layout: "app-fullscreen"` - - `layout: "app-sidebar"` - - `RdxEditorPageLayout` - - `PageFormHeader` - - `RdxEditorStickyFooter` - - `create` - - `update` - - `edit` - - `Dialog` - -## Inventario - -| Module | Feature | Route | Type | Current layout | Rdx editor layout | Compliance | Risk | Priority | Recommended action | Evidence | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| `identity` | Account self-service | `/account` | `PAGE_SIMPLE` | `app-fullscreen` | `simple` | `COMPLIANT` | `LOW` | `-` | Ninguna. Mantener como referencia de editor simple focalizado. | `modules/identity/src/web/account-routes.tsx`, `modules/identity/src/web/account/ui/pages/account-page.tsx` | -| `identity` | Change password dialog | Sin ruta propia. Dialog dentro de `/account` | `DIALOG` | Host `app-fullscreen` | No aplica | `COMPLIANT` | `LOW` | `-` | Ninguna. Mantener como dialog de edición contenida. | `modules/identity/src/web/account/ui/pages/account-page.tsx` | -| `customers` | Create customer | `/customers/create` | `PAGE_SIMPLE` | `app-fullscreen` | `simple` | `COMPLIANT` | `LOW` | `-` | Ninguna. Referencia vigente de editor simple. | `modules/customers/src/web/customer-routes.tsx`, `modules/customers/src/web/create/ui/pages/customer-create-page.tsx` | -| `customers` | Update customer | `/customers/:id/edit` | `PAGE_SIMPLE` | `app-fullscreen` | `simple` | `COMPLIANT` | `LOW` | `-` | Ninguna. Referencia vigente de editor simple. | `modules/customers/src/web/customer-routes.tsx`, `modules/customers/src/web/update/ui/pages/customer-update-page.tsx` | -| `customers` | Quick create customer | Sin ruta propia. Dialog reutilizado desde proformas | `DIALOG` | Host `app-fullscreen` | No aplica | `COMPLIANT` | `LOW` | `-` | Ninguna. Mantenerlo como creación rápida embebida. | `modules/customers/src/common/features/quick-create-customer/ui/dialogs/quick-create-customer-dialog.tsx` | -| `companies` | Company details settings | `/settings/company/details` | `PAGE_SIMPLE` | `app-sidebar` | No usa `RdxEditorPageLayout` | `EXCEPTION_JUSTIFIED` | `MEDIUM` | `P3` | Mantener en settings por ahora. Revaluar solo si se define una variante focalizada específica para formularios de settings. | `modules/companies/src/web/company-routes.tsx`, `modules/companies/src/web/settings/company-details/ui/pages/company-details-settings-page.tsx` | -| `customer-invoices` | Verifactu settings | `/settings/billing/verifactu` | `PAGE_SIMPLE` | `app-sidebar` | No usa `RdxEditorPageLayout` | `EXCEPTION_JUSTIFIED` | `MEDIUM` | `P3` | Mantener en settings por ahora. Revaluar junto con una política específica de formularios largos dentro de settings. | `modules/customer-invoices/src/web/customer-invoice-routes.tsx`, `modules/customer-invoices/src/web/settings/verifactu/ui/pages/company-verifactu-settings-page.tsx` | -| `customer-invoices` | Create proforma | `/proformas/create` | `PAGE_SIMPLE` | `app-fullscreen` | `simple` | `COMPLIANT` | `MEDIUM` | `-` | Ninguna. Mantener como bootstrap inicial de proforma hacia el editor documental completo. | `modules/customer-invoices/src/web/customer-invoice-routes.tsx`, `modules/customer-invoices/src/web/proformas/create/ui/pages/proforma-create-page.tsx`, `modules/customer-invoices/src/web/proformas/create/ui/blocks/proforma-create-form.tsx` | -| `customer-invoices` | Update proforma | `/proformas/:id/edit` | `PAGE_COMPLEX` | `app-fullscreen` | `complex` | `COMPLIANT` | `HIGH` | `-` | Ninguna. Mantener como referencia vigente de editor complejo. | `modules/customer-invoices/src/web/customer-invoice-routes.tsx`, `modules/customer-invoices/src/web/proformas/update/ui/pages/proforma-update-page.tsx`, `modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-editor-form.tsx` | -| `customer-invoices` | Change proforma status | Sin ruta propia. Dialog usado desde listado y detalle | `DIALOG` | Host mixto (`app-sidebar` o `app-fullscreen`) | No aplica | `COMPLIANT` | `LOW` | `-` | Ninguna. Es una edición contenida por dialog, no candidata a fullscreen. | `modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx`, `modules/customer-invoices/src/web/proformas/list/ui/pages/list-proformas-page.tsx`, `modules/customer-invoices/src/web/proformas/detail/ui/pages/proforma-detail-page.tsx` | -| `catalogs` | Payment methods create/update | Host: `/settings/general/payment-methods` | `DIALOG` | Host `app-sidebar` | No aplica | `COMPLIANT` | `LOW` | `-` | Ninguna. Mantener como edición contenida en dialog dentro de settings. | `modules/catalogs/src/web/catalogs-routes.tsx`, `modules/catalogs/src/web/settings/payment-methods/ui/pages/payment-methods-settings-page.tsx`, `modules/catalogs/src/web/settings/payment-methods/ui/components/payment-method-form-dialog.tsx` | -| `catalogs` | Payment terms create/update | Host: `/settings/general/payment-terms` | `DIALOG` | Host `app-sidebar` | No aplica | `COMPLIANT` | `LOW` | `-` | Ninguna. Mantener como dialog aunque su contenido interno sea más rico. | `modules/catalogs/src/web/catalogs-routes.tsx`, `modules/catalogs/src/web/settings/payment-terms/ui/pages/payment-terms-settings-page.tsx`, `modules/catalogs/src/web/settings/payment-terms/ui/components/payment-term-form-dialog.tsx` | -| `catalogs` | Tax regimes create/update | Host: `/settings/general/tax-regimes` | `DIALOG` | Host `app-sidebar` | No aplica | `COMPLIANT` | `LOW` | `-` | Ninguna. Mantener como dialog de edición contenida. | `modules/catalogs/src/web/catalogs-routes.tsx`, `modules/catalogs/src/web/settings/tax-regimes/ui/pages/tax-regimes-settings-page.tsx`, `modules/catalogs/src/web/settings/tax-regimes/ui/components/tax-regime-form-dialog.tsx` | -| `customer-invoices` | Issued invoices create/edit references | Referencias a `/issued-invoices/create` y `/customer-invoices/:id/edit`, sin ruta activa | `UNCLEAR` | Sin ruta activa | No aplica | `UNKNOWN` | `MEDIUM` | `P2` | Aclarar si esas rutas deben existir o limpiar referencias muertas antes de cualquier trabajo visual. | `modules/customer-invoices/src/web/issued-invoices/list/ui/pages/list-issued-invoices-page.tsx`, `modules/customer-invoices/src/web/issued-invoices/list/ui/blocks/issued-invoices-grid/issued-invoices-grid.tsx`, `modules/customer-invoices/src/web/customer-invoice-routes.tsx` | -| `document-series` | Frontend editor activo | No detectado | `LEGACY_OR_UNUSED` | Sin route frontend | No aplica | `UNKNOWN` | `LOW` | `P3` | Confirmar si el módulo tendrá UI activa antes de cualquier migración de layout. | Sin `src/web/*routes.tsx` ni páginas frontend activas en `modules/document-series` | -| `supplier` | Frontend editor activo | No detectado | `LEGACY_OR_UNUSED` | Sin route frontend | No aplica | `UNKNOWN` | `LOW` | `P3` | Resolver primero si el módulo se reactiva o se limpia; no priorizar migración visual. | Sin `src/web/*routes.tsx`; solo `modules/supplier/src/web/shared/*` | - -## Editores conformes - -Conformes respecto a la norma vigente: - -- `customers/create` -- `customers/update` -- `/account` -- `proformas/:id/edit` - -Motivos comunes observados: - -- ruta bajo `app-fullscreen`; -- navegación mínima por `PageFormHeader` o header de dominio equivalente; -- uso explícito de `RdxEditorPageLayout`; -- geometría coherente con `simple` o `complex` según caso; -- acciones de salida/guardado visibles y no delegadas al sidebar. - -## Desviaciones - -### 1. `proformas/create` - -HECHO OBSERVADO - -- la ruta usa `app-fullscreen`; -- la página usa header de edición y `RdxEditorPageLayout complexity="simple"`; -- el flujo de alta actual solo captura cabecera inicial y deriva después al editor documental completo; -- no existe todavía editor de líneas, `aside`, totales sticky ni footer persistente equivalentes a `proformas/:id/edit`. - -IMPACTO - -- el create queda alineado con el patrón focalizado simple sin fingir que ya es el editor documental completo; -- la diferencia visual restante frente a `update` responde a una diferencia funcional real y no a drift accidental de layout. - -DECISIÓN - -- `PAGE_SIMPLE`; -- `COMPLIANT`. - -### 2. Referencias a create/edit de issued invoices sin ruta activa - -HECHO OBSERVADO - -- hay referencias a `/issued-invoices/create` y a `/customer-invoices/:id/edit`; -- `customer-invoice-routes.tsx` no declara esas rutas activas. - -IMPACTO - -- el inventario de editores queda ambiguo en esa área; -- puede existir deuda de navegación muerta o una funcionalidad incompleta; -- no conviene planificar migración visual ahí hasta aclarar si hay editor real. - -RECOMENDACIÓN - -- confirmar si esas rutas deben implementarse; -- si no, limpiar referencias muertas antes de meterlas en cualquier backlog de layouts. - -## Excepciones justificadas - -### Company details settings - -HECHO OBSERVADO - -- es un formulario de página completa; -- vive bajo `/settings/company/details`; -- depende del shell de settings con `app-sidebar`. - -INFERENCIA - -- la navegación lateral de settings forma parte funcional de la tarea y hoy actúa como mecanismo primario de cambio de subsección. - -DECISIÓN DE AUDITORÍA - -- `EXCEPTION_JUSTIFIED` por ahora; -- no priorizar fullscreen salvo que el sistema de settings evolucione con una variante focalizada explícita. - -### Verifactu settings - -HECHO OBSERVADO - -- es un formulario de configuración largo; -- vive bajo `/settings/billing/verifactu`; -- comparte patrón de settings con shell lateral. - -INFERENCIA - -- igual que en `company/details`, la tarea sigue integrada en navegación de settings y no en un editor aislado. - -DECISIÓN DE AUDITORÍA - -- `EXCEPTION_JUSTIFIED` por ahora; -- revisar sólo si se aborda una política transversal de formularios largos dentro de settings. - -## Dialogs e inline editors - -Dialogs correctamente clasificados y no candidatos automáticos a fullscreen: - -- `QuickCreateCustomerDialog` -- `PaymentMethodFormDialog` -- `PaymentTermFormDialog` -- `TaxRegimeFormDialog` -- dialog de cambio de contraseña de `/account` -- `ChangeProformaStatusDialog` - -No se detectaron rutas independientes que deban reclasificarse desde `DIALOG` o `INLINE` a `PAGE_*`. - -No se detectaron inline editors de entidad de primer nivel con route propia. Sí existen bloques especializados internos, como el editor de líneas de proformas, pero forman parte de un `PAGE_COMPLEX` y no deben inventariarse como pantalla independiente. - -## Incoherencias create/update - -Agregados revisados: - -- `customers` - - `create` y `update` comparten patrón y están alineados. -- `proformas` - - `create` usa `RdxEditorPageLayout complexity="simple"` porque hoy es un bootstrap inicial; - - `update` usa `RdxEditorPageLayout complexity="complex"` porque ya es el editor documental completo con `main`, bloques especializados y footer sticky. - -Conclusión: - -- la diferencia create/update restante es funcional legítima: `create` todavía no edita el documento completo y `update` sí. - -## Riesgos de migración - -Riesgos bajos: - -- `customers/create` -- `customers/update` -- `/account` - -Riesgos medios: - -- `proformas/create` por convivencia con quick-create/select customer dialogs y porque el flujo actual parece de bootstrap inicial, no de edición documental completa; -- `company/details` y `verifactu` si se intentara moverlos fuera del shell settings. - -Riesgos altos: - -- `proformas/:id/edit`, aunque hoy ya cumple. Cualquier cambio allí impacta: - - scroll complejo - - footer sticky - - totales embebidos - - `aside` - - dialogs anidados - -## Plan incremental - -### Fase A - -- completada: migración de `proformas/create` - -Motivo: - -- era el único `PAGE_*` activo no plenamente alineado con el patrón Rdx; -- ya usaba `app-fullscreen`, así que el salto era incremental; -- permite distinguir mejor entre bootstrap inicial y editor documental completo del agregado Proforma. - -### Fase B - -- revisar si `company/details` y `verifactu` deben seguir como excepción de settings o si merece una variante focalizada específica para formularios largos dentro de settings - -Motivo: - -- ahora mismo la excepción parece razonable; -- no conviene forzar fullscreen sin una decisión explícita sobre el sistema de settings. - -### Fase C - -- aclarar `issued-invoices` create/edit - -Motivo: - -- primero hay que determinar si existe editor real o solo referencias muertas. - -### Fase D - -- hacer una auditoría UX más fina de dialogs ricos, empezando por `payment-terms` - -Motivo: - -- no es un incumplimiento de fullscreen; -- sí es el dialog más complejo de los auditados y puede requerir reglas específicas de consistencia interna. - -## No cambios - -Durante esta auditoría: - -- no se modificaron rutas ni layouts runtime de `customers`, `companies`, `customer-invoices`, `catalogs`, `identity`, `document-series` o `supplier`; -- no se tocaron hooks, controllers, formularios, React Query ni backend de los editores auditados; -- el único cambio de código del turno es este documento operativo de auditoría. diff --git a/docs/frontend/frontend-architecture.md b/docs/frontend/frontend-architecture.md new file mode 100644 index 00000000..f8175b41 --- /dev/null +++ b/docs/frontend/frontend-architecture.md @@ -0,0 +1,133 @@ +# Arquitectura frontend + +> **Propósito:** explicar cómo está construido el frontend. Este documento, junto con [UI system](./ui-system.md) y [UI patterns](./ui-patterns.md), forma la fuente de verdad (SSOT) de frontend/UI. + +## 1. Stack + +El frontend usa React 19, TypeScript estricto, React Router, TanStack Query, React Hook Form, Axios, Tailwind CSS v4, Shadcn/UI y `@repo/rdx-ui`. + +## 2. Arquitectura por capas + +El flujo habitual de una feature es: + +```text +API → DTO → adapter → Entity → query hook → controller → page controller → Page → UI +``` + +- `shared/api`: transporte y contratos HTTP. +- `shared/adapters`: transforma DTOs en entidades de frontend. +- `shared/entities`: modelos consumidos por la aplicación. +- `shared/hooks`: queries, mutations y query keys de TanStack Query. +- `controllers`: orquestación funcional, formularios, navegación y efectos. +- `page controllers`: componen el estado completo de una pantalla. +- `Page`: conecta controller y componentes; debe tener poco estado propio. +- `ui`: renderiza props preparadas y emite eventos. + +La UI no debe contener reglas de dominio, fetch manual ni traducciones entre DTO y entidad. Los controllers no deben convertirse en servicios de dominio. + +## 3. Módulos frontend + +Cada módulo publica sus rutas y su cliente desde `modules//src/web`. Las features se organizan por caso de uso (`list`, `create`, `update`, `detail`) y comparten transporte, entidades y hooks mediante `shared` cuando existe reutilización real. + +Las dependencias deben apuntar hacia contratos compartidos, no hacia páginas concretas. Un componente de dominio puede envolver una foundation visual si aporta defaults o vocabulario propios, pero no debe recrear el layout de página. + +## 4. Routing y layouts + +Las rutas declaran su shell en `handle.layout`: + +- `auth`: login y recuperación, bajo `AuthLayout`. +- `app-sidebar`: dashboard, listados, settings y navegación general. +- `app-fullscreen`: tareas focalizadas de create/update/detail y Account cuando la ruta así lo declara. + +`apps/web/src/routes/app-routes.tsx` agrupa las rutas por ese valor y las monta bajo el layout correspondiente. `protected` y `requireCompany` expresan guards independientes del shell. La elección debe seguir las rutas productivas del área; no se infiere únicamente por el nombre de la pantalla. + +## 5. Shells de aplicación + +```text +Application shell +├── AppSidebarLayout +│ ├── AppSidebar +│ ├── AppTopbar +│ └── AppMain +├── AppFullscreenLayout +│ └── AppMain +└── AuthLayout +``` + +- `AppSidebarLayout` proporciona navegación y topbar. +- `AppFullscreenLayout` ocupa el viewport para una tarea focalizada. +- `AuthLayout` centra las pantallas públicas de identidad. +- `AppMain` entrega el espacio disponible. No es propietario del scroll de una página. + +Dentro de `AppMain`, una pantalla usa `RdxPageFrame`; el sistema visual y sus reglas se documentan en [UI system](./ui-system.md). + +## 6. Identity y sesión + +El runtime actual se compone en `apps/web/src/app.tsx`: + +```text +DataSourceProvider +└── IdentityAuthSessionProvider + └── router +``` + +El login usa `POST /identity/auth/login`. Tras autenticar: + +1. persiste `accessToken` y `refreshToken`; +2. elimina cualquier empresa activa anterior; +3. obtiene la sesión con `GET /identity/auth/session`; +4. carga `GET /companies/available`; +5. resuelve la empresa activa y el destino autenticado. + +Al restaurar una sesión, el provider valida el token, hidrata identidad y empresas y limpia ambos estados si la restauración falla. El frontend nuevo usa `@erp/identity/client`; no debe introducir dependencias nuevas sobre el runtime legacy de auth. + +## 7. Selección de empresa + +- Sin empresas: navega a `/no-companies`; no existe `activeCompanyId` y el logout permanece disponible. +- Una empresa: se selecciona automáticamente. +- Varias empresas: navega a `/company-selection` hasta que el usuario elige. +- Empresa persistida no disponible: vuelve a aplicar las tres reglas anteriores. + +El switcher autenticado consume `availableCompanies`, `activeCompany` y `changeActiveCompany()` desde `useIdentityAuthSession()`. No obtiene tokens ni repite la consulta directamente. + +## 8. Contexto tenant y `X-Company-Id` + +Las requests tenant-scoped incluyen `X-Company-Id` cuando existe una empresa activa. No lo incluyen: + +```text +POST /identity/auth/login +POST /identity/auth/refresh +POST /identity/auth/logout +GET /identity/auth/session +GET /companies/available +``` + +Los guards de compañía resuelven la navegación; los componentes visuales no deben inspeccionar headers HTTP ni decidir tenancy. + +## 9. Refresh de Axios + +Ante un `401` autenticado, el interceptor: + +1. inicia `POST /identity/auth/refresh` si existe refresh token; +2. comparte una única `refreshPromise` entre requests concurrentes; +3. persiste los tokens renovados; +4. reintenta una sola vez la request original mediante `_retry`; +5. limpia sesión y navega a `/login` si el refresh falla. + +Login, refresh, logout, session y companies available están excluidos del bucle de refresh y del header tenant. Una request tenant reintentada conserva el contexto de empresa. + +## 10. Navegación y contexto + +La navegación derivada de una operación pertenece al controller. Las páginas conectan callbacks y las vistas puras no importan React Router. El retorno desde editores debe reutilizar el mecanismo de `returnTo`/navegación existente en vez de reconstruir rutas localmente. + +El estado remoto permanece en TanStack Query. No se duplica en estado local salvo estado efímero de presentación. Queries, invalidaciones y mutations viven en hooks compartidos del dominio. + +## 11. Reglas de contribución arquitectónica + +- Mantener TypeScript estricto y `import type` en cabecera. +- Naming y código en inglés; documentación y comentarios arquitectónicos en español. +- Preferir composición a frameworks CRUD o controllers genéricos. +- No introducir acceso a red en UI ni reglas de negocio en Pages. +- Mantener routing, sesión, tenant y sistema visual como responsabilidades separadas. +- Cambios de API visual pública deben sincronizar implementación, [UI system](./ui-system.md), UI Kit y tests. + diff --git a/docs/frontend/ui-patterns.md b/docs/frontend/ui-patterns.md new file mode 100644 index 00000000..52c12a2a --- /dev/null +++ b/docs/frontend/ui-patterns.md @@ -0,0 +1,187 @@ +# 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 + + + + } + /> + {refreshError} + + + + +``` + +- 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 +} + footer={} +> + + + + {notice} + {fields} + + + + +``` + +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 `
`. +- 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: + +- frecuentes en `primaryActions`; +- secundarias y destructivas en `overflowActions` por defecto; +- 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. + diff --git a/docs/frontend/ui-system.md b/docs/frontend/ui-system.md new file mode 100644 index 00000000..d67573aa --- /dev/null +++ b/docs/frontend/ui-system.md @@ -0,0 +1,185 @@ +# Sistema UI Rdx + +> **Propósito:** explicar qué foundations y componentes visuales existen. Este documento, junto con [Arquitectura frontend](./frontend-architecture.md) y [UI patterns](./ui-patterns.md), forma la fuente de verdad (SSOT) de frontend/UI. + +## 1. Principios + +- Claridad antes que abstracción. +- UI pura: recibe datos preparados y emite eventos. +- Composición antes que configuración extensa. +- KISS: no existe un framework CRUD genérico. +- Tema claro y oscuro desde el inicio. +- Accesibilidad y densidad informativa. +- Tokens semánticos en lugar de colores físicos. + +## 2. Responsabilidad de `rdx-ui` + +`packages/rdx-ui` contiene foundations y composiciones visuales reutilizables. No conoce React Query, routing, DTOs, entidades de dominio, controllers, permisos, casos de uso ni errores de transporte. El consumidor decide contenido, acciones, orden, labels y estados disabled. + +Los componentes públicos propios usan prefijo `Rdx`, API pequeña, props explícitas y `className` solo como escape controlado. + +## 3. Arquitectura visual + +```text +AppMain +└── RdxPageFrame + ├── header + │ ├── RdxPageHeader + │ └── RdxEditorHeader + ├── content (scroll principal) + │ └── RdxPageContainer + │ ├── RdxPageStack + │ └── RdxEditorLayout + └── footer opcional + └── RdxEditorActionBar +``` + +Cada nivel tiene una sola responsabilidad. No se reconstruye viewport, anchura y scroll en componentes de página. + +## 4. Foundations + +### Espaciado y anchura + +`RdxPageContainer` controla ancho, centrado y padding: + +| Propiedad | Resultado | +| --- | --- | +| padding móvil | `px-4 py-4` | +| padding desktop | `md:px-6 md:py-6` | +| `width="full"` | ancho completo | +| `width="wide"` | centrado, `max-w-screen-2xl` | +| `width="content"` | centrado, `max-w-5xl` | + +`padding="none"` elimina únicamente su padding. El container no controla altura ni scroll. + +`RdxPageStack` establece el ritmo vertical: + +| Spacing | Gap | +| --- | --- | +| `compact` | 4 | +| `default` | 6 | +| `relaxed` | 8 | + +### Tema + +Se usan tokens como `bg-background`, `text-foreground`, `bg-muted`, `text-muted-foreground`, `border-border`, `bg-primary` y `bg-destructive`. El color nunca es el único indicador de estado. Toda foundation nueva debe verificarse en light y dark. + +## 5. Componentes públicos + +### `RdxPageFrame` + +Compone header, contenido y footer y es propietario del scroll principal. No conoce routing, queries, width, formularios ni negocio. + +```tsx +} + footer={} +> + {content} + +``` + +`contentClassName="overflow-hidden"` se reserva para layouts especializados con panes y scrolls internos. + +### Headers + +`RdxPageHeader` es la cabecera editorial estándar. Presenta `title`, `description`, `leading`, `metadata`, `actions` y contenido opcional. + +`RdxEditorHeader` es la cabecera compacta fullscreen. Presenta `title`, retorno mediante `onBack`/`backLabel`, `status`, `actions` y contenido contextual opcional. + +Ninguno usa `sticky` para seleccionar una variante. El frame coloca el header fuera del scroll. + +### `RdxPageActions` + +Ordena regiones `secondary`, `primary` y `overflow`. No decide qué acciones existen ni sus permisos. + +### `RdxEditorLayout` + +Distribuye main y un `aside` opcional. No controla width, padding ni scroll. Sin aside no reserva una columna. En desktop, el aside usa `22rem`; en anchos menores se apila. + +El aside aloja contexto secundario, metadata o configuración auxiliar. No se mueven campos allí solo por estética. + +### `RdxEditorActionBar` + +Compone `leading`, `status`, `secondary` y `primary`. El patrón normal la coloca en `RdxPageFrame.footer`, fuera del scroll. La capacidad `sticky` solo se usa si la barra vive justificadamente dentro de un área scrollable. + +### `RdxPanel` + +Agrupa una sección con título, descripción, icono, acciones y contenido: + +- `variant="plain"`: sección plana con divisor; valor predeterminado. +- `variant="card"`: superficie independiente. + +### `RdxFormGrid` + +Proporciona la retícula de campos. No conoce React Hook Form ni debe envolver tablas, line editors o layouts especializados. + +### `RdxRowActions` + +Renderiza acciones primarias y overflow. El consumidor decide acciones, orden, navegación, `disabledReason` y destructividad. Las destructivas van en overflow por defecto. + +### `RdxContextPanel` + +Compone cabecera, cierre, body y footer de un panel contextual. La geometría redimensionable y el contenido de dominio permanecen en el consumidor. + +## 6. Scroll y viewport + +Regla canónica: + +```text +AppMain (espacio disponible, sin scroll) +└── RdxPageFrame + └── content: overflow-y-auto +``` + +Una Page no debe usar `fixed inset-0`, `h-screen`, `h-dvh`, `overflow-y-auto` ni `overflow-auto` para reconstruir viewport o scroll principal. + +Excepción: un detalle documental puede configurar el contenido del frame con `overflow-hidden` y delegar scroll interno a document panes o inspectors. Dialogs, sheets, drawers, overlays, viewers y paneles internos también pueden necesitar posicionamiento o scroll propios. + +## 7. Superficies + +- El contenido principal es plano por defecto. +- Las secciones se separan con spacing y divisores. +- Una card es una excepción, no el envoltorio automático de cada sección. +- El aside puede usar cards cuando necesita una superficie independiente. +- Tablas y editores de líneas conservan su layout funcional. +- Evitar card dentro de card y sombras acumuladas. + +## 8. Responsive y accesibilidad + +- Las columnas se apilan antes de producir overflow horizontal. +- Todo contenedor flexible susceptible de truncado usa `min-w-0`. +- Botones de icono tienen nombre accesible y foco visible. +- No se crean `div` clicables. +- El orden DOM debe ser coherente con lectura y teclado. +- Los iconos decorativos usan `aria-hidden`. +- Estados no dependen exclusivamente del color. + +## 9. UI Kit + +`/development/ui-kit` muestra las foundations públicas vigentes y sirve para revisar tema, responsive y composición. No es una especificación alternativa: debe mantenerse sincronizado con implementación, este documento y tests. + +## 10. Testing + +Los componentes públicos se prueban por comportamiento y contrato observable. No se fijan clases Tailwind salvo cuando expresan una responsabilidad estructural esencial, como el ownership de scroll o la ausencia de una columna aside. + +Cada cambio de API pública debe actualizar conjuntamente: + +1. implementación; +2. documentación; +3. UI Kit; +4. tests aplicables. + +## 11. Anti-patterns + +- Variantes genéricas `simple`/`complex` para mezclar width y geometría. +- Layouts CRUD universales. +- Scroll principal fuera de `RdxPageFrame`. +- Padding duplicado entre shell, container y superficie. +- Routing, permisos o queries dentro de `rdx-ui`. +- Acciones completas duplicadas en header y footer. +- Cards por sección sin necesidad funcional. +- Colores físicos o color como única señal. + +Las recetas para construir páginas concretas están en [UI patterns](./ui-patterns.md). +