mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 16:35:42 +00:00
feat: Stripe-style filter bar for freight backoffice (pilot: contracts)
Replace the ad-hoc filter controls with a URL-linkable pill filter bar: each filter is a pill that opens a type-aware popover (text/enum/date/ number/boolean, each with the right operator set), overflow filters live behind a searchable "More filters" menu, sorting is a separate control, and filter state round-trips through the URL query string (shareable, back/forward-safe, backward compatible with existing ?statuses=A,B links). Frontend (apps/edr-freight-web/backoffice/src/components/filters/): - FilterDef schema + a pure url.ts codec (parse/serialize/toApiParams), with a 24-case round-trip + malformed-input test suite - useFilters hook driving react-query params straight from useSearchParams, debounced search, saved views in localStorage (@mantine/hooks useLocalStorage), page-reset-on-filter-change baked into one setSearchParams call instead of a separate effect - FilterBar/FilterPill/OperatorSelect/MoreFiltersMenu/SortControl + per-type popover bodies (Mantine) - ContractRequestsPage migrated end to end as the pilot Backend (apps/edr-freight-api): - pagination.util: applySort() — whitelisted sortBy resolved against a per-module column map (never interpolated), with a mandatory `id ASC` tiebreaker so paginating a non-unique sort can't drop/duplicate rows - facets.util: computeFacets() — one GROUP BY per enum column, each omitting its own predicate, so picking a value doesn't hide its siblings - contracts/bookings: list-summary now returns real filter-scoped facet counts (contracts' getStatusCounts was unfiltered/global; superseded) - deleted drivers/vehicles findAllWithFilters — dead code that interpolated an unwhitelisted sortBy straight into orderBy() - migration: missing bookings(status)/wagons(status) indexes + (created_at DESC, id ASC) partials on the hot list tables UI polish pass: inactive pill uses the opaque "default" variant instead of a faint tinted outline, active pill uses "light" not "filled", larger X hit target, applied filters sort first, sort control separated behind a divider on the right and wraps independently from the filter row, popover option rows are fully clickable (count moved inside the native label) with bigger hit area and font, fixed a real date-filter bug where the calendar's own portal falsely registered as an "outside click" and closed the popover, and fixed a timezone bug where bare YYYY-MM-DD strings were parsed as UTC instead of local time (shifts a day for EAT). Not in this commit: rollout to the other ~59 list pages, the Ethiopian- calendar DateBody branch, and the Family-B (client-side) bridge mode — tracked in the filter-bar plan.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { FacetBucket } from '../../../common/utils/facets.util';
|
||||
|
||||
export class ContractListSummaryMetricsDto {
|
||||
@ApiProperty({ example: 42 })
|
||||
@@ -15,6 +16,23 @@ export class ContractListSummaryDto {
|
||||
@ApiProperty({ type: ContractListSummaryMetricsDto })
|
||||
metrics!: ContractListSummaryMetricsDto;
|
||||
|
||||
/** @deprecated use `facets.status` — kept for existing callers, computed
|
||||
* from the same filter-scoped query now instead of `getStatusCounts`'s
|
||||
* unfiltered global count. */
|
||||
@ApiProperty({ description: 'Count per contract status', type: 'object', additionalProperties: { type: 'number' } })
|
||||
statusCounts!: Record<string, number>;
|
||||
|
||||
/**
|
||||
* Per-column value counts for the filter bar's enum popovers, scoped to
|
||||
* every OTHER currently-active filter (each column's own predicate is
|
||||
* omitted from its own count — see `ContractsRepository.getFacets`).
|
||||
* Absent/omitted keys mean the frontend falls back to its static option
|
||||
* list with no counts, never an error.
|
||||
*/
|
||||
@ApiProperty({
|
||||
description: 'Facet counts keyed by filter field, for the pill filter bar',
|
||||
type: 'object',
|
||||
additionalProperties: { type: 'array', items: { type: 'object' } },
|
||||
})
|
||||
facets!: Record<string, FacetBucket[]>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user