Compare commits

..

No commits in common. "35dca3954df94e30a09273c6b9a5f36af5c93a2d" and "902f7806ed8a525af29e9e1b4b157a2384abff24" have entirely different histories.

17 changed files with 297 additions and 334 deletions

View File

@ -24,13 +24,13 @@ export const SettingsLayout = ({ sections }: SettingsLayoutProps) => {
return ( return (
<RdxPageFrame> <RdxPageFrame>
<RdxPageContainer width="full"> <RdxPageContainer className="space-y-6" width="full">
<RdxPageContent spacing="default"> <RdxPageHeader
<RdxPageHeader description={t("web.settings.description")}
description={t("web.settings.description")} title={t("web.settings.title")}
title={t("web.settings.title")} />
/>
<RdxPageContent spacing="default">
<div className="grid gap-6 lg:grid-cols-[18rem_minmax(0,1fr)]"> <div className="grid gap-6 lg:grid-cols-[18rem_minmax(0,1fr)]">
<aside className="min-w-0 lg:sticky lg:top-6 lg:self-start"> <aside className="min-w-0 lg:sticky lg:top-6 lg:self-start">
<SettingsNavigation sections={sections} /> <SettingsNavigation sections={sections} />

View File

@ -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. En Create/Update, Detail/Show y LIST, la composición canónica es RdxPageFrame → RdxPageContainer → RdxPageContent: errores, alertas, secciones semánticas y contenido funcional son hijos directos de RdxPageContent; los dialogs son hermanos del frame; RdxPanel se reserva para secciones semánticas reales. Regla: Las Pages no deben reconstruir estas responsabilidades con wrappers HTML locales. En Create/Update y Detail/Show, la composición canónica es RdxPageFrame → RdxPageContainer → RdxPageContent: errores, alertas, secciones semánticas y contenido funcional 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

View File

@ -22,18 +22,6 @@ Los componentes aquí citados se definen en `ui-system.md`; este documento expli
</RdxPageFrame> </RdxPageFrame>
``` ```
El error inicial bloqueante conserva el mismo shell:
```tsx
<RdxPageFrame>
<RdxPageContainer width="content">
<RdxPageContent>
<PageErrorState />
</RdxPageContent>
</RdxPageContainer>
</RdxPageFrame>
```
- El CTA de creación permanece durante loading, empty y errores de refresh; puede desaparecer ante error inicial bloqueante. - 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`. - Búsqueda, filtros y controles viven en `DataTable.topContent`.
- TanStack Query conserva los datos remotos y la paginación server-side. - TanStack Query conserva los datos remotos y la paginación server-side.
@ -41,7 +29,6 @@ El error inicial bloqueante conserva el mismo shell:
- No se calculan contadores globales a partir de una página parcial. - 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`. - 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. - Una vista de composición de dominio puede encapsular el shell si permanece pura.
- Los listados no reconstruyen con HTML local las responsabilidades de Frame, Container o Content. Search, filtros, tabs, tabla y paginación son contenido funcional de `RdxPageContent`; el scroll interno de una tabla o grid es legítimo y no sustituye el scroll de página.
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. 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.

View File

@ -152,8 +152,6 @@ Excepción: un detalle documental puede configurar el contenido del frame con `o
Los avisos, errores inline, secciones y contenido funcional deben ser hijos directos cuando pertenezcan al mismo nivel visual. Los avisos, errores inline, secciones y contenido funcional deben ser hijos directos cuando pertenezcan al mismo nivel visual.
`RdxPanel` no es un wrapper genérico. Se usa únicamente cuando existe una sección con identidad visual o semántica propia. `RdxPanel` no es un wrapper genérico. Se usa únicamente cuando existe una sección con identidad visual o semántica propia.
En LIST, toolbar, búsqueda, filtros, tabs, tabla/lista/grid y paginación son bloques funcionales de `RdxPageContent`. Una tabla no implica un `RdxPanel`, y su scroll horizontal, virtualizado o interno es legítimo: no sustituye el scroll estructural de `RdxPageFrame`. No se anidan containers ni contents para filtros, alertas o paginación.
```tsx ```tsx
<RdxPageContent> <RdxPageContent>
{error ? <ErrorAlert /> : null} {error ? <ErrorAlert /> : null}

View File

@ -36,18 +36,16 @@ export const AuditPage = () => {
return ( return (
<RdxPageFrame> <RdxPageFrame>
<RdxPageContainer width="content"> <RdxPageContainer width="content">
<RdxPageContent> <PageErrorState
<PageErrorState description={t("audit.error.load")}
description={t("audit.error.load")} primaryAction={{
primaryAction={{ label: t("audit.retry"),
label: t("audit.retry"), onClick: retry,
onClick: retry, isLoading: controller.isFetching,
isLoading: controller.isFetching, }}
}} title={t("audit.error.load")}
title={t("audit.error.load")} variant="unknown"
variant="unknown" />
/>
</RdxPageContent>
</RdxPageContainer> </RdxPageContainer>
</RdxPageFrame> </RdxPageFrame>
); );
@ -98,9 +96,9 @@ export const AuditPage = () => {
)} )}
</div> </div>
</div> </div>
<AuditEntrySheet entryId={controller.entry} onClose={controller.closeEntry} />
</RdxPageContent> </RdxPageContent>
</RdxPageContainer> </RdxPageContainer>
<AuditEntrySheet entryId={controller.entry} onClose={controller.closeEntry} />
</RdxPageFrame> </RdxPageFrame>
); );
}; };

View File

@ -1,5 +1,5 @@
import { PageErrorState } from "@erp/core/components"; import { PageErrorState } from "@erp/core/components";
import { RdxBadge } from "@repo/rdx-ui/components"; import { RdxBadge, RdxPageContainer } from "@repo/rdx-ui/components";
import { import {
Button, Button,
Card, Card,
@ -30,23 +30,27 @@ export const PaymentMethodsSettingsPage = () => {
if (ctrl.pageError) { if (ctrl.pageError) {
return ( return (
<PageErrorState <RdxPageContainer width="full">
description={ctrl.pageError.description} <div className="mx-auto w-full max-w-3xl">
primaryAction={ <PageErrorState
ctrl.pageError.canRetry description={ctrl.pageError.description}
? { primaryAction={
label: ctrl.isRetrying ctrl.pageError.canRetry
? t("settings.paymentMethods.pageError.actions.retrying") ? {
: t("settings.paymentMethods.pageError.actions.retry"), label: ctrl.isRetrying
onClick: ctrl.retry, ? t("settings.paymentMethods.pageError.actions.retrying")
isLoading: ctrl.isRetrying, : t("settings.paymentMethods.pageError.actions.retry"),
} onClick: ctrl.retry,
: undefined isLoading: ctrl.isRetrying,
} }
reference={ctrl.pageError.reference} : undefined
title={ctrl.pageError.title} }
variant={ctrl.pageError.variant} reference={ctrl.pageError.reference}
/> title={ctrl.pageError.title}
variant={ctrl.pageError.variant}
/>
</div>
</RdxPageContainer>
); );
} }

View File

@ -1,4 +1,5 @@
import { PageErrorState } from "@erp/core/components"; import { PageErrorState } from "@erp/core/components";
import { RdxPageContainer } from "@repo/rdx-ui/components";
import { import {
Button, Button,
Card, Card,
@ -37,23 +38,27 @@ export const PaymentTermsSettingsPage = () => {
if (ctrl.pageError) { if (ctrl.pageError) {
return ( return (
<PageErrorState <RdxPageContainer width="full">
description={ctrl.pageError.description} <div className="mx-auto w-full max-w-3xl">
primaryAction={ <PageErrorState
ctrl.pageError.canRetry description={ctrl.pageError.description}
? { primaryAction={
label: ctrl.isRetrying ctrl.pageError.canRetry
? t("settings.paymentTerms.pageError.actions.retrying") ? {
: t("settings.paymentTerms.pageError.actions.retry"), label: ctrl.isRetrying
onClick: ctrl.retry, ? t("settings.paymentTerms.pageError.actions.retrying")
isLoading: ctrl.isRetrying, : t("settings.paymentTerms.pageError.actions.retry"),
} onClick: ctrl.retry,
: undefined isLoading: ctrl.isRetrying,
} }
reference={ctrl.pageError.reference} : undefined
title={ctrl.pageError.title} }
variant={ctrl.pageError.variant} reference={ctrl.pageError.reference}
/> title={ctrl.pageError.title}
variant={ctrl.pageError.variant}
/>
</div>
</RdxPageContainer>
); );
} }

View File

@ -1,5 +1,5 @@
import { PageErrorState } from "@erp/core/components"; import { PageErrorState } from "@erp/core/components";
import { RdxBadge } from "@repo/rdx-ui/components"; import { RdxBadge, RdxPageContainer } from "@repo/rdx-ui/components";
import { Button, Card, CardContent, CardHeader } from "@repo/shadcn-ui/components"; import { Button, Card, CardContent, CardHeader } from "@repo/shadcn-ui/components";
import { cn } from "@repo/shadcn-ui/lib/utils"; import { cn } from "@repo/shadcn-ui/lib/utils";
import { CircleIcon, LandmarkIcon, PlusIcon } from "lucide-react"; import { CircleIcon, LandmarkIcon, PlusIcon } from "lucide-react";
@ -23,23 +23,27 @@ export const TaxRegimesSettingsPage = () => {
if (ctrl.pageError) { if (ctrl.pageError) {
return ( return (
<PageErrorState <RdxPageContainer width="full">
description={ctrl.pageError.description} <div className="mx-auto w-full max-w-3xl">
primaryAction={ <PageErrorState
ctrl.pageError.canRetry description={ctrl.pageError.description}
? { primaryAction={
label: ctrl.isRetrying ctrl.pageError.canRetry
? t("settings.taxRegimes.pageError.actions.retrying") ? {
: t("settings.taxRegimes.pageError.actions.retry"), label: ctrl.isRetrying
onClick: ctrl.retry, ? t("settings.taxRegimes.pageError.actions.retrying")
isLoading: ctrl.isRetrying, : t("settings.taxRegimes.pageError.actions.retry"),
} onClick: ctrl.retry,
: undefined isLoading: ctrl.isRetrying,
} }
reference={ctrl.pageError.reference} : undefined
title={ctrl.pageError.title} }
variant={ctrl.pageError.variant} reference={ctrl.pageError.reference}
/> title={ctrl.pageError.title}
variant={ctrl.pageError.variant}
/>
</div>
</RdxPageContainer>
); );
} }

View File

@ -127,8 +127,7 @@ export const IssuedInvoicesStatusSegmentedFilter = ({
}: IssuedInvoicesStatusSegmentedFilterProps) => { }: IssuedInvoicesStatusSegmentedFilterProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const handleValueChange = (nextValues: string[]) => { const handleValueChange = (nextValue: string) => {
const nextValue = nextValues[0];
if (!(nextValue && isIssuedInvoiceStatusFilter(nextValue)) || nextValue === value) { if (!(nextValue && isIssuedInvoiceStatusFilter(nextValue)) || nextValue === value) {
return; return;
} }
@ -139,22 +138,21 @@ export const IssuedInvoicesStatusSegmentedFilter = ({
return ( return (
<ToggleGroup <ToggleGroup
aria-label={t("pages.issued_invoices.list.filters.status.label", "Filtrar por estado")} aria-label={t("pages.issued_invoices.list.filters.status.label", "Filtrar por estado")}
className="flex w-full flex-wrap justify-start gap-4" className="flex w-full flex-wrap justify-start gap-1"
disabled={disabled} disabled={disabled}
multiple={false}
onValueChange={handleValueChange} onValueChange={handleValueChange}
size="sm" size="sm"
spacing={1} spacing={1}
value={[value]} type="single"
value={value}
variant="outline" variant="outline"
> >
{ISSUED_INVOICE_STATUS_FILTER_OPTIONS.map((option) => ( {ISSUED_INVOICE_STATUS_FILTER_OPTIONS.map((option) => (
<ToggleGroupItem <ToggleGroupItem
className={cn( className={cn(
"group h-auto min-w-fit rounded-md border-transparent px-3 py-2 text-xs sm:text-sm shadow-none transition-colors", "group h-auto min-w-fit rounded-full border-transparent px-3 py-2 text-xs shadow-none transition-colors sm:text-sm",
"text-muted-foreground hover:border-border hover:bg-muted/60 hover:text-foreground", "text-muted-foreground hover:border-border hover:bg-muted/60 hover:text-foreground",
"data-[state=on]:shadow-none", "data-[state=on]:shadow-none",
option.activeClassName option.activeClassName
)} )}
key={option.value} key={option.value}
@ -166,7 +164,7 @@ export const IssuedInvoicesStatusSegmentedFilter = ({
value={option.value} value={option.value}
> >
<span className="flex items-center gap-2"> <span className="flex items-center gap-2">
{option.icon ? <option.icon className="size-4" /> : null} {option.icon ? <option.icon className="size-3.5" /> : null}
<span> <span>
{t( {t(
`catalog.issued_invoices.status.${option.value.toLowerCase()}.label`, `catalog.issued_invoices.status.${option.value.toLowerCase()}.label`,

View File

@ -3,7 +3,6 @@ import { useReturnToNavigation } from "@erp/core/hooks";
import { import {
LogoVerifactu, LogoVerifactu,
RdxPageContainer, RdxPageContainer,
RdxPageContent,
RdxPageFrame, RdxPageFrame,
RdxPageHeader, RdxPageHeader,
} from "@repo/rdx-ui/components"; } from "@repo/rdx-ui/components";
@ -66,36 +65,34 @@ export const ListIssuedInvoicesPage = () => {
return ( return (
<RdxPageFrame> <RdxPageFrame>
<RdxPageContainer width="content"> <RdxPageContainer width="content">
<RdxPageContent> <PageErrorState
<PageErrorState description={pageError.description}
description={pageError.description} primaryAction={
primaryAction={ pageError.canRetry
pageError.canRetry ? {
? { label: isRetrying
label: isRetrying ? t("pages.issued_invoices.list.actions.retrying")
? t("pages.issued_invoices.list.actions.retrying") : t("pages.issued_invoices.list.actions.retry"),
: t("pages.issued_invoices.list.actions.retry"), onClick: retryList,
onClick: retryList, isLoading: isRetrying,
isLoading: isRetrying, }
} : {
: { label: t("pages.issued_invoices.list.actions.go_home"),
label: t("pages.issued_invoices.list.actions.go_home"), onClick: () => navigate("/"),
onClick: () => navigate("/"), }
} }
} reference={pageError.reference}
reference={pageError.reference} secondaryAction={
secondaryAction={ pageError.canRetry
pageError.canRetry ? {
? { label: t("pages.issued_invoices.list.actions.go_home"),
label: t("pages.issued_invoices.list.actions.go_home"), onClick: () => navigate("/"),
onClick: () => navigate("/"), }
} : undefined
: undefined }
} title={pageError.title}
title={pageError.title} variant={pageError.variant}
variant={pageError.variant} />
/>
</RdxPageContent>
</RdxPageContainer> </RdxPageContainer>
</RdxPageFrame> </RdxPageFrame>
); );
@ -103,80 +100,78 @@ export const ListIssuedInvoicesPage = () => {
return ( return (
<RdxPageFrame> <RdxPageFrame>
<RdxPageContainer width="full"> <RdxPageContainer className="space-y-6" width="full">
<RdxPageContent> <RdxPageHeader
<RdxPageHeader actions={
actions={ <Button
<Button aria-label={t("pages.issued_invoices.create.title")}
aria-label={t("pages.issued_invoices.create.title")} className="hidden"
className="hidden" onClick={() => navigate("/issued-invoices/create")}
onClick={() => navigate("/issued-invoices/create")} >
> <PlusIcon aria-hidden className="mr-2 size-4" />
<PlusIcon aria-hidden className="mr-2 size-4" /> {t("pages.issued_invoices.create.title")}
{t("pages.issued_invoices.create.title")} </Button>
</Button> }
description={t("pages.issued_invoices.list.description")}
title={t("pages.issued_invoices.list.title")}
/>
<div className="mx-auto w-full space-y-4">
{hasRefreshError && refreshError ? (
<ErrorAlert message={refreshError.message} title={refreshError.title} />
) : null}
<Alert className="bg-green-50 text-green-800 flex items-center justify-between gap-6">
<div>
<AlertTitle className="font-semibold text-green-800">¡Atención!</AlertTitle>
<AlertDescription className="text-green-800">
Las facturas de esta pantalla son de solo lectura. Se generan automáticamente al
emitir una proforma aprobada y no se pueden modificar ni eliminar. Solo puedes
descargar el PDF de cada factura.
</AlertDescription>
</div>
<LogoVerifactu className="text-green-800 w-50" color="" />
</Alert>
<IssuedInvoicesGrid
columns={columns}
data={listCtrl.data}
fetching={listCtrl.isFetching}
loading={listCtrl.isLoading}
onPageChange={listCtrl.setPageIndex}
onPageSizeChange={listCtrl.setPageSize}
onRowClick={handleOpenDetail}
onSortChange={listCtrl.setSort}
pageIndex={listCtrl.pageIndex}
pageSize={listCtrl.pageSize}
searching={isSearchPending}
sort={listCtrl.sort}
topContent={
<div className="space-y-5">
<div className="flex flex-col gap-4 xl:flex-row xl:items-center xl:justify-between">
<div className="w-full">
<SimpleSearchInput
loading={listCtrl.isLoading}
onPendingChange={setIsSearchPending}
onSearchChange={listCtrl.setSearchValue}
placeholder={t("pages.issued_invoices.list.search_placeholder")}
value={listCtrl.search}
/>
</div>
</div>
<IssuedInvoicesStatusSegmentedFilter
counts={listCtrl.data.statusCounts}
disabled={listCtrl.isLoading}
onValueChange={listCtrl.setStatusFilter}
value={listCtrl.statusFilter}
/>
</div>
} }
description={t("pages.issued_invoices.list.description")}
title={t("pages.issued_invoices.list.title")}
/> />
<div className="space-y-4"> {listCtrl.data.totalItems === 0 ? <IssuedInvoicesEmptyGrid /> : null}
{hasRefreshError && refreshError ? ( </div>
<ErrorAlert message={refreshError.message} title={refreshError.title} />
) : null}
<Alert className="bg-green-50 text-green-800 flex items-center justify-between gap-6">
<div>
<AlertTitle className="font-semibold text-green-800">¡Atención!</AlertTitle>
<AlertDescription className="text-green-800">
Las facturas de esta pantalla son de solo lectura. Se generan automáticamente al
emitir una proforma aprobada y no se pueden modificar ni eliminar. Solo puedes
descargar el PDF de cada factura.
</AlertDescription>
</div>
<LogoVerifactu className="text-green-800 w-50" color="" />
</Alert>
<IssuedInvoicesGrid
columns={columns}
data={listCtrl.data}
fetching={listCtrl.isFetching}
loading={listCtrl.isLoading}
onPageChange={listCtrl.setPageIndex}
onPageSizeChange={listCtrl.setPageSize}
onRowClick={handleOpenDetail}
onSortChange={listCtrl.setSort}
pageIndex={listCtrl.pageIndex}
pageSize={listCtrl.pageSize}
searching={isSearchPending}
sort={listCtrl.sort}
topContent={
<div className="space-y-5">
<div className="flex flex-col gap-4 xl:flex-row xl:items-center xl:justify-between">
<div className="w-full">
<SimpleSearchInput
loading={listCtrl.isLoading}
onPendingChange={setIsSearchPending}
onSearchChange={listCtrl.setSearchValue}
placeholder={t("pages.issued_invoices.list.search_placeholder")}
value={listCtrl.search}
/>
</div>
</div>
<IssuedInvoicesStatusSegmentedFilter
counts={listCtrl.data.statusCounts}
disabled={listCtrl.isLoading}
onValueChange={listCtrl.setStatusFilter}
value={listCtrl.statusFilter}
/>
</div>
}
/>
{listCtrl.data.totalItems === 0 ? <IssuedInvoicesEmptyGrid /> : null}
</div>
</RdxPageContent>
</RdxPageContainer> </RdxPageContainer>
</RdxPageFrame> </RdxPageFrame>
); );

View File

@ -11,8 +11,7 @@ import {
import type { ElementType } from "react"; import type { ElementType } from "react";
import { useTranslation } from "../../../../i18n"; import { useTranslation } from "../../../../i18n";
import { getProformaStatusDisplayIcon } from "../../../change-status/helpers"; import type { ProformaStatusCounts } from "../../../shared";
import type { ProformaStatus, ProformaStatusCounts } from "../../../shared";
import type { ProformaListStatusFilter } from "../../types/proforma-list-filters"; import type { ProformaListStatusFilter } from "../../types/proforma-list-filters";
export type ProformaStatusFilter = ProformaListStatusFilter; export type ProformaStatusFilter = ProformaListStatusFilter;
@ -93,9 +92,7 @@ export const ProformaStatusSegmentedFilter = ({
}: ProformaStatusSegmentedFilterProps) => { }: ProformaStatusSegmentedFilterProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const handleValueChange = (nextValues: string[]) => { const handleValueChange = (nextValue: string) => {
const nextValue = nextValues[0];
if (!(nextValue && isProformaStatusFilter(nextValue)) || nextValue === value) { if (!(nextValue && isProformaStatusFilter(nextValue)) || nextValue === value) {
return; return;
} }
@ -106,55 +103,47 @@ export const ProformaStatusSegmentedFilter = ({
return ( return (
<ToggleGroup <ToggleGroup
aria-label={t("pages.proformas.list.filters.status.label")} aria-label={t("pages.proformas.list.filters.status.label")}
className="w-full flex-wrap justify-start gap-4" className="w-full flex-wrap justify-start gap-1"
disabled={disabled} disabled={disabled}
multiple={false}
onValueChange={handleValueChange} onValueChange={handleValueChange}
size="sm" size="sm"
spacing={1} spacing={1}
value={[value]} type="single"
value={value}
variant="outline" variant="outline"
> >
{PROFORMA_STATUS_FILTER_OPTIONS.map((option) => { {PROFORMA_STATUS_FILTER_OPTIONS.map((option) => (
const normalizedStatus = option.value.toLowerCase() as ProformaStatus; <ToggleGroupItem
const Icon = getProformaStatusDisplayIcon(normalizedStatus); className={cn(
"group h-auto min-w-fit rounded-md border-transparent px-3 py-2 text-xs sm:text-sm shadow-none transition-colors",
const disabled = "text-muted-foreground hover:border-border hover:bg-muted/60 hover:text-foreground",
"text-muted-foreground hover:border-border hover:bg-muted/60 hover:text-foreground"; "data-[state=on]:shadow-none",
option.activeClassName
return ( )}
<ToggleGroupItem key={option.value}
className={cn( onPointerDown={(event) => {
"group h-auto min-w-fit rounded-md border-none px-3 py-2 text-xs sm:text-sm shadow-none transition-colors", if (option.value === value) {
"data-[state=on]:shadow-none", event.preventDefault();
disabled, }
option.activeClassName }}
)} value={option.value}
key={option.value} >
onPointerDown={(event) => { <span className="flex items-center gap-2">
if (option.value === value) { {option.icon ? <option.icon className="size-3.5" /> : null}
event.preventDefault(); <span>{t(option.labelKey)}</span>
} <RdxBadge
}} className={cn(
value={option.value} "min-w-5 rounded-full px-1.5 text-[11px] font-semibold tabular-nums",
> "bg-muted text-muted-foreground",
<span className="flex items-center gap-2"> "group-data-[state=on]:bg-white/20 group-data-[state=on]:text-current"
{option.icon ? <option.icon className="size-4" /> : null} )}
<span>{t(option.labelKey)}</span> variant="secondary"
<RdxBadge >
className={cn( {counts[option.countKey]}
"min-w-5 rounded-full px-1.5 text-[11px] font-semibold tabular-nums", </RdxBadge>
"bg-muted text-muted-foreground", </span>
"group-data-[state=on]:bg-white/20 group-data-[state=on]:text-current" </ToggleGroupItem>
)} ))}
variant="secondary"
>
{counts[option.countKey]}
</RdxBadge>
</span>
</ToggleGroupItem>
);
})}
</ToggleGroup> </ToggleGroup>
); );
}; };

View File

@ -1,11 +1,6 @@
import { PageErrorState, SimpleSearchInput } from "@erp/core/components"; import { PageErrorState, SimpleSearchInput } from "@erp/core/components";
import { useDataSource, useReturnToNavigation } from "@erp/core/hooks"; import { useDataSource, useReturnToNavigation } from "@erp/core/hooks";
import { import { type DataTableMeta, RdxPageContainer } from "@repo/rdx-ui/components";
type DataTableMeta,
RdxPageContainer,
RdxPageContent,
RdxPageFrame,
} from "@repo/rdx-ui/components";
import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers"; import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers";
import { import {
Button, Button,
@ -463,32 +458,30 @@ const ScopedListProformasPage = ({ scope }: ScopedListProformasPageProps) => {
if (pageError) { if (pageError) {
return ( return (
<RdxPageFrame> <RdxPageContainer width="full">
<RdxPageContainer width="content"> <div className="mx-auto w-full max-w-3xl">
<RdxPageContent> <PageErrorState
<PageErrorState description={pageError.description}
description={pageError.description} primaryAction={
primaryAction={ pageError.canRetry
pageError.canRetry ? {
? { label: isRetrying
label: isRetrying ? t("pages.proformas.list.actions.retrying")
? t("pages.proformas.list.actions.retrying") : t("pages.proformas.list.actions.retry"),
: t("pages.proformas.list.actions.retry"), onClick: retryList,
onClick: retryList, isLoading: isRetrying,
isLoading: isRetrying, }
} : {
: { label: t("pages.proformas.list.actions.go_home"),
label: t("pages.proformas.list.actions.go_home"), onClick: goHome,
onClick: goHome, }
} }
} reference={pageError.reference}
reference={pageError.reference} title={pageError.title}
title={pageError.title} variant={pageError.variant}
variant={pageError.variant} />
/> </div>
</RdxPageContent> </RdxPageContainer>
</RdxPageContainer>
</RdxPageFrame>
); );
} }

View File

@ -46,9 +46,9 @@ export const ScopedListProformasPageView = ({
{content} {content}
</RdxPageContent> </RdxPageContent>
</RdxPageContainer>
{dialogs} {dialogs}
</RdxPageContainer>
</RdxPageFrame> </RdxPageFrame>
); );
}; };

View File

@ -4,8 +4,6 @@ import {
DataTableColumnHeader, DataTableColumnHeader,
RdxBadge, RdxBadge,
RdxPageContainer, RdxPageContainer,
RdxPageContent,
RdxPageFrame,
type RdxRowAction, type RdxRowAction,
RdxRowActions, RdxRowActions,
SkeletonDataTable, SkeletonDataTable,
@ -269,22 +267,20 @@ export const ListRecurringProformasPage = () => {
if (hasBlockingError && pageError) { if (hasBlockingError && pageError) {
return ( return (
<RdxPageFrame> <RdxPageContainer width="full">
<RdxPageContainer width="content"> <div className="mx-auto w-full max-w-3xl">
<RdxPageContent> <PageErrorState
<PageErrorState description={pageError.description}
description={pageError.description} primaryAction={{
primaryAction={{ label: t("recurring_proformas.list.error.retry"),
label: t("recurring_proformas.list.error.retry"), onClick: retryList,
onClick: retryList, isLoading: isRetrying,
isLoading: isRetrying, }}
}} title={pageError.title}
title={pageError.title} variant="unknown"
variant="unknown" />
/> </div>
</RdxPageContent> </RdxPageContainer>
</RdxPageContainer>
</RdxPageFrame>
); );
} }

View File

@ -46,9 +46,9 @@ export const ListCustomersPageView = ({
{content} {content}
</RdxPageContent> </RdxPageContent>
</RdxPageContainer>
{dialogs} {dialogs}
</RdxPageContainer>
</RdxPageFrame> </RdxPageFrame>
); );
}; };

View File

@ -1,6 +1,6 @@
import { PageErrorState, SimpleSearchInput } from "@erp/core/components"; import { PageErrorState, SimpleSearchInput } from "@erp/core/components";
import { useReturnToNavigation } from "@erp/core/hooks"; import { useReturnToNavigation } from "@erp/core/hooks";
import { RdxPageContainer, RdxPageContent, RdxPageFrame } from "@repo/rdx-ui/components"; import { RdxPageContainer, RdxPageFrame } from "@repo/rdx-ui/components";
import { import {
Button, Button,
ResizableHandle, ResizableHandle,
@ -93,26 +93,24 @@ export const ListCustomersPage = () => {
return ( return (
<RdxPageFrame> <RdxPageFrame>
<RdxPageContainer width="content"> <RdxPageContainer width="content">
<RdxPageContent> <PageErrorState
<PageErrorState description={pageError.description}
description={pageError.description} primaryAction={
primaryAction={ pageError.canRetry
pageError.canRetry ? {
? { label: isRetrying ? t("list.actions.retrying") : t("list.actions.retry"),
label: isRetrying ? t("list.actions.retrying") : t("list.actions.retry"), onClick: retryList,
onClick: retryList, isLoading: isRetrying,
isLoading: isRetrying, }
} : {
: { label: t("list.actions.go_home"),
label: t("list.actions.go_home"), onClick: goHome,
onClick: goHome, }
} }
} reference={pageError.reference}
reference={pageError.reference} title={pageError.title}
title={pageError.title} variant={pageError.variant}
variant={pageError.variant} />
/>
</RdxPageContent>
</RdxPageContainer> </RdxPageContainer>
</RdxPageFrame> </RdxPageFrame>
); );

View File

@ -39,19 +39,17 @@ export const SuppliersListPage = () => {
return ( return (
<RdxPageFrame> <RdxPageFrame>
<RdxPageContainer width="content"> <RdxPageContainer width="content">
<RdxPageContent> <PageErrorState
<PageErrorState description={t("list.error.description")}
description={t("list.error.description")} primaryAction={{
primaryAction={{ label: t("common.actions.retry"),
label: t("common.actions.retry"), onClick: async () => {
onClick: async () => { await controller.refetch();
await controller.refetch(); },
}, }}
}} title={t("list.error.title")}
title={t("list.error.title")} variant="unknown"
variant="unknown" />
/>
</RdxPageContent>
</RdxPageContainer> </RdxPageContainer>
</RdxPageFrame> </RdxPageFrame>
); );