diff --git a/apps/web/src/development/ui-kit/ui-kit-page.tsx b/apps/web/src/development/ui-kit/ui-kit-page.tsx index ed62a004..eb4b13d3 100644 --- a/apps/web/src/development/ui-kit/ui-kit-page.tsx +++ b/apps/web/src/development/ui-kit/ui-kit-page.tsx @@ -1,14 +1,14 @@ import { RdxBadge, RdxContextPanel, + RdxEditorActionBar, RdxEditorPageLayout, - RdxEditorSection, - RdxEditorStickyFooter, RdxFormGrid, RdxPageActions, RdxPageContainer, RdxPageHeader, RdxPageStack, + RdxPanel, RdxRowActions, RightPanel, } from "@repo/rdx-ui/components"; @@ -761,26 +761,26 @@ export default function UiKitPage() { - +
- } title="Plain section" >
Section body
-
+
-
Panel body
-
+
@@ -814,7 +814,7 @@ export default function UiKitPage() {
- } title="Basic information" @@ -827,9 +827,9 @@ export default function UiKitPage() { Tax ID field
- + - } title="Preferences" @@ -842,7 +842,7 @@ export default function UiKitPage() { Currency field - + @@ -873,16 +873,13 @@ export default function UiKitPage() { - +
Customer selector and helper actions
-
+ - @@ -894,7 +891,7 @@ export default function UiKitPage() { Default tax selector - + } complexity="complex" @@ -905,7 +902,7 @@ export default function UiKitPage() { Inline feedback aligned with the editor content - +
Reference field @@ -917,7 +914,7 @@ export default function UiKitPage() { Series field
-
+
@@ -930,7 +927,7 @@ export default function UiKitPage() {
- Sticky totals region diff --git a/apps/web/src/layout/app-fullscreen-layout.tsx b/apps/web/src/layout/app-fullscreen-layout.tsx index 9e15f073..405b5120 100644 --- a/apps/web/src/layout/app-fullscreen-layout.tsx +++ b/apps/web/src/layout/app-fullscreen-layout.tsx @@ -1,5 +1,5 @@ -import { Outlet } from "react-router-dom"; import type React from "react"; +import { Outlet } from "react-router-dom"; import { AppMain } from "./app-main"; @@ -8,11 +8,14 @@ interface AppFullscreenLayoutFrameProps { } export const AppFullscreenLayoutFrame = ({ children }: AppFullscreenLayoutFrameProps) => { - return ( - {children} - ); + return {children}; }; +/** + * Shell de aplicación para páginas que ocupan toda el área disponible. + * Se utiliza en editores y vistas de detalle sin sidebar ni topbar global. + */ + export const AppFullscreenLayout = () => { return ( diff --git a/apps/web/src/layout/app-main.tsx b/apps/web/src/layout/app-main.tsx index 1a69517b..c0415156 100644 --- a/apps/web/src/layout/app-main.tsx +++ b/apps/web/src/layout/app-main.tsx @@ -6,6 +6,19 @@ interface AppMainProps { className?: string; } +/** + * Área principal de contenido de la aplicación. + * + * Ocupa todo el espacio disponible dentro de su contenedor padre. + * No decide cómo se muestra su contenido. + */ + export const AppMain = ({ children, className }: AppMainProps) => { - return
{children}
; + return ( +
+ {children} +
+ ); }; diff --git a/apps/web/src/layout/app-sidebar-layout.tsx b/apps/web/src/layout/app-sidebar-layout.tsx index 5e836b53..51ede720 100644 --- a/apps/web/src/layout/app-sidebar-layout.tsx +++ b/apps/web/src/layout/app-sidebar-layout.tsx @@ -21,7 +21,7 @@ export const AppSidebarLayoutFrame = ({ children }: AppSidebarLayoutFrameProps) } > - + {children} @@ -29,6 +29,17 @@ export const AppSidebarLayoutFrame = ({ children }: AppSidebarLayoutFrameProps) ); }; +/** + * Shell principal de la aplicación con navegación lateral. + * + * Compone: + * - sidebar global + * - topbar global + * - área principal de contenido + * + * No decide cómo se muestra su contenido. + */ + export const AppSidebarLayout = () => { return ( diff --git a/apps/web/src/layout/auth-layout.tsx b/apps/web/src/layout/auth-layout.tsx index c3042791..d9046d57 100644 --- a/apps/web/src/layout/auth-layout.tsx +++ b/apps/web/src/layout/auth-layout.tsx @@ -1,5 +1,5 @@ -import { Outlet } from "react-router-dom"; import type React from "react"; +import { Outlet } from "react-router-dom"; interface AuthLayoutFrameProps { children: React.ReactNode; diff --git a/packages/rdx-ui/docs/contributing-ui.md b/docs/frontend/UI/contributing-ui.md similarity index 95% rename from packages/rdx-ui/docs/contributing-ui.md rename to docs/frontend/UI/contributing-ui.md index 4ddca758..d3c88595 100644 --- a/packages/rdx-ui/docs/contributing-ui.md +++ b/docs/frontend/UI/contributing-ui.md @@ -66,7 +66,7 @@ Deben recibir props ya preparadas por la capa consumidora. - 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; -- `RdxEditorSection` con `variant="plain"` es el patrón por defecto del editor simple; +- `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; @@ -81,7 +81,7 @@ Deben recibir props ya preparadas por la capa consumidora. - 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; -- `RdxEditorSection` puede usar `variant="panel"` dentro del `aside` cuando el bloque necesite superficie independiente; +- `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; diff --git a/packages/rdx-ui/docs/foundations.md b/docs/frontend/UI/foundations.md similarity index 100% rename from packages/rdx-ui/docs/foundations.md rename to docs/frontend/UI/foundations.md diff --git a/packages/rdx-ui/docs/page-layouts.md b/docs/frontend/UI/page-layouts.md similarity index 94% rename from packages/rdx-ui/docs/page-layouts.md rename to docs/frontend/UI/page-layouts.md index dd13ffcc..3d4a2f91 100644 --- a/packages/rdx-ui/docs/page-layouts.md +++ b/docs/frontend/UI/page-layouts.md @@ -94,8 +94,8 @@ Reglas: scroll container └── RdxEditorPageLayout complexity="simple" └── form stack - ├── RdxEditorSection - ├── RdxEditorSection + ├── RdxPanel + ├── RdxPanel └── ... ``` @@ -105,7 +105,7 @@ Reglas: - 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; -- `RdxEditorSection` expresa jerarquía por defecto con spacing y divisor inferior; +- `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; @@ -122,9 +122,9 @@ Ejemplo validado en `customers/update`, `customers/create` y `/account`: {notice}
- + {fields} - +
@@ -166,8 +166,8 @@ shell │ └── RdxEditorPageLayout complexity="complex" │ ├── main │ │ └── única superficie editable -│ │ ├── RdxEditorSection -│ │ ├── RdxEditorSection +│ │ ├── RdxPanel +│ │ ├── RdxPanel │ │ ├── bloque de líneas con layout propio │ │ └── ... │ └── aside @@ -206,9 +206,9 @@ Ejemplo validado en `proformas/update`: >
- + {documentFields} - + {lineEditor}
diff --git a/packages/rdx-ui/docs/visual-system-design.md b/docs/frontend/UI/visual-system-design.md similarity index 97% rename from packages/rdx-ui/docs/visual-system-design.md rename to docs/frontend/UI/visual-system-design.md index 0059edc1..0fb641db 100644 --- a/packages/rdx-ui/docs/visual-system-design.md +++ b/docs/frontend/UI/visual-system-design.md @@ -28,7 +28,7 @@ RdxContextPanel RdxRowActions RdxListPageLayout RdxEditorPageLayout -RdxEditorSection +RdxPanel RdxFormGrid RdxEditorStickyFooter RdxDetailPageLayout @@ -525,7 +525,7 @@ RdxPageActions RdxContextPanel RdxRowActions RdxEditorPageLayout -RdxEditorSection +RdxPanel RdxFormGrid RdxEditorStickyFooter ``` @@ -1074,14 +1074,14 @@ Usar `customers/update` como piloto para: ```txt RdxEditorPageLayout -RdxEditorSection +RdxPanel RdxFormGrid ``` Resultado observado a 6 de agosto de 2026: - `RdxEditorPageLayout` absorbe el ancho centrado y el padding interior del editor simple; -- `RdxEditorSection` sustituye en `customers/update` el uso sistemático de `FormSectionCard` cuando la superficie no necesita card; +- `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. @@ -1096,7 +1096,7 @@ customers/create Resultado observado a 6 de agosto de 2026: -- `customers/create` reutiliza `RdxEditorPageLayout`, `RdxEditorSection` y `RdxFormGrid` sin requerir nuevas primitives; +- `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. @@ -1120,7 +1120,7 @@ Resultado observado a 10 de agosto de 2026: - 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; -- `RdxEditorSection` 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. +- `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 @@ -1166,9 +1166,9 @@ No hacer: - `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`, `RdxEditorSection` y `RdxFormGrid` quedan confirmados tras migrar `customers/update` y `customers/create`. +- `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. -- `RdxEditorSection` soporta tanto secciones planas de formulario como grupos secundarios del `aside` sin volver a `Card` por defecto. +- `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. diff --git a/docs/suppliers/frontend-suppliers-ui-ux-design.md b/docs/suppliers/frontend-suppliers-ui-ux-design.md index 6cc02333..e33284e1 100644 --- a/docs/suppliers/frontend-suppliers-ui-ux-design.md +++ b/docs/suppliers/frontend-suppliers-ui-ux-design.md @@ -119,7 +119,7 @@ Todos los textos visibles pertenecen al namespace `Suppliers`. Se mantienen recu ## 17. Componentes reutilizados - `RdxPageContainer`, `RdxPageStack`, `RdxPageHeader`, `RdxPageActions`; -- `RdxEditorPageLayout`, `RdxEditorSection`, `RdxFormGrid`; +- `RdxEditorPageLayout`, `RdxPanel`, `RdxFormGrid`; - `DataTable`, `SkeletonDataTable`, `DataTableColumnHeader`, `RdxRowActions`; - `TextField`, `SelectField`, `PageFormHeader`, `FormActionsBar`; - `SimpleSearchInput`, `PageErrorState`, `ErrorAlert`; diff --git a/modules/companies/src/web/settings/company-details/ui/pages/company-details-settings-page.test.tsx b/modules/companies/src/web/settings/company-details/ui/pages/company-details-settings-page.test.tsx index 08490188..cfe9b620 100644 --- a/modules/companies/src/web/settings/company-details/ui/pages/company-details-settings-page.test.tsx +++ b/modules/companies/src/web/settings/company-details/ui/pages/company-details-settings-page.test.tsx @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import { mock, test } from "node:test"; + import type { ReactNode } from "react"; import { renderToStaticMarkup } from "react-dom/server"; @@ -73,13 +74,9 @@ mock.module("@repo/rdx-ui/components", { capturedResetProps = props; return null; }, - RdxEditorSection: ({ - children, - title, - }: { - children: ReactNode; - title: string; - }) =>
{children}
, + RdxPanel: ({ children, title }: { children: ReactNode; title: string }) => ( +
{children}
+ ), RdxEditorStickyFooter: () => null, RdxFormGrid: ({ children }: { children: ReactNode }) =>
{children}
, RhfSubmitActionButton: (props: Record) => { @@ -108,7 +105,7 @@ mock.module("../../../../i18n", { useTranslation: () => ({ t: (key: string) => ( - { + ({ "settings.companyDetails.title": "Datos de empresa", "settings.companyDetails.loading": "Cargando datos de la empresa activa...", "settings.companyDetails.description": @@ -157,7 +154,8 @@ mock.module("../../../../i18n", { "Debe pertenecer a la empresa activa y estar activo para nuevas asignaciones.", "settings.companyDetails.sections.paymentTerm.field.placeholder": "Selecciona un vencimiento", - "settings.companyDetails.sections.paymentTerm.field.none": "Sin vencimiento por defecto", + "settings.companyDetails.sections.paymentTerm.field.none": + "Sin vencimiento por defecto", "settings.companyDetails.actions.save": "Guardar cambios", "settings.companyDetails.actions.saving": "Guardando...", "settings.companyDetails.actions.reset": "Restablecer", @@ -165,7 +163,7 @@ mock.module("../../../../i18n", { "settings.companyDetails.feedback.saving": "Guardando configuracion de empresa.", "settings.companyDetails.pageError.actions.retry": "Reintentar", "settings.companyDetails.pageError.actions.retrying": "Reintentando...", - } as Record + }) as Record )[key] ?? key, }), }, diff --git a/modules/companies/src/web/settings/company-details/ui/pages/company-details-settings-page.tsx b/modules/companies/src/web/settings/company-details/ui/pages/company-details-settings-page.tsx index 68124932..317b79d2 100644 --- a/modules/companies/src/web/settings/company-details/ui/pages/company-details-settings-page.tsx +++ b/modules/companies/src/web/settings/company-details/ui/pages/company-details-settings-page.tsx @@ -3,9 +3,9 @@ import { UnsavedChangesProvider } from "@erp/core/hooks"; import { AppContent, CancelActionButton, - RdxEditorSection, - RdxEditorStickyFooter, + RdxEditorActionBar, RdxFormGrid, + RdxPanel, RhfSubmitActionButton, SelectField, TextField, @@ -88,11 +88,11 @@ export const CompanyDetailsSettingsPage = () => { onKeyDown={ctrl.onFormKeyDown} onSubmit={ctrl.onSubmit} > - } title={t("settings.companyDetails.sections.general.title")} - variant="panel" + variant="card" > @@ -132,13 +132,13 @@ export const CompanyDetailsSettingsPage = () => { placeholder={t("settings.companyDetails.sections.general.fields.tin.placeholder")} /> - + - } title={t("settings.companyDetails.sections.contact.title")} - variant="panel" + variant="card" > @@ -184,13 +184,13 @@ export const CompanyDetailsSettingsPage = () => { typePreset="url" /> - + - } title={t("settings.companyDetails.sections.paymentTerm.title")} - variant="panel" + variant="card" > @@ -207,7 +207,7 @@ export const CompanyDetailsSettingsPage = () => { placeholder={t("settings.companyDetails.sections.paymentTerm.field.placeholder")} /> - +

@@ -216,7 +216,7 @@ export const CompanyDetailsSettingsPage = () => { : t("settings.companyDetails.feedback.idle")}

- {/* Descuentos */} @@ -184,15 +186,22 @@ export const ProformaCompactTotals = ({
- -
+
{t("proformas.update.totals.subtotalBeforeDiscounts", "Subtotal")} @@ -210,7 +219,7 @@ export const ProformaCompactTotals = ({ - + {t("proformas.update.totals.totalDiscountAmount", "Dtos")} {`-${formatMoney(totals.totalDiscountAmount)}`} @@ -230,7 +239,7 @@ export const ProformaCompactTotals = ({ {t("proformas.update.totals.taxTotal", "Total impuestos")} - + {t("proformas.update.totals.taxTotal", "Impuestos")} @@ -246,7 +255,7 @@ export const ProformaCompactTotals = ({ {t("proformas.update.totals.retentionAmount", "Total retenciones")} - + {t("proformas.update.totals.retentionAmount", "Retenc.")} @@ -258,7 +267,7 @@ export const ProformaCompactTotals = ({ {isEmbedded ? (
- Total + Total {formatMoney(totals.total)} diff --git a/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx b/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx index 99df5326..d6b754cd 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx @@ -308,7 +308,9 @@ export const ProformaLineFooterEditor = ({
{/* Global discount */}
- Descuento global (%): + + {t("proformas.update.totals.globalDiscountPercentage", "Descuento global")} (%): +
-
+
{t("form_fields.items.total.label", "Total dto. global")}: {MoneyHelper.formatCurrency(totals.globalDiscountAmount, 2, currency)} @@ -335,7 +337,7 @@ export const ProformaLineFooterEditor = ({ {/* Totals summary */}
-
+
{t("form_fields.items.total.label", "Total dto. líneas")}: @@ -345,8 +347,8 @@ export const ProformaLineFooterEditor = ({
-
- {t("form_fields.items.total.label", "Total")}: +
+ {t("form_fields.items.total.label", "Total de líneas")}: {MoneyHelper.formatCurrency(itemsTotals.total, 2, currency)} diff --git a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-payment-editor.tsx b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-payment-editor.tsx index c000b0f3..2c667e5a 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-payment-editor.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-payment-editor.tsx @@ -1,9 +1,4 @@ -import { - RdxEditorSection, - RdxFormGrid, - SelectField, - type SelectFieldItem, -} from "@repo/rdx-ui/components"; +import { RdxFormGrid, RdxPanel, SelectField, type SelectFieldItem } from "@repo/rdx-ui/components"; import { CreditCardIcon } from "lucide-react"; import { useTranslation } from "../../../../i18n"; @@ -30,12 +25,12 @@ export const ProformaUpdatePaymentEditor = ({ const { t } = useTranslation(); return ( - } title={t("form_groups.proformas.payment.title", "Condiciones de pago")} - variant="panel" + variant="card" > - + ); }; diff --git a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-header-editor.tsx b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-header-editor.tsx index 7d039db2..66c0cae1 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-header-editor.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-header-editor.tsx @@ -1,8 +1,8 @@ import { DatePickerField, RdxBadge, - RdxEditorSection, RdxFormGrid, + RdxPanel, SelectField, type SelectFieldItem, TextAreaField, @@ -29,11 +29,11 @@ export const ProformaUpdateHeaderEditor = ({ const { t } = useTranslation(); return ( - } title={t("form_groups.proformas.basic_info.title")} - variant="panel" + variant="card" > - + ); }; diff --git a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-items-editor.tsx b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-items-editor.tsx index 740827bd..a57641ef 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-items-editor.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-items-editor.tsx @@ -1,4 +1,4 @@ -import { RdxEditorSection } from "@repo/rdx-ui/components"; +import { RdxPanel } from "@repo/rdx-ui/components"; import { cn } from "@repo/shadcn-ui/lib/utils"; import { ListIcon } from "lucide-react"; @@ -29,7 +29,7 @@ export const ProformaUpdateItemsEditor = ({ const { itemErrors } = itemsCtrl; return ( - } title={t("form_groups.items.title", "Líneas de detalle")} - variant="panel" + variant="card" > - + ); }; diff --git a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-recipient-editor.tsx b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-recipient-editor.tsx index 39b18c14..372c5861 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-recipient-editor.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-recipient-editor.tsx @@ -1,5 +1,5 @@ import type { CustomerSelectionOption } from "@erp/customers"; -import { RdxEditorSection } from "@repo/rdx-ui/components"; +import { RdxPanel } from "@repo/rdx-ui/components"; import { Button } from "@repo/shadcn-ui/components"; import { PlusIcon, RefreshCwIcon, UserIcon } from "lucide-react"; @@ -32,12 +32,15 @@ export const ProformaUpdateRecipientEditor = ({ const showActions = !readOnly; return ( - } title={t("form_groups.proformas.customer.title", "Cliente")} - variant="panel" + variant="card" >
@@ -69,6 +72,6 @@ export const ProformaUpdateRecipientEditor = ({
) : null}
- + ); }; diff --git a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-tax-editor.tsx b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-tax-editor.tsx index 3d0fdb75..c3b58657 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-tax-editor.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-tax-editor.tsx @@ -1,6 +1,6 @@ import { - RdxEditorSection, RdxFormGrid, + RdxPanel, SelectField, type SelectFieldItem, SwitchField, @@ -38,7 +38,7 @@ export const ProformaUpdateTaxEditor = ({ const { t } = useTranslation(); return ( - } title={t("form_groups.proformas.taxes.title", "Configuración fiscal")} - variant="panel" + variant="card" > (typeof value === "number" ? String(value) : "")} /> - + ); }; diff --git a/modules/customer-invoices/src/web/proformas/update/ui/pages/proforma-update-page.tsx b/modules/customer-invoices/src/web/proformas/update/ui/pages/proforma-update-page.tsx index f509a764..d35d6cf5 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/pages/proforma-update-page.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/pages/proforma-update-page.tsx @@ -5,7 +5,7 @@ import { CancelActionButton, PercentageField, RdxBadge, - RdxEditorStickyFooter, + RdxEditorActionBar, RdxPageContainer, RdxPageStack, RhfSubmitActionButton, @@ -204,7 +204,7 @@ export const ProformaUpdatePage = () => { /> - { > - {(profileCtrl.showExemptionFields || profileCtrl.showVerifactuFields) ? ( + {profileCtrl.showExemptionFields || profileCtrl.showVerifactuFields ? ( { : t("settings.verifactu.feedback.idle")}

- } title={t("settings.verifactu.configuration.title")} - variant="panel" + variant="card" > {showExemptionFields ? ( @@ -63,8 +63,8 @@ export const CompanyVerifactuConfigurationSection = ({ /> ) : null} - + ); }; diff --git a/modules/customer-invoices/src/web/settings/verifactu/ui/sections/company-verifactu-defaults-section.tsx b/modules/customer-invoices/src/web/settings/verifactu/ui/sections/company-verifactu-defaults-section.tsx index 6205e477..97cded83 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/ui/sections/company-verifactu-defaults-section.tsx +++ b/modules/customer-invoices/src/web/settings/verifactu/ui/sections/company-verifactu-defaults-section.tsx @@ -1,9 +1,4 @@ -import { - RdxEditorSection, - RdxFormGrid, - SelectField, - type SelectFieldItem, -} from "@repo/rdx-ui/components"; +import { RdxFormGrid, RdxPanel, SelectField, type SelectFieldItem } from "@repo/rdx-ui/components"; import { LandmarkIcon } from "lucide-react"; import { useTranslation } from "../../../../i18n"; @@ -22,16 +17,16 @@ export const CompanyVerifactuDefaultsSection = ({ const { t } = useTranslation(); return ( - } title={t("settings.verifactu.defaults.title")} - variant="panel" + variant="card" > - + ); }; diff --git a/modules/customer-invoices/src/web/settings/verifactu/ui/sections/company-verifactu-status-section.tsx b/modules/customer-invoices/src/web/settings/verifactu/ui/sections/company-verifactu-status-section.tsx index 4ddaed54..ef4d5e0c 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/ui/sections/company-verifactu-status-section.tsx +++ b/modules/customer-invoices/src/web/settings/verifactu/ui/sections/company-verifactu-status-section.tsx @@ -1,4 +1,4 @@ -import { FormFieldLabel, RdxEditorSection } from "@repo/rdx-ui/components"; +import { FormFieldLabel, RdxPanel } from "@repo/rdx-ui/components"; import { Field, FieldContent, @@ -19,11 +19,11 @@ export const CompanyVerifactuStatusSection = () => { const { control } = useFormContext(); return ( - } title={t("settings.verifactu.status.title")} - variant="panel" + variant="card" > { )} /> - + ); }; diff --git a/modules/customers/src/common/features/quick-create-customer/ui/blocks/quick-customer-create-form-fields.tsx b/modules/customers/src/common/features/quick-create-customer/ui/blocks/quick-customer-create-form-fields.tsx index cdf65eab..de831e67 100644 --- a/modules/customers/src/common/features/quick-create-customer/ui/blocks/quick-customer-create-form-fields.tsx +++ b/modules/customers/src/common/features/quick-create-customer/ui/blocks/quick-customer-create-form-fields.tsx @@ -1,9 +1,4 @@ -import { - FormFieldLabel, - RdxEditorSection, - RdxFormGrid, - TextField, -} from "@repo/rdx-ui/components"; +import { FormFieldLabel, RdxFormGrid, RdxPanel, TextField } from "@repo/rdx-ui/components"; import { Field, FieldContent, @@ -30,7 +25,7 @@ export const QuickCustomerCreateFormFields = ({ return (
- @@ -111,9 +106,9 @@ export const QuickCustomerCreateFormFields = ({ )} /> - + - @@ -129,7 +124,7 @@ export const QuickCustomerCreateFormFields = ({ typePreset="email" /> - +
); }; diff --git a/modules/customers/src/web/update/ui/editor/customer-additional-config-fields.tsx b/modules/customers/src/web/update/ui/editor/customer-additional-config-fields.tsx index baf84ba3..09785b7b 100644 --- a/modules/customers/src/web/update/ui/editor/customer-additional-config-fields.tsx +++ b/modules/customers/src/web/update/ui/editor/customer-additional-config-fields.tsx @@ -1,4 +1,4 @@ -import { RdxEditorSection, RdxFormGrid, SelectField } from "@repo/rdx-ui/components"; +import { RdxFormGrid, RdxPanel, SelectField } from "@repo/rdx-ui/components"; import { Settings2Icon } from "lucide-react"; import { useTranslation } from "../../../i18n"; @@ -16,7 +16,7 @@ export const CustomerAdditionalConfigEditor = ({ const { t } = useTranslation(); return ( - } title={t("form_groups.preferences.title")} @@ -45,6 +45,6 @@ export const CustomerAdditionalConfigEditor = ({ required /> - + ); }; diff --git a/modules/customers/src/web/update/ui/editor/customer-address-editor.tsx b/modules/customers/src/web/update/ui/editor/customer-address-editor.tsx index 0d12d3f2..0abed863 100644 --- a/modules/customers/src/web/update/ui/editor/customer-address-editor.tsx +++ b/modules/customers/src/web/update/ui/editor/customer-address-editor.tsx @@ -1,9 +1,4 @@ -import { - RdxEditorSection, - RdxFormGrid, - SelectField, - TextField, -} from "@repo/rdx-ui/components"; +import { RdxFormGrid, RdxPanel, SelectField, TextField } from "@repo/rdx-ui/components"; import { MapPinIcon } from "lucide-react"; import { useTranslation } from "../../../i18n"; @@ -21,7 +16,7 @@ export const CustomerAddressEditor = ({ const { t } = useTranslation(); return ( - } title={t("form_groups.address.title")} @@ -82,6 +77,6 @@ export const CustomerAddressEditor = ({ required /> - + ); }; diff --git a/modules/customers/src/web/update/ui/editor/customer-basic-info-editor.tsx b/modules/customers/src/web/update/ui/editor/customer-basic-info-editor.tsx index bf989810..2b3048a5 100644 --- a/modules/customers/src/web/update/ui/editor/customer-basic-info-editor.tsx +++ b/modules/customers/src/web/update/ui/editor/customer-basic-info-editor.tsx @@ -1,7 +1,7 @@ import { FormFieldLabel, - RdxEditorSection, RdxFormGrid, + RdxPanel, TextAreaField, TextField, } from "@repo/rdx-ui/components"; @@ -32,7 +32,7 @@ export const CustomerBasicInfoEditor = ({ const { control } = useFormContext(); return ( - } title={t("form_groups.basic_info.title")} @@ -145,6 +145,6 @@ export const CustomerBasicInfoEditor = ({ readOnly={readOnly} /> - + ); }; diff --git a/modules/customers/src/web/update/ui/editor/customer-contact-fields.tsx b/modules/customers/src/web/update/ui/editor/customer-contact-fields.tsx index f753cd87..a0867188 100644 --- a/modules/customers/src/web/update/ui/editor/customer-contact-fields.tsx +++ b/modules/customers/src/web/update/ui/editor/customer-contact-fields.tsx @@ -1,4 +1,4 @@ -import { RdxEditorSection, RdxFormGrid, TextField } from "@repo/rdx-ui/components"; +import { RdxFormGrid, RdxPanel, TextField } from "@repo/rdx-ui/components"; import { Collapsible, CollapsibleContent, @@ -23,7 +23,7 @@ export const CustomerContactEditor = ({ const [open, setOpen] = useState(true); return ( - } title={t("form_groups.contact_info.title")} @@ -140,6 +140,6 @@ export const CustomerContactEditor = ({ - + ); }; diff --git a/modules/customers/src/web/update/ui/editor/customer-fiscal-editor.tsx b/modules/customers/src/web/update/ui/editor/customer-fiscal-editor.tsx index 9aadc2aa..78e0e0ed 100644 --- a/modules/customers/src/web/update/ui/editor/customer-fiscal-editor.tsx +++ b/modules/customers/src/web/update/ui/editor/customer-fiscal-editor.tsx @@ -1,6 +1,6 @@ import { - RdxEditorSection, RdxFormGrid, + RdxPanel, SelectField, type SelectFieldItem, SwitchField, @@ -27,7 +27,7 @@ export const CustomerFiscalEditor = ({ const { t } = useTranslation(); return ( - } title={t("form_groups.fiscal_info.title")} @@ -88,6 +88,6 @@ export const CustomerFiscalEditor = ({ readOnly={readOnly} /> - + ); }; diff --git a/modules/identity/src/web/account/ui/pages/account-page.tsx b/modules/identity/src/web/account/ui/pages/account-page.tsx index e1ad18af..b5ce7d00 100644 --- a/modules/identity/src/web/account/ui/pages/account-page.tsx +++ b/modules/identity/src/web/account/ui/pages/account-page.tsx @@ -2,9 +2,9 @@ import { ErrorAlert, PageErrorState, PageFormHeader } from "@erp/core/components import { UnsavedChangesProvider, useReturnToNavigation } from "@erp/core/hooks"; import { CancelActionButton, + RdxEditorActionBar, RdxEditorPageLayout, - RdxEditorSection, - RdxEditorStickyFooter, + RdxPanel, RhfSubmitActionButton, SelectField, TextField, @@ -283,7 +283,7 @@ export const AccountPage = () => { onKeyDown={ctrl.onFormKeyDown} onSubmit={ctrl.onProfileSubmit} > - @@ -337,7 +337,7 @@ export const AccountPage = () => { )} />
- +

@@ -346,7 +346,7 @@ export const AccountPage = () => { : t("account.feedback.idle")}

- { - @@ -387,9 +387,9 @@ export const AccountPage = () => { {t("account.actions.changePassword")}
- + - @@ -401,7 +401,7 @@ export const AccountPage = () => { {t("account.actions.logout")}
- +
diff --git a/modules/suppliers/src/web/shared/ui/supplier-editor-form.tsx b/modules/suppliers/src/web/shared/ui/supplier-editor-form.tsx index a9c0fa70..fa96c860 100644 --- a/modules/suppliers/src/web/shared/ui/supplier-editor-form.tsx +++ b/modules/suppliers/src/web/shared/ui/supplier-editor-form.tsx @@ -1,7 +1,7 @@ import { FormFieldLabel, - RdxEditorSection, RdxFormGrid, + RdxPanel, SelectField, TextField, } from "@repo/rdx-ui/components"; @@ -58,7 +58,7 @@ export const SupplierEditorForm = ({ return (
- } title={t("form.sections.identity.title")} @@ -105,9 +105,9 @@ export const SupplierEditorForm = ({ - + - } title={t("form.sections.address.title")} @@ -125,9 +125,9 @@ export const SupplierEditorForm = ({ name="country" /> - + - } title={t("form.sections.contact.title")} @@ -168,9 +168,9 @@ export const SupplierEditorForm = ({ - + - } title={t("form.sections.preferences.title")} @@ -191,7 +191,7 @@ export const SupplierEditorForm = ({ required /> - +
); }; diff --git a/packages/rdx-ui/src/components/editor-page/index.ts b/packages/rdx-ui/src/components/editor-page/index.ts index 51889b0e..b450e339 100644 --- a/packages/rdx-ui/src/components/editor-page/index.ts +++ b/packages/rdx-ui/src/components/editor-page/index.ts @@ -1,3 +1,3 @@ +export * from "./rdx-editor-action-bar.tsx"; export * from "./rdx-editor-page-layout.tsx"; -export * from "./rdx-editor-section.tsx"; -export * from "./rdx-editor-sticky-footer.tsx"; +export * from "./rdx-panel.tsx"; diff --git a/packages/rdx-ui/src/components/editor-page/rdx-editor-sticky-footer.tsx b/packages/rdx-ui/src/components/editor-page/rdx-editor-action-bar.tsx similarity index 68% rename from packages/rdx-ui/src/components/editor-page/rdx-editor-sticky-footer.tsx rename to packages/rdx-ui/src/components/editor-page/rdx-editor-action-bar.tsx index e8bbb16b..442fe1db 100644 --- a/packages/rdx-ui/src/components/editor-page/rdx-editor-sticky-footer.tsx +++ b/packages/rdx-ui/src/components/editor-page/rdx-editor-action-bar.tsx @@ -1,29 +1,43 @@ import { cn } from "@repo/shadcn-ui/lib/utils"; import type { ComponentPropsWithoutRef, ReactNode } from "react"; -export interface RdxEditorStickyFooterProps - extends Omit, "children"> { +export interface RdxEditorActionBarProps extends Omit, "children"> { leading?: ReactNode; status?: ReactNode; secondary?: ReactNode; primary?: ReactNode; + sticky?: boolean; } -export const RdxEditorStickyFooter = ({ +/** + * Barra inferior de acciones de una pantalla de edición. + * + * Puede mostrar: + * - contenido auxiliar a la izquierda + * - estado de edición + * - acciones secundarias + * - acción principal + * + * Puede funcionar dentro o fuera de un área scrollable. + */ + +export const RdxEditorActionBar = ({ leading, status, secondary, primary, + sticky = false, className, ...props -}: RdxEditorStickyFooterProps) => { +}: RdxEditorActionBarProps) => { const hasLeading = Boolean(leading); const hasActionRegion = Boolean(status || secondary || primary); return (
{status ?
{status}
: null} - {(secondary || primary) ? ( + {secondary || primary ? (
{secondary ?
{secondary}
: null} + {primary ?
{primary}
: null}
) : null} diff --git a/packages/rdx-ui/src/components/editor-page/rdx-editor-foundations.test.tsx b/packages/rdx-ui/src/components/editor-page/rdx-editor-foundations.test.tsx index ebc50020..97b5ff39 100644 --- a/packages/rdx-ui/src/components/editor-page/rdx-editor-foundations.test.tsx +++ b/packages/rdx-ui/src/components/editor-page/rdx-editor-foundations.test.tsx @@ -5,9 +5,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { RdxFormGrid } from "../form/rdx-form-grid.tsx"; +import { RdxEditorActionBar } from "./rdx-editor-action-bar.tsx"; import { RdxEditorPageLayout } from "./rdx-editor-page-layout.tsx"; -import { RdxEditorSection } from "./rdx-editor-section.tsx"; -import { RdxEditorStickyFooter } from "./rdx-editor-sticky-footer.tsx"; +import { RdxPanel } from "./rdx-panel.tsx"; test("RdxEditorPageLayout aplica complejidad simple por defecto y renderiza children", () => { const html = renderToStaticMarkup( @@ -47,16 +47,16 @@ test("RdxEditorPageLayout no reserva aside cuando no existe incluso en complex", assert.doesNotMatch(html, /grid-cols-\[/); }); -test("RdxEditorSection plain renderiza header semántico y contenido sin card", () => { +test("RdxPanel plain renderiza header semántico y contenido sin card", () => { const html = renderToStaticMarkup( - Action} description="Section description" icon={Icon} title="Section title" >
Section body
-
+ ); assert.match(html, /]*>Section title<\/h2>/); @@ -66,11 +66,11 @@ test("RdxEditorSection plain renderiza header semántico y contenido sin card", assert.doesNotMatch(html, /shadow/); }); -test("RdxEditorSection panel conserva superficie independiente", () => { +test("RdxPanel panel conserva superficie independiente", () => { const html = renderToStaticMarkup( - +
Panel body
-
+ ); assert.match(html, /rounded-xl/); @@ -95,7 +95,7 @@ test("RdxFormGrid mantiene la retícula base y combina className", () => { test("RdxEditorStickyFooter renderiza regiones presentes sin wrappers vacíos", () => { const html = renderToStaticMarkup( - Totals summary
} primary={} @@ -116,7 +116,7 @@ test("RdxEditorStickyFooter renderiza regiones presentes sin wrappers vacíos", test("RdxEditorStickyFooter omite regiones opcionales vacías", () => { const html = renderToStaticMarkup( - Save} /> + Save} /> ); assert.match(html, /Save/); diff --git a/packages/rdx-ui/src/components/editor-page/rdx-editor-page-layout.tsx b/packages/rdx-ui/src/components/editor-page/rdx-editor-page-layout.tsx index 4507617c..b1dac7bf 100644 --- a/packages/rdx-ui/src/components/editor-page/rdx-editor-page-layout.tsx +++ b/packages/rdx-ui/src/components/editor-page/rdx-editor-page-layout.tsx @@ -20,6 +20,15 @@ const ASIDE_GRID_CLASSNAME: Record = { full: "xl:grid-cols-[minmax(0,1fr)_22rem]", }; +/** + * Distribuye el contenido interno de un contenedor en: + * + * - una región principal + * - un aside opcional. + * + * No controla padding, scroll ni estructura general de página. + */ + export const RdxEditorPageLayout = ({ children, aside, diff --git a/packages/rdx-ui/src/components/editor-page/rdx-editor-section.tsx b/packages/rdx-ui/src/components/editor-page/rdx-panel.tsx similarity index 77% rename from packages/rdx-ui/src/components/editor-page/rdx-editor-section.tsx rename to packages/rdx-ui/src/components/editor-page/rdx-panel.tsx index 73df2f46..6fcf5dcd 100644 --- a/packages/rdx-ui/src/components/editor-page/rdx-editor-section.tsx +++ b/packages/rdx-ui/src/components/editor-page/rdx-panel.tsx @@ -2,24 +2,37 @@ import { cn } from "@repo/shadcn-ui/lib/utils"; import type { ComponentPropsWithoutRef, ReactNode } from "react"; import { useId } from "react"; -export type RdxEditorSectionVariant = "plain" | "panel"; +export type RdxPanelVariant = "plain" | "card"; -export interface RdxEditorSectionProps +export interface RdxPanelProps extends Omit, "children" | "title"> { title: ReactNode; description?: ReactNode; icon?: ReactNode; actions?: ReactNode; children: ReactNode; - variant?: RdxEditorSectionVariant; + variant?: RdxPanelVariant; } -const VARIANT_CLASSNAME: Record = { +const VARIANT_CLASSNAME: Record = { plain: "min-w-0 border-b border-border pb-6 last:border-b-0 last:pb-0", - panel: "min-w-0 rounded-xl border border-border bg-background p-4 shadow-xs sm:p-5", + card: "min-w-0 rounded-xl border border-border bg-background p-4 shadow-xs sm:p-5", }; -export const RdxEditorSection = ({ +/** + * Contenedor para agrupar contenido relacionado. + * + * Proporciona: + * - título + * - descripción + * - icono + * - acciones + * - contenido + * + * Puede representarse sin border (`plain`) o como una tarjeta (`card`). + */ + +export const RdxPanel = ({ title, description, icon, @@ -28,7 +41,7 @@ export const RdxEditorSection = ({ variant = "plain", className, ...props -}: RdxEditorSectionProps) => { +}: RdxPanelProps) => { const titleId = useId(); const hasHeader = Boolean(title || description || icon || actions); diff --git a/packages/rdx-ui/src/components/layout/rdx-page-actions.tsx b/packages/rdx-ui/src/components/layout/rdx-page-actions.tsx index 7dc62c7a..e3658774 100644 --- a/packages/rdx-ui/src/components/layout/rdx-page-actions.tsx +++ b/packages/rdx-ui/src/components/layout/rdx-page-actions.tsx @@ -7,6 +7,16 @@ export interface RdxPageActionsProps extends Omit { - if (!primary && !secondary && !overflow) { + if (!(primary || secondary || overflow)) { return null; } diff --git a/packages/rdx-ui/src/components/layout/rdx-page-container.tsx b/packages/rdx-ui/src/components/layout/rdx-page-container.tsx index d271b3ad..ab0284cc 100644 --- a/packages/rdx-ui/src/components/layout/rdx-page-container.tsx +++ b/packages/rdx-ui/src/components/layout/rdx-page-container.tsx @@ -21,6 +21,21 @@ const PADDING_CLASSNAME: Record = { none: "", }; +/** + * Contenedor visual del contenido de una página. + * + * Controla: + * - ancho máximo + * - centrado horizontal + * - padding exterior + * + * No controla: + * - scroll + * - altura de la página + * - header/footer + * - estructura del editor + */ + export const RdxPageContainer = ({ children, className, diff --git a/packages/rdx-ui/src/components/layout/rdx-page-header.tsx b/packages/rdx-ui/src/components/layout/rdx-page-header.tsx index 15bf0afd..41652ecb 100644 --- a/packages/rdx-ui/src/components/layout/rdx-page-header.tsx +++ b/packages/rdx-ui/src/components/layout/rdx-page-header.tsx @@ -21,6 +21,20 @@ export interface RdxPageHeaderProps children?: ReactNode; } +/** + * Cabecera estándar de una página. + * + * Muestra: + * - navegación de retorno opcional + * - título + * - descripción o metadata opcional + * - estado opcional + * - acciones de página + * + * Puede permanecer fija dentro de un contenedor scrollable + * mediante `sticky`. + */ + export const RdxPageHeader = ({ title, description, diff --git a/packages/rdx-ui/src/components/layout/rdx-page-stack.tsx b/packages/rdx-ui/src/components/layout/rdx-page-stack.tsx index 88bfbfbe..f3c0c3c3 100644 --- a/packages/rdx-ui/src/components/layout/rdx-page-stack.tsx +++ b/packages/rdx-ui/src/components/layout/rdx-page-stack.tsx @@ -15,6 +15,11 @@ const SPACING_CLASSNAME: Record = { relaxed: "gap-8", }; +/** + * Contenedor para layout para apilar contenido verticalmente. + * No proporciona estructura de página ni controla scroll. + */ + export const RdxPageStack = ({ children, className,