From 7c566075828fc9a62efbf7f78603b66f3a9fd76a Mon Sep 17 00:00:00 2001 From: Nathnael Date: Wed, 19 Aug 2026 10:11:48 +0000 Subject: [PATCH 1/4] fix(filters): honor a date filter's own operator, not always "between" DateBody always initialized its op state to DEFAULT_OP.date ("between"), ignoring a def's `operators` restriction. A single-operator exact-date filter (e.g. `operators: ["before"]`) opened on the range UI with no way off it, since OperatorSelect hides itself when there's only one choice. Default to the def's first allowed operator instead. --- .../backoffice/src/components/filters/bodies/DateBody.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/edr-freight-web/backoffice/src/components/filters/bodies/DateBody.tsx b/apps/edr-freight-web/backoffice/src/components/filters/bodies/DateBody.tsx index b2032170d..32970abef 100644 --- a/apps/edr-freight-web/backoffice/src/components/filters/bodies/DateBody.tsx +++ b/apps/edr-freight-web/backoffice/src/components/filters/bodies/DateBody.tsx @@ -15,7 +15,11 @@ import type { FilterBodyProps } from "./TextBody"; // i18n.language !== "en" branch here when this body is first wired into a // record-management page (Phase 4 of the filter-bar rollout). export function DateBody({ def, value, onChange, onClose }: FilterBodyProps) { - const [op, setOp] = useState(value?.op ?? DEFAULT_OP.date); + // DEFAULT_OP.date is always "between" — a def restricted to a single + // non-default operator (e.g. `operators: ["before"]` for an exact-date + // filter) would otherwise open on the range UI with no way to switch off + // it, since OperatorSelect hides itself when there's only one choice. + const [op, setOp] = useState(value?.op ?? def.operators?.[0] ?? DEFAULT_OP.date); // Mantine 9's date inputs speak `YYYY-MM-DD` strings, not Date objects. const [from, setFrom] = useState(value?.v[0]?.slice(0, 10) ?? null); const [to, setTo] = useState(value?.v[1]?.slice(0, 10) ?? null); From 6687def4afc36c36e6c9483e7af982efb297d0f5 Mon Sep 17 00:00:00 2001 From: Nathnael Date: Wed, 19 Aug 2026 10:12:03 +0000 Subject: [PATCH 2/4] feat(reports): use the shared FilterBar instead of ReportFilters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swaps ReportView's bespoke ReportFilters for the same FilterBar/useFilters combo BookingRequestsPage uses — pills, saved-view-ready state, URL sync. toFilterDefs() maps the report catalog's own filter vocabulary (daterange/date/select/multiselect/text) onto FilterDef, matched against every report definition's backend param handling. The search box only shows for reports that actually implement `search` server-side, so it's not a dead control on the rest. ReportFilters.tsx is now dead, removed. --- .../src/components/reports/ReportFilters.tsx | 110 -------------- .../src/components/reports/ReportView.tsx | 139 ++++++++++++++---- 2 files changed, 110 insertions(+), 139 deletions(-) delete mode 100644 apps/edr-freight-web/backoffice/src/components/reports/ReportFilters.tsx diff --git a/apps/edr-freight-web/backoffice/src/components/reports/ReportFilters.tsx b/apps/edr-freight-web/backoffice/src/components/reports/ReportFilters.tsx deleted file mode 100644 index e44e97ec8..000000000 --- a/apps/edr-freight-web/backoffice/src/components/reports/ReportFilters.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { Group, MultiSelect, Select, TextInput } from "@mantine/core"; -import { DateInput, DatePickerInput } from "@mantine/dates"; -import { Search } from "lucide-react"; - -import { getDateRangePresets } from "@/components/common/dateRangePresets"; -import type { ReportFilterDef } from "@/types/reports"; - -export interface ReportFilterValues { - [param: string]: string | undefined; -} - -interface ReportFiltersProps { - filters: ReportFilterDef[]; - values: ReportFilterValues; - onChange: (values: ReportFilterValues) => void; -} - -const toDate = (value: string | undefined): Date | null => (value ? new Date(value) : null); -const fromDate = (value: string | null): string | undefined => value ?? undefined; - -/** Renders one widget per report-declared filter and reports raw param values back up. */ -export function ReportFilters({ filters, values, onChange }: ReportFiltersProps) { - if (!filters.length) return null; - - const set = (patch: ReportFilterValues) => onChange({ ...values, ...patch }); - - return ( - - {filters.map((filter) => { - switch (filter.type) { - case "daterange": - return ( - - set({ [`${filter.key}From`]: fromDate(from), [`${filter.key}To`]: fromDate(to) }) - } - presets={getDateRangePresets()} - radius="md" - size="sm" - clearable - w={230} - /> - ); - case "date": - return ( - set({ [filter.key]: fromDate(d) })} - radius="md" - size="sm" - clearable - w={150} - /> - ); - case "select": - return ( -