mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
feat(freight-backoffice): consolidate Mantine from/to date filters into range pickers
Replaces every remaining split from/to Mantine DateInput/DatePickerInput pair with a single DatePickerInput type="range", sharing one preset list (Today, Last 7/30 days, this/last month, YTD) via getDateRangePresets(). Covers ListControls (18 consumers), FleetResourcePage, ContractRequestsPage, BookingRequestsPage (created + scheduled ranges), WagonCancellationsPage, BatchBoardPage, ClearanceDocumentsPage, ShipmentRequestsPage. Native Mantine range picker, not the shadcn DateRangePicker, to match each page's existing design system instead of clashing with it. Reports date-range filter (ReportFilters.tsx) intentionally left untouched.
This commit is contained in:
@@ -2,6 +2,7 @@ import { Button, Group, TextInput } from "@mantine/core";
|
|||||||
import { DatePickerInput } from "@mantine/dates";
|
import { DatePickerInput } from "@mantine/dates";
|
||||||
import { Search, X } from "lucide-react";
|
import { Search, X } from "lucide-react";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
import { getDateRangePresets } from "./dateRangePresets";
|
||||||
|
|
||||||
export interface ListControlsProps {
|
export interface ListControlsProps {
|
||||||
search: string;
|
search: string;
|
||||||
@@ -54,28 +55,19 @@ const ListControls = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{showDateRange && (
|
{showDateRange && (
|
||||||
<>
|
<DatePickerInput
|
||||||
<DatePickerInput
|
type="range"
|
||||||
label={dateLabel ? `${dateLabel} from` : "From"}
|
label={dateLabel ?? "Date range"}
|
||||||
placeholder="Any"
|
placeholder="Any"
|
||||||
value={dateFrom}
|
value={[dateFrom, dateTo]}
|
||||||
onChange={onDateFromChange}
|
onChange={([from, to]) => {
|
||||||
// Cannot start after it ends — the picker refuses the invalid range
|
onDateFromChange(from);
|
||||||
// instead of silently returning nothing.
|
onDateToChange(to);
|
||||||
maxDate={dateTo ?? undefined}
|
}}
|
||||||
clearable
|
presets={getDateRangePresets()}
|
||||||
w={150}
|
clearable
|
||||||
/>
|
w={230}
|
||||||
<DatePickerInput
|
/>
|
||||||
label={dateLabel ? `${dateLabel} to` : "To"}
|
|
||||||
placeholder="Any"
|
|
||||||
value={dateTo}
|
|
||||||
onChange={onDateToChange}
|
|
||||||
minDate={dateFrom ?? undefined}
|
|
||||||
clearable
|
|
||||||
w={150}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import {
|
||||||
|
format,
|
||||||
|
startOfDay,
|
||||||
|
endOfDay,
|
||||||
|
startOfMonth,
|
||||||
|
endOfMonth,
|
||||||
|
startOfYear,
|
||||||
|
subDays,
|
||||||
|
subMonths,
|
||||||
|
} from "date-fns";
|
||||||
|
import type { DatePickerPreset } from "@mantine/dates";
|
||||||
|
|
||||||
|
const iso = (date: Date) => format(date, "yyyy-MM-dd");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared "Today / Last 7 days / …" presets for every Mantine
|
||||||
|
* `<DatePickerInput type="range" presets={getDateRangePresets()} />` in the app,
|
||||||
|
* so every from/to filter offers the same shortcuts. Computed fresh per call
|
||||||
|
* (not a module-level constant) so "Today" stays today.
|
||||||
|
*/
|
||||||
|
export function getDateRangePresets(): DatePickerPreset<"range">[] {
|
||||||
|
const today = new Date();
|
||||||
|
return [
|
||||||
|
{ label: "Today", value: [iso(startOfDay(today)), iso(endOfDay(today))] },
|
||||||
|
{
|
||||||
|
label: "Yesterday",
|
||||||
|
value: [iso(startOfDay(subDays(today, 1))), iso(endOfDay(subDays(today, 1)))],
|
||||||
|
},
|
||||||
|
{ label: "Last 7 days", value: [iso(startOfDay(subDays(today, 6))), iso(endOfDay(today))] },
|
||||||
|
{ label: "Last 30 days", value: [iso(startOfDay(subDays(today, 29))), iso(endOfDay(today))] },
|
||||||
|
{ label: "This month", value: [iso(startOfMonth(today)), iso(endOfDay(today))] },
|
||||||
|
{
|
||||||
|
label: "Last month",
|
||||||
|
value: [
|
||||||
|
iso(startOfMonth(subMonths(today, 1))),
|
||||||
|
iso(endOfMonth(subMonths(today, 1))),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ label: "Year to date", value: [iso(startOfYear(today)), iso(endOfDay(today))] },
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -13,7 +13,8 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { DateInput } from "@mantine/dates";
|
import { DatePickerInput } from "@mantine/dates";
|
||||||
|
import { getDateRangePresets } from "@/components/common/dateRangePresets";
|
||||||
import { useDebouncedValue } from "@mantine/hooks";
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
import {
|
import {
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
@@ -764,53 +765,33 @@ export default function BookingRequestsPage() {
|
|||||||
radius="lg"
|
radius="lg"
|
||||||
style={{ minWidth: 140 }}
|
style={{ minWidth: 140 }}
|
||||||
/>
|
/>
|
||||||
<DateInput
|
<DatePickerInput
|
||||||
placeholder="Created from"
|
type="range"
|
||||||
value={createdFrom}
|
placeholder="Created date range"
|
||||||
onChange={(v) => {
|
value={[createdFrom, createdTo]}
|
||||||
setCreatedFrom(v ? new Date(v) : null);
|
onChange={([from, to]) => {
|
||||||
|
setCreatedFrom(from ? new Date(from) : null);
|
||||||
|
setCreatedTo(to ? new Date(to) : null);
|
||||||
resetPage();
|
resetPage();
|
||||||
}}
|
}}
|
||||||
maxDate={createdTo ?? undefined}
|
presets={getDateRangePresets()}
|
||||||
clearable
|
clearable
|
||||||
radius="lg"
|
radius="lg"
|
||||||
style={{ minWidth: 140 }}
|
style={{ minWidth: 220 }}
|
||||||
/>
|
/>
|
||||||
<DateInput
|
<DatePickerInput
|
||||||
placeholder="Created to"
|
type="range"
|
||||||
value={createdTo}
|
placeholder="Scheduled date range"
|
||||||
onChange={(v) => {
|
value={[scheduledFrom, scheduledTo]}
|
||||||
setCreatedTo(v ? new Date(v) : null);
|
onChange={([from, to]) => {
|
||||||
|
setScheduledFrom(from ? new Date(from) : null);
|
||||||
|
setScheduledTo(to ? new Date(to) : null);
|
||||||
resetPage();
|
resetPage();
|
||||||
}}
|
}}
|
||||||
minDate={createdFrom ?? undefined}
|
presets={getDateRangePresets()}
|
||||||
clearable
|
clearable
|
||||||
radius="lg"
|
radius="lg"
|
||||||
style={{ minWidth: 140 }}
|
style={{ minWidth: 230 }}
|
||||||
/>
|
|
||||||
<DateInput
|
|
||||||
placeholder="Scheduled from"
|
|
||||||
value={scheduledFrom}
|
|
||||||
onChange={(v) => {
|
|
||||||
setScheduledFrom(v ? new Date(v) : null);
|
|
||||||
resetPage();
|
|
||||||
}}
|
|
||||||
maxDate={scheduledTo ?? undefined}
|
|
||||||
clearable
|
|
||||||
radius="lg"
|
|
||||||
style={{ minWidth: 150 }}
|
|
||||||
/>
|
|
||||||
<DateInput
|
|
||||||
placeholder="Scheduled to"
|
|
||||||
value={scheduledTo}
|
|
||||||
onChange={(v) => {
|
|
||||||
setScheduledTo(v ? new Date(v) : null);
|
|
||||||
resetPage();
|
|
||||||
}}
|
|
||||||
minDate={scheduledFrom ?? undefined}
|
|
||||||
clearable
|
|
||||||
radius="lg"
|
|
||||||
style={{ minWidth: 150 }}
|
|
||||||
/>
|
/>
|
||||||
{activeFilterCount > 0 ? (
|
{activeFilterCount > 0 ? (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { DateInput } from "@mantine/dates";
|
import { DatePickerInput } from "@mantine/dates";
|
||||||
|
import { getDateRangePresets } from "@/components/common/dateRangePresets";
|
||||||
import { useDebouncedValue } from "@mantine/hooks";
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import { Search, XCircle } from "lucide-react";
|
import { Search, XCircle } from "lucide-react";
|
||||||
@@ -304,29 +305,19 @@ export default function WagonCancellationsPage() {
|
|||||||
w={190}
|
w={190}
|
||||||
radius="md"
|
radius="md"
|
||||||
/>
|
/>
|
||||||
<DateInput
|
<DatePickerInput
|
||||||
placeholder="From"
|
type="range"
|
||||||
value={from}
|
placeholder="Date range"
|
||||||
onChange={(v) => {
|
value={[from, to]}
|
||||||
setFrom(v ? new Date(v) : null);
|
onChange={([newFrom, newTo]) => {
|
||||||
|
setFrom(newFrom ? new Date(newFrom) : null);
|
||||||
|
setTo(newTo ? new Date(newTo) : null);
|
||||||
resetPage();
|
resetPage();
|
||||||
}}
|
}}
|
||||||
maxDate={to ?? undefined}
|
presets={getDateRangePresets()}
|
||||||
clearable
|
clearable
|
||||||
radius="md"
|
radius="md"
|
||||||
style={{ minWidth: 140 }}
|
style={{ minWidth: 220 }}
|
||||||
/>
|
|
||||||
<DateInput
|
|
||||||
placeholder="To"
|
|
||||||
value={to}
|
|
||||||
onChange={(v) => {
|
|
||||||
setTo(v ? new Date(v) : null);
|
|
||||||
resetPage();
|
|
||||||
}}
|
|
||||||
minDate={from ?? undefined}
|
|
||||||
clearable
|
|
||||||
radius="md"
|
|
||||||
style={{ minWidth: 140 }}
|
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
TextInput,
|
TextInput,
|
||||||
ThemeIcon,
|
ThemeIcon,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { DateInput } from "@mantine/dates";
|
import { DatePickerInput } from "@mantine/dates";
|
||||||
|
import { getDateRangePresets } from "@/components/common/dateRangePresets";
|
||||||
import { useDebouncedValue } from "@mantine/hooks";
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||||
import { FileText, Inbox, RefreshCw, Search, User, X } from "lucide-react";
|
import { FileText, Inbox, RefreshCw, Search, User, X } from "lucide-react";
|
||||||
@@ -348,31 +349,20 @@ export default function ClearanceDocumentsPage() {
|
|||||||
style={{ minWidth: 140 }}
|
style={{ minWidth: 140 }}
|
||||||
aria-label="Filter by ownership"
|
aria-label="Filter by ownership"
|
||||||
/>
|
/>
|
||||||
<DateInput
|
<DatePickerInput
|
||||||
placeholder="Created from"
|
type="range"
|
||||||
value={createdFrom}
|
placeholder="Created date range"
|
||||||
onChange={(v) => {
|
value={[createdFrom, createdTo]}
|
||||||
setCreatedFrom(v ? new Date(v) : null);
|
onChange={([from, to]) => {
|
||||||
|
setCreatedFrom(from ? new Date(from) : null);
|
||||||
|
setCreatedTo(to ? new Date(to) : null);
|
||||||
resetPage();
|
resetPage();
|
||||||
}}
|
}}
|
||||||
maxDate={createdTo ?? undefined}
|
presets={getDateRangePresets()}
|
||||||
clearable
|
clearable
|
||||||
radius="lg"
|
radius="lg"
|
||||||
style={{ minWidth: 140 }}
|
style={{ minWidth: 220 }}
|
||||||
aria-label="Created from"
|
aria-label="Created date range"
|
||||||
/>
|
|
||||||
<DateInput
|
|
||||||
placeholder="Created to"
|
|
||||||
value={createdTo}
|
|
||||||
onChange={(v) => {
|
|
||||||
setCreatedTo(v ? new Date(v) : null);
|
|
||||||
resetPage();
|
|
||||||
}}
|
|
||||||
minDate={createdFrom ?? undefined}
|
|
||||||
clearable
|
|
||||||
radius="lg"
|
|
||||||
style={{ minWidth: 140 }}
|
|
||||||
aria-label="Created to"
|
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import {
|
|||||||
TextInput,
|
TextInput,
|
||||||
ThemeIcon,
|
ThemeIcon,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { DateInput } from "@mantine/dates";
|
import { DatePickerInput } from "@mantine/dates";
|
||||||
|
import { getDateRangePresets } from "@/components/common/dateRangePresets";
|
||||||
import { useDebouncedValue } from "@mantine/hooks";
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
import {
|
import {
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
@@ -613,31 +614,20 @@ export default function ContractRequestsPage() {
|
|||||||
style={{ minWidth: 140 }}
|
style={{ minWidth: 140 }}
|
||||||
aria-label="Filter by payment currency"
|
aria-label="Filter by payment currency"
|
||||||
/>
|
/>
|
||||||
<DateInput
|
<DatePickerInput
|
||||||
placeholder="Created from"
|
type="range"
|
||||||
value={createdFrom}
|
placeholder="Created date range"
|
||||||
onChange={(v) => {
|
value={[createdFrom, createdTo]}
|
||||||
setCreatedFrom(v ? new Date(v) : null);
|
onChange={([from, to]) => {
|
||||||
|
setCreatedFrom(from ? new Date(from) : null);
|
||||||
|
setCreatedTo(to ? new Date(to) : null);
|
||||||
resetPage();
|
resetPage();
|
||||||
}}
|
}}
|
||||||
maxDate={createdTo ?? undefined}
|
presets={getDateRangePresets()}
|
||||||
clearable
|
clearable
|
||||||
radius="lg"
|
radius="lg"
|
||||||
style={{ minWidth: 140 }}
|
style={{ minWidth: 220 }}
|
||||||
aria-label="Created from"
|
aria-label="Created date range"
|
||||||
/>
|
|
||||||
<DateInput
|
|
||||||
placeholder="Created to"
|
|
||||||
value={createdTo}
|
|
||||||
onChange={(v) => {
|
|
||||||
setCreatedTo(v ? new Date(v) : null);
|
|
||||||
resetPage();
|
|
||||||
}}
|
|
||||||
minDate={createdFrom ?? undefined}
|
|
||||||
clearable
|
|
||||||
radius="lg"
|
|
||||||
style={{ minWidth: 140 }}
|
|
||||||
aria-label="Created to"
|
|
||||||
/>
|
/>
|
||||||
{activeFilterCount > 0 ? (
|
{activeFilterCount > 0 ? (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ import {
|
|||||||
Textarea,
|
Textarea,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { DateInput } from "@mantine/dates";
|
import { DatePickerInput } from "@mantine/dates";
|
||||||
|
import { getDateRangePresets } from "@/components/common/dateRangePresets";
|
||||||
import {
|
import {
|
||||||
ArrowUpDown,
|
ArrowUpDown,
|
||||||
FilterX,
|
FilterX,
|
||||||
@@ -460,25 +461,19 @@ export default function ShipmentRequestsPage() {
|
|||||||
allowDeselect={false}
|
allowDeselect={false}
|
||||||
aria-label="Cargo type"
|
aria-label="Cargo type"
|
||||||
/>
|
/>
|
||||||
<DateInput
|
<DatePickerInput
|
||||||
|
type="range"
|
||||||
radius="md"
|
radius="md"
|
||||||
w={150}
|
w={230}
|
||||||
placeholder="Preferred from"
|
placeholder="Preferred date range"
|
||||||
value={preferredFrom}
|
value={[preferredFrom, preferredTo]}
|
||||||
onChange={(v) => setPreferredFrom(v ? new Date(v) : null)}
|
onChange={([from, to]) => {
|
||||||
maxDate={preferredTo ?? undefined}
|
setPreferredFrom(from ? new Date(from) : null);
|
||||||
|
setPreferredTo(to ? new Date(to) : null);
|
||||||
|
}}
|
||||||
|
presets={getDateRangePresets()}
|
||||||
clearable
|
clearable
|
||||||
aria-label="Preferred date from"
|
aria-label="Preferred date range"
|
||||||
/>
|
|
||||||
<DateInput
|
|
||||||
radius="md"
|
|
||||||
w={150}
|
|
||||||
placeholder="Preferred to"
|
|
||||||
value={preferredTo}
|
|
||||||
onChange={(v) => setPreferredTo(v ? new Date(v) : null)}
|
|
||||||
minDate={preferredFrom ?? undefined}
|
|
||||||
clearable
|
|
||||||
aria-label="Preferred date to"
|
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
radius="md"
|
radius="md"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { ColumnDef } from "@edr/ui-common";
|
import type { ColumnDef } from "@edr/ui-common";
|
||||||
import { Box, Button, Card, Container, Group, Modal, Select, Stack, Text, TextInput, Title } from "@mantine/core";
|
import { Box, Button, Card, Container, Group, Modal, Select, Stack, Text, TextInput, Title } from "@mantine/core";
|
||||||
import { DatePickerInput } from "@mantine/dates";
|
import { DatePickerInput } from "@mantine/dates";
|
||||||
|
import { getDateRangePresets } from "@/components/common/dateRangePresets";
|
||||||
import { keepPreviousData, useMutation, useQuery } from "@tanstack/react-query";
|
import { keepPreviousData, useMutation, useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { api } from "@/services/api";
|
import { api } from "@/services/api";
|
||||||
@@ -613,26 +614,19 @@ const FleetResourcePage = () => {
|
|||||||
filters={
|
filters={
|
||||||
<Group gap="sm" wrap="wrap" align="center">
|
<Group gap="sm" wrap="wrap" align="center">
|
||||||
<DatePickerInput
|
<DatePickerInput
|
||||||
aria-label="Created from"
|
type="range"
|
||||||
placeholder="Created from"
|
aria-label="Created date range"
|
||||||
value={dateFrom}
|
placeholder="Created date range"
|
||||||
onChange={setDateFrom}
|
value={[dateFrom, dateTo]}
|
||||||
maxDate={dateTo ?? undefined}
|
onChange={([from, to]) => {
|
||||||
|
setDateFrom(from);
|
||||||
|
setDateTo(to);
|
||||||
|
}}
|
||||||
|
presets={getDateRangePresets()}
|
||||||
clearable
|
clearable
|
||||||
size="sm"
|
size="sm"
|
||||||
radius="lg"
|
radius="lg"
|
||||||
w={160}
|
w={240}
|
||||||
/>
|
|
||||||
<DatePickerInput
|
|
||||||
aria-label="Created to"
|
|
||||||
placeholder="Created to"
|
|
||||||
value={dateTo}
|
|
||||||
onChange={setDateTo}
|
|
||||||
minDate={dateFrom ?? undefined}
|
|
||||||
clearable
|
|
||||||
size="sm"
|
|
||||||
radius="lg"
|
|
||||||
w={160}
|
|
||||||
/>
|
/>
|
||||||
{listFilterSelects ? (
|
{listFilterSelects ? (
|
||||||
<Group gap="sm" wrap="wrap" align="center">
|
<Group gap="sm" wrap="wrap" align="center">
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ import {
|
|||||||
ThemeIcon,
|
ThemeIcon,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { DateInput } from "@mantine/dates";
|
import { DatePickerInput } from "@mantine/dates";
|
||||||
|
import { getDateRangePresets } from "@/components/common/dateRangePresets";
|
||||||
import { useDebouncedValue } from "@mantine/hooks";
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
import {
|
import {
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
@@ -792,24 +793,19 @@ export default function BatchBoardPage() {
|
|||||||
w={140}
|
w={140}
|
||||||
styles={{ input: { borderColor: "var(--mantine-color-gray-3)" } }}
|
styles={{ input: { borderColor: "var(--mantine-color-gray-3)" } }}
|
||||||
/>
|
/>
|
||||||
<DateInput
|
<DatePickerInput
|
||||||
|
type="range"
|
||||||
size="sm"
|
size="sm"
|
||||||
radius="lg"
|
radius="lg"
|
||||||
placeholder="Departs from"
|
placeholder="Departure date range"
|
||||||
value={departureFrom}
|
value={[departureFrom, departureTo]}
|
||||||
onChange={(v) => setDepartureFrom(v ? new Date(v) : null)}
|
onChange={([from, to]) => {
|
||||||
|
setDepartureFrom(from ? new Date(from) : null);
|
||||||
|
setDepartureTo(to ? new Date(to) : null);
|
||||||
|
}}
|
||||||
|
presets={getDateRangePresets()}
|
||||||
clearable
|
clearable
|
||||||
w={140}
|
w={230}
|
||||||
styles={{ input: { borderColor: "var(--mantine-color-gray-3)" } }}
|
|
||||||
/>
|
|
||||||
<DateInput
|
|
||||||
size="sm"
|
|
||||||
radius="lg"
|
|
||||||
placeholder="Departs to"
|
|
||||||
value={departureTo}
|
|
||||||
onChange={(v) => setDepartureTo(v ? new Date(v) : null)}
|
|
||||||
clearable
|
|
||||||
w={140}
|
|
||||||
styles={{ input: { borderColor: "var(--mantine-color-gray-3)" } }}
|
styles={{ input: { borderColor: "var(--mantine-color-gray-3)" } }}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
Reference in New Issue
Block a user