From fed29a71fd22aab1eb63ed6376e3c9e55f20e280 Mon Sep 17 00:00:00 2001 From: david Date: Thu, 30 Jul 2026 11:19:16 +0200 Subject: [PATCH] feat: add ProformaStatusSegmentedFilter component and integrate into list-proformas-page --- .../web/proformas/list/ui/components/index.ts | 1 + .../proforma-status-segmented-filter.tsx | 77 ++++++++++++ .../list/ui/pages/list-proformas-page.tsx | 35 ++---- packages/shadcn-ui/package.json | 3 +- .../shadcn-ui/src/components/toggle-group.tsx | 117 +++++++++--------- pnpm-lock.yaml | 3 + 6 files changed, 154 insertions(+), 82 deletions(-) create mode 100644 modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-segmented-filter.tsx diff --git a/modules/customer-invoices/src/web/proformas/list/ui/components/index.ts b/modules/customer-invoices/src/web/proformas/list/ui/components/index.ts index d09eb799..83b4bcec 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/components/index.ts +++ b/modules/customer-invoices/src/web/proformas/list/ui/components/index.ts @@ -1,3 +1,4 @@ export * from "./archive-proforma-dialog"; export * from "./proforma-status-badge"; export * from "./unarchive-proforma-dialog"; +export * from "./proforma-status-segmented-filter"; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-segmented-filter.tsx b/modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-segmented-filter.tsx new file mode 100644 index 00000000..e14e6e4d --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-segmented-filter.tsx @@ -0,0 +1,77 @@ +import { ToggleGroup, ToggleGroupItem } from "@repo/shadcn-ui/components"; +import { cn } from "@repo/shadcn-ui/lib/utils"; + +import { useTranslation } from "../../../../i18n"; +import type { ProformaListStatusFilter } from "../../types/proforma-list-filters"; + +export type ProformaStatusFilter = ProformaListStatusFilter; + +export type ProformaStatusSegmentedFilterProps = { + value: ProformaStatusFilter; + onValueChange: (value: ProformaStatusFilter) => void; + disabled?: boolean; +}; + +const PROFORMA_STATUS_FILTER_OPTIONS = [ + { value: "all", labelKey: "catalog.proformas.status.all.label" }, + { value: "draft", labelKey: "catalog.proformas.status.draft.label" }, + { value: "sent", labelKey: "catalog.proformas.status.sent.label" }, + { value: "approved", labelKey: "catalog.proformas.status.approved.label" }, + { value: "rejected", labelKey: "catalog.proformas.status.rejected.label" }, + { value: "issued", labelKey: "catalog.proformas.status.issued.label" }, +] as const satisfies readonly { + value: ProformaStatusFilter; + labelKey: string; +}[]; + +const isProformaStatusFilter = (value: string): value is ProformaStatusFilter => { + return PROFORMA_STATUS_FILTER_OPTIONS.some((option) => option.value === value); +}; + +export const ProformaStatusSegmentedFilter = ({ + value, + onValueChange, + disabled = false, +}: ProformaStatusSegmentedFilterProps) => { + const { t } = useTranslation(); + + const handleValueChange = (nextValue: string) => { + if (!(nextValue && isProformaStatusFilter(nextValue)) || nextValue === value) { + return; + } + + onValueChange(nextValue); + }; + + return ( + + {PROFORMA_STATUS_FILTER_OPTIONS.map((option) => ( + { + if (option.value === value) { + event.preventDefault(); + } + }} + value={option.value} + > + {t(option.labelKey)} + + ))} + + ); +}; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/pages/list-proformas-page.tsx b/modules/customer-invoices/src/web/proformas/list/ui/pages/list-proformas-page.tsx index fd297b97..2f1d9412 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/pages/list-proformas-page.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/pages/list-proformas-page.tsx @@ -8,18 +8,12 @@ import { ResizableHandle, ResizablePanel, ResizablePanelGroup, - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, } from "@repo/shadcn-ui/components"; import { CheckCircle2Icon, CircleDollarSignIcon, ClockIcon, FileTextIcon, - FilterIcon, PlusIcon, } from "lucide-react"; import { createSearchParams, useNavigate } from "react-router-dom"; @@ -32,10 +26,15 @@ import { prepareDeleteProformaTargets } from "../../../delete/utils"; import { IssueProformaDialog } from "../../../issue-proforma"; import { prepareIssueProformaTarget } from "../../../issue-proforma/utils"; import type { ProformaListRow } from "../../../shared"; -import type { ProformaListScope } from "../../types/proforma-list-filters"; import { useListProformasPageController } from "../../controllers"; +import type { ProformaListScope } from "../../types/proforma-list-filters"; import { ProformaSummaryPanel, ProformasGrid, useProformasGridColumns } from "../blocks"; -import { ArchiveProformaDialog, ProformaStatusBadge, UnarchiveProformaDialog } from "../components"; +import { + ArchiveProformaDialog, + ProformaStatusBadge, + ProformaStatusSegmentedFilter, + UnarchiveProformaDialog, +} from "../components"; interface ScopedListProformasPageProps { scope: ProformaListScope; @@ -136,23 +135,11 @@ const ScopedListProformasPage = ({ scope }: ScopedListProformasPageProps) => { value={listCtrl.search} /> - + />
& { - spacing?: number - orientation?: "horizontal" | "vertical" + spacing?: number; + orientation?: "horizontal" | "vertical"; } >({ size: "default", variant: "default", spacing: 0, orientation: "horizontal", -}) +}); + +const ToggleGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps & { + spacing?: number; + } +>(({ className, variant, size, spacing = 0, children, style, ...props }, ref) => { + const orientation = props.orientation ?? "horizontal"; -function ToggleGroup({ - className, - variant, - size, - spacing = 0, - orientation = "horizontal", - children, - ...props -}: ToggleGroupPrimitive.Props & - VariantProps & { - spacing?: number - orientation?: "horizontal" | "vertical" - }) { return ( - - + {children} - - ) -} + + ); +}); +ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName; -function ToggleGroupItem({ - className, - children, - variant = "default", - size = "default", - ...props -}: TogglePrimitive.Props & VariantProps) { - const context = React.useContext(ToggleGroupContext) +const ToggleGroupItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, children, variant, size, ...props }, ref) => { + const context = React.useContext(ToggleGroupContext); return ( - {children} - - ) -} + + ); +}); +ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName; -export { ToggleGroup, ToggleGroupItem } +export { ToggleGroup, ToggleGroupItem }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cdb4e17b..77cb600d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1383,6 +1383,9 @@ importers: '@hookform/resolvers': specifier: ^5.2.2 version: 5.4.0(react-hook-form@7.76.1(react@19.2.6)) + '@radix-ui/react-toggle-group': + specifier: ^1.1.11 + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) add: specifier: ^2.0.6 version: 2.0.6