feat(filters): route filter, select-styled trigger, tune default pin set

- New "route" FilterType: RouteBody popover (searchable origin +
  destination selects, apply once both are picked), wired into
  ContractRequestsPage and BookingRequestsPage. Bookings already had
  server-side originYardId/destinationYardId; contracts gets both new
  (contract_routes is one-to-many, so origin/destination are separate
  EXISTS subqueries, not a join).
- Inactive FilterPill trigger restyled to read like a closed Mantine
  Select (opaque solid border, trailing chevron) instead of a dashed
  "+" pill — trigger only, popover body/position unchanged.
- Search box text set to regular weight.
- A couple more filters (Direction, Freight) pinned by default per
  page on top of the existing always-pinned ones; the rest stay behind
  More filters.
This commit is contained in:
Nathnael
2026-08-15 07:23:43 +00:00
parent e5e75b4166
commit 839dfdbae3
10 changed files with 157 additions and 19 deletions

View File

@@ -136,9 +136,9 @@ export default function BookingRequestsPage() {
{ key: "statuses", label: "Status", type: "enum", options: STATUS_OPTIONS },
{
key: "tradeDirection", label: "Direction", type: "enum", multiple: false,
options: filterOptions(TRADE_DIRECTION_OPTIONS), secondary: true,
options: filterOptions(TRADE_DIRECTION_OPTIONS),
},
{ key: "freightType", label: "Freight", type: "enum", multiple: false, options: FREIGHT_TYPE_OPTIONS, secondary: true },
{ key: "freightType", label: "Freight", type: "enum", multiple: false, options: FREIGHT_TYPE_OPTIONS },
{ key: "paymentStatus", label: "Payment", type: "enum", multiple: false, options: PAYMENT_STATUS_OPTIONS, secondary: true },
{
// Wins over the `paymentStatus` filter above — the queue is by
@@ -149,8 +149,10 @@ export default function BookingRequestsPage() {
toParams: (v) => (v.v[0] === "true" ? { paymentStatus: "PAID", assignedToSchedule: "false" } : {}),
},
{ key: "isGovernment", label: "Ownership", type: "enum", multiple: false, options: OWNERSHIP_OPTIONS, secondary: true },
{ key: "originYardId", label: "Origin", type: "enum", multiple: false, options: yardOptions, secondary: true },
{ key: "destinationYardId", label: "Destination", type: "enum", multiple: false, options: yardOptions, secondary: true },
{
key: "route", label: "Route", type: "route", options: yardOptions, secondary: true,
toParams: ({ v }) => ({ originYardId: v[0], destinationYardId: v[1] }),
},
{ key: "created", label: "Created", type: "date", secondary: true, toParams: ({ v }) => ({ createdFrom: v[0], createdTo: v[1] }) },
{ key: "scheduled", label: "Scheduled", type: "date", secondary: true, toParams: ({ v }) => ({ scheduledFrom: v[0], scheduledTo: v[1] }) },
],

View File

@@ -23,6 +23,8 @@ import {
} from "lucide-react";
import { useCallback, useMemo, type ReactNode } from "react";
import { useNavigate } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import { api } from "@/services/api";
import { ContractStatusBadge } from "@/components/contracts/ContractStatusBadge";
import { bookingTable } from "@/components/bookings/booking-ui.styles";
@@ -103,6 +105,16 @@ export default function ContractRequestsPage() {
const navigate = useNavigate();
const { filterOptions } = useMyTradeAccess();
// Yard options for the route filter (shared routes reference list, same
// query BookingRequestsPage uses).
const { data: yardRefs } = useQuery(
api.routes.yards.queryOptions({ staleTime: 5 * 60_000 }),
);
const yardOptions = useMemo(
() => (yardRefs ?? []).map((y) => ({ value: y.id, label: y.label ?? y.code })),
[yardRefs],
);
// Static shape only (no facet counts) — this is what useFilters needs to
// parse the URL and build API params. Counts are attached separately below,
// for rendering only, once the summary query (which itself depends on
@@ -123,7 +135,6 @@ export default function ContractRequestsPage() {
type: "enum",
multiple: false,
options: filterOptions(TRADE_DIRECTION_OPTIONS),
secondary: true,
},
{
key: "freightType",
@@ -131,7 +142,6 @@ export default function ContractRequestsPage() {
type: "enum",
multiple: false,
options: FREIGHT_TYPE_OPTIONS,
secondary: true,
},
{
key: "paymentCurrency",
@@ -150,8 +160,16 @@ export default function ContractRequestsPage() {
// onChange, so this just routes to the API's existing param names.
toParams: ({ v }) => ({ createdFrom: v[0], createdTo: v[1] }),
},
{
key: "route",
label: "Route",
type: "route",
options: yardOptions,
secondary: true,
toParams: ({ v }) => ({ originYardId: v[0], destinationYardId: v[1] }),
},
],
[filterOptions],
[filterOptions, yardOptions],
);
const controls = useFilters(filterDefs, {