feat(ui): actualizar la estructura de RdxPageContent en varias páginas y componentes para mejorar la jerarquía y la semántica
This commit is contained in:
parent
450cc8d7bd
commit
b6aa20e52b
@ -82,7 +82,7 @@ RdxPageContent
|
|||||||
→ composición de los hijos principales
|
→ composición de los hijos principales
|
||||||
```
|
```
|
||||||
|
|
||||||
Regla: Las Pages no deben reconstruir estas responsabilidades con wrappers HTML locales.
|
Regla: Las Pages no deben reconstruir estas responsabilidades con wrappers HTML locales. En Create/Update, la composición canónica es RdxPageFrame → RdxPageContainer → RdxPageContent: los errores inline y el editor son hijos directos de RdxPageContent; los dialogs son hermanos del frame; RdxPanel se reserva para secciones semánticas reales.
|
||||||
|
|
||||||
## 6. Identity y sesión
|
## 6. Identity y sesión
|
||||||
|
|
||||||
|
|||||||
@ -34,6 +34,8 @@ El controller de página concentra URL, filtros, consulta, estados y navegación
|
|||||||
|
|
||||||
## 2. Create / Update
|
## 2. Create / Update
|
||||||
|
|
||||||
|
Estructura canónica: providers → RdxPageFrame (header/content/footer) → un único RdxPageContainer → un único RdxPageContent; los dialogs son hermanos del frame.
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
<RdxPageFrame
|
<RdxPageFrame
|
||||||
header={<RdxEditorHeader />}
|
header={<RdxEditorHeader />}
|
||||||
@ -53,6 +55,7 @@ Los errores inline no definen su propio spacing estructural. Participan del spac
|
|||||||
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.
|
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.
|
Loading y error bloqueante usan también `RdxPageFrame` y `RdxPageContainer`; no reconstruyen el viewport.
|
||||||
|
El error bloqueante conserva la misma jerarquía: RdxPageFrame → RdxPageContainer → RdxPageContent → PageErrorState.
|
||||||
|
|
||||||
## 3. Detail
|
## 3. Detail
|
||||||
|
|
||||||
|
|||||||
@ -108,6 +108,7 @@ Agrupa una sección con título, descripción, icono, acciones y contenido:
|
|||||||
|
|
||||||
- `variant="plain"`: sección plana con divisor; valor predeterminado.
|
- `variant="plain"`: sección plana con divisor; valor predeterminado.
|
||||||
- `variant="card"`: superficie independiente.
|
- `variant="card"`: superficie independiente.
|
||||||
|
RdxPanel no sustituye un div estructural: solo representa una sección con identidad semántica, como fiscalidad, dirección o preferencias.
|
||||||
|
|
||||||
### `RdxFormGrid`
|
### `RdxFormGrid`
|
||||||
|
|
||||||
@ -157,7 +158,7 @@ Los avisos, errores inline, secciones y contenido funcional deben ser hijos dire
|
|||||||
<Editor />
|
<Editor />
|
||||||
</RdxPageContent>
|
</RdxPageContent>
|
||||||
```
|
```
|
||||||
No deben introducirse wrappers adicionales únicamente para controlar gap, ancho, padding o centrado.
|
No deben introducirse wrappers adicionales únicamente para controlar gap, ancho, padding o centrado. No se anidan RdxPageContainer ni RdxPageContent salvo razón arquitectónica explícita. Los errores inline participan como hijos directos del spacing estándar de RdxPageContent; no crean su propio layout.
|
||||||
|
|
||||||
## 8. Responsive y accesibilidad
|
## 8. Responsive y accesibilidad
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ Se adoptan de Customer:
|
|||||||
|
|
||||||
- manifest y rutas lazy por módulo;
|
- manifest y rutas lazy por módulo;
|
||||||
- páginas de listado con `RdxPageContainer`, `RdxPageHeader`, búsqueda con debounce y `DataTable`;
|
- páginas de listado con `RdxPageContainer`, `RdxPageHeader`, búsqueda con debounce y `DataTable`;
|
||||||
- editores fullscreen con `RdxEditorPageLayout`, header persistente, protección de cambios sin guardar y secciones Rdx;
|
- editores fullscreen con `RdxPageFrame`, header persistente, protección de cambios sin guardar y secciones Rdx;
|
||||||
- `PageErrorState`, skeletons, alertas y toasts del sistema;
|
- `PageErrorState`, skeletons, alertas y toasts del sistema;
|
||||||
- React Hook Form con validación Zod;
|
- React Hook Form con validación Zod;
|
||||||
- queries y mutations encapsuladas en hooks y adapters de transporte.
|
- queries y mutations encapsuladas en hooks y adapters de transporte.
|
||||||
@ -119,9 +119,9 @@ Todos los textos visibles pertenecen al namespace `Suppliers`. Se mantienen recu
|
|||||||
## 17. Componentes reutilizados
|
## 17. Componentes reutilizados
|
||||||
|
|
||||||
- `RdxPageContainer`, `RdxPageContent`, `RdxPageHeader`, `RdxPageActions`;
|
- `RdxPageContainer`, `RdxPageContent`, `RdxPageHeader`, `RdxPageActions`;
|
||||||
- `RdxEditorPageLayout`, `RdxPanel`, `RdxFormGrid`;
|
- `RdxPageFrame`, `RdxPanel`, `RdxFormGrid`;
|
||||||
- `DataTable`, `SkeletonDataTable`, `DataTableColumnHeader`, `RdxRowActions`;
|
- `DataTable`, `SkeletonDataTable`, `DataTableColumnHeader`, `RdxRowActions`;
|
||||||
- `TextField`, `SelectField`, `PageFormHeader`, `FormActionsBar`;
|
- `TextField`, `SelectField`, `RdxEditorHeader`, `FormActionsBar`;
|
||||||
- `SimpleSearchInput`, `PageErrorState`, `ErrorAlert`;
|
- `SimpleSearchInput`, `PageErrorState`, `ErrorAlert`;
|
||||||
- `UnsavedChangesProvider` y `useReturnToNavigation`.
|
- `UnsavedChangesProvider` y `useReturnToNavigation`.
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export const ProformaCreatePage = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<RdxPageContainer width="content">
|
<RdxPageContainer width="content">
|
||||||
<RdxPageContent spacing="default">
|
<RdxPageContent>
|
||||||
{createCtrl.isCreateError && (
|
{createCtrl.isCreateError && (
|
||||||
<ErrorAlert
|
<ErrorAlert
|
||||||
message={
|
message={
|
||||||
|
|||||||
@ -1,20 +1,13 @@
|
|||||||
|
import { ErrorAlert } from "@erp/core/components";
|
||||||
import { useHookForm } from "@erp/core/hooks";
|
import { useHookForm } from "@erp/core/hooks";
|
||||||
import {
|
import {
|
||||||
RdxEditorHeader,
|
RdxEditorHeader,
|
||||||
RdxPageContainer,
|
RdxPageContainer,
|
||||||
RdxPageContent,
|
RdxPageContent,
|
||||||
RdxPageFrame,
|
RdxPageFrame,
|
||||||
|
RdxPanel,
|
||||||
} from "@repo/rdx-ui/components";
|
} from "@repo/rdx-ui/components";
|
||||||
import {
|
import { Button, Input, Label, Textarea } from "@repo/shadcn-ui/components";
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
Input,
|
|
||||||
Label,
|
|
||||||
Textarea,
|
|
||||||
} from "@repo/shadcn-ui/components";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
@ -89,6 +82,7 @@ export const RecurringProformaCreatePage = () => {
|
|||||||
>
|
>
|
||||||
<RdxPageContainer width="content">
|
<RdxPageContainer width="content">
|
||||||
<RdxPageContent>
|
<RdxPageContent>
|
||||||
|
{submitError ? <ErrorAlert message={submitError} /> : null}
|
||||||
<form
|
<form
|
||||||
className="space-y-6"
|
className="space-y-6"
|
||||||
id={RECURRING_PROFORMA_CREATE_FORM_ID}
|
id={RECURRING_PROFORMA_CREATE_FORM_ID}
|
||||||
@ -98,19 +92,8 @@ export const RecurringProformaCreatePage = () => {
|
|||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
{t("recurring_proformas.create.description")}
|
{t("recurring_proformas.create.description")}
|
||||||
</p>
|
</p>
|
||||||
{submitError ? (
|
<RdxPanel title={t("recurring_proformas.create.form_title")}>
|
||||||
<p
|
<div className="space-y-5">
|
||||||
className="rounded-md border border-destructive/30 p-3 text-sm text-destructive"
|
|
||||||
role="alert"
|
|
||||||
>
|
|
||||||
{submitError}
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>{t("recurring_proformas.create.form_title")}</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-5">
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="recurring-proforma-name">
|
<Label htmlFor="recurring-proforma-name">
|
||||||
{t("recurring_proformas.create.name")}
|
{t("recurring_proformas.create.name")}
|
||||||
@ -136,8 +119,8 @@ export const RecurringProformaCreatePage = () => {
|
|||||||
{...form.register("description")}
|
{...form.register("description")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</RdxPanel>
|
||||||
</form>
|
</form>
|
||||||
</RdxPageContent>
|
</RdxPageContent>
|
||||||
</RdxPageContainer>
|
</RdxPageContainer>
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export const CustomerCreatePage = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<RdxPageContainer width="content">
|
<RdxPageContainer width="content">
|
||||||
<RdxPageContent spacing="default">
|
<RdxPageContent>
|
||||||
{createCtrl.isCreateError ? (
|
{createCtrl.isCreateError ? (
|
||||||
<ErrorAlert
|
<ErrorAlert
|
||||||
message={(createCtrl.createError as Error)?.message ?? t("create.errors.message")}
|
message={(createCtrl.createError as Error)?.message ?? t("create.errors.message")}
|
||||||
|
|||||||
@ -40,21 +40,17 @@ export const SupplierCreatePage = () => {
|
|||||||
>
|
>
|
||||||
<RdxPageContainer width="content">
|
<RdxPageContainer width="content">
|
||||||
<RdxPageContent>
|
<RdxPageContent>
|
||||||
<div className="rounded-2xl border border-border/60 bg-background p-4 shadow-xs md:p-6">
|
{controller.mutation.isError ? (
|
||||||
<div className="flex min-w-0 flex-col gap-6">
|
<ErrorAlert
|
||||||
{controller.mutation.isError ? (
|
message={controller.mutation.error.message || t("create.error")}
|
||||||
<ErrorAlert
|
title={t("create.error")}
|
||||||
message={controller.mutation.error.message || t("create.error")}
|
/>
|
||||||
title={t("create.error")}
|
) : null}
|
||||||
/>
|
<SupplierEditorForm
|
||||||
) : null}
|
formId={controller.formId}
|
||||||
<SupplierEditorForm
|
isSubmitting={controller.mutation.isPending}
|
||||||
formId={controller.formId}
|
onSubmit={controller.onSubmit}
|
||||||
isSubmitting={controller.mutation.isPending}
|
/>
|
||||||
onSubmit={controller.onSubmit}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</RdxPageContent>
|
</RdxPageContent>
|
||||||
</RdxPageContainer>
|
</RdxPageContainer>
|
||||||
</RdxPageFrame>
|
</RdxPageFrame>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { RdxPageContainer, RdxPageFrame } from "@repo/rdx-ui/components";
|
import { RdxPageContainer, RdxPageContent, RdxPageFrame } from "@repo/rdx-ui/components";
|
||||||
|
|
||||||
import { useTranslation } from "../../i18n";
|
import { useTranslation } from "../../i18n";
|
||||||
|
|
||||||
@ -19,15 +19,17 @@ export const SupplierEditorSkeleton = () => {
|
|||||||
return (
|
return (
|
||||||
<RdxPageFrame>
|
<RdxPageFrame>
|
||||||
<RdxPageContainer width="content">
|
<RdxPageContainer width="content">
|
||||||
<div aria-hidden className="space-y-6">
|
<RdxPageContent>
|
||||||
<div className="h-10 w-64 animate-pulse rounded-md bg-muted" />
|
<div aria-hidden className="space-y-6">
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
<div className="h-10 w-64 animate-pulse rounded-md bg-muted" />
|
||||||
{SKELETON_FIELDS.map((field) => (
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
<div className="h-16 animate-pulse rounded-md bg-muted" key={field} />
|
{SKELETON_FIELDS.map((field) => (
|
||||||
))}
|
<div className="h-16 animate-pulse rounded-md bg-muted" key={field} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<span className="sr-only">{t("common.loading")}</span>
|
||||||
<span className="sr-only">{t("common.loading")}</span>
|
</RdxPageContent>
|
||||||
</RdxPageContainer>
|
</RdxPageContainer>
|
||||||
</RdxPageFrame>
|
</RdxPageFrame>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -32,18 +32,20 @@ export const SupplierUpdatePage = () => {
|
|||||||
return (
|
return (
|
||||||
<RdxPageFrame>
|
<RdxPageFrame>
|
||||||
<RdxPageContainer width="content">
|
<RdxPageContainer width="content">
|
||||||
<PageErrorState
|
<RdxPageContent>
|
||||||
description={t("detail.error.description")}
|
<PageErrorState
|
||||||
primaryAction={{
|
description={t("detail.error.description")}
|
||||||
label: t("common.actions.retry"),
|
primaryAction={{
|
||||||
onClick: async () => {
|
label: t("common.actions.retry"),
|
||||||
await controller.query.refetch();
|
onClick: async () => {
|
||||||
},
|
await controller.query.refetch();
|
||||||
}}
|
},
|
||||||
secondaryAction={{ label: t("common.actions.back"), onClick: navigateBack }}
|
}}
|
||||||
title={t("detail.error.title")}
|
secondaryAction={{ label: t("common.actions.back"), onClick: navigateBack }}
|
||||||
variant="notFound"
|
title={t("detail.error.title")}
|
||||||
/>
|
variant="notFound"
|
||||||
|
/>
|
||||||
|
</RdxPageContent>
|
||||||
</RdxPageContainer>
|
</RdxPageContainer>
|
||||||
</RdxPageFrame>
|
</RdxPageFrame>
|
||||||
);
|
);
|
||||||
@ -66,21 +68,17 @@ export const SupplierUpdatePage = () => {
|
|||||||
>
|
>
|
||||||
<RdxPageContainer width="content">
|
<RdxPageContainer width="content">
|
||||||
<RdxPageContent>
|
<RdxPageContent>
|
||||||
<div className="rounded-2xl border border-border/60 bg-background p-4 shadow-xs md:p-6">
|
{controller.mutation.isError ? (
|
||||||
<div className="flex min-w-0 flex-col gap-6">
|
<ErrorAlert
|
||||||
{controller.mutation.isError ? (
|
message={controller.mutation.error.message || t("update.error")}
|
||||||
<ErrorAlert
|
title={t("update.error")}
|
||||||
message={controller.mutation.error.message || t("update.error")}
|
/>
|
||||||
title={t("update.error")}
|
) : null}
|
||||||
/>
|
<SupplierEditorForm
|
||||||
) : null}
|
formId={controller.formId}
|
||||||
<SupplierEditorForm
|
isSubmitting={controller.mutation.isPending}
|
||||||
formId={controller.formId}
|
onSubmit={controller.onSubmit}
|
||||||
isSubmitting={controller.mutation.isPending}
|
/>
|
||||||
onSubmit={controller.onSubmit}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</RdxPageContent>
|
</RdxPageContent>
|
||||||
</RdxPageContainer>
|
</RdxPageContainer>
|
||||||
</RdxPageFrame>
|
</RdxPageFrame>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user