refactor(train-scheduling): move the schedule list onto the shared FilterBar

The list was the last major board still on ad-hoc filtering: usePagination
plus FleetToolbar's hand-rolled Selects, a debounced search and a manual
sort Select, assembled into a filters object by hand. It now uses
useFilters/FilterBar like every other list, so pagination, search, sort
and filters all travel as URL params and a link reproduces the view.

Origin and destination ride the shared Route filter, which no longer
requires both ends — filtering by origin alone stays possible, and each
end now takes several stations.

The card/table view toggle moves into FilterBar's children slot, and the
row tinting for shipping-line and direction is untouched.
This commit is contained in:
Nathnael
2026-08-20 11:37:02 +00:00
parent d6f1c5fb43
commit 1b08666527
2 changed files with 264 additions and 363 deletions

View File

@@ -201,16 +201,20 @@ export function StatTile({
/**
* Origin → destination corridor visual: two anchored stops joined by a rail
* line. `variant="compact"` is for dense table rows; `default` for cards.
* `orientation="vertical"` stacks the stops as waypoints, which keeps a long
* yard name off one wide line in a table cell.
*/
export function RouteCorridor({
origin,
destination,
variant = "default",
orientation = "horizontal",
onDark = false,
}: {
origin?: string | null;
destination?: string | null;
variant?: "default" | "compact";
orientation?: "horizontal" | "vertical";
onDark?: boolean;
}) {
const compact = variant === "compact";
@@ -218,6 +222,48 @@ export function RouteCorridor({
const strong = onDark ? "white" : "var(--mantine-color-gray-8)";
const lineColor = onDark ? "rgba(255,255,255,0.4)" : "var(--mantine-color-gray-3)";
const accent = onDark ? "white" : freightBrand.primary;
const dot = compact ? 7 : 9;
if (orientation === "vertical") {
return (
<Stack gap={2} style={{ minWidth: 0 }}>
<Group gap={8} wrap="nowrap" style={{ minWidth: 0 }}>
<Box
w={dot}
h={dot}
style={{
borderRadius: 999,
flexShrink: 0,
border: `2px solid ${accent}`,
background: onDark ? "transparent" : "white",
}}
/>
<Text size="sm" fw={600} c={strong} lh={1.2} truncate>
{origin ?? "—"}
</Text>
</Group>
{/* Rail between the stops, aligned to the dot centres. */}
<Box
ml={dot / 2 - 1}
style={{
width: 0,
height: compact ? 10 : 14,
borderLeft: `2px dashed ${lineColor}`,
}}
/>
<Group gap={8} wrap="nowrap" style={{ minWidth: 0 }}>
<Box
w={dot}
h={dot}
style={{ borderRadius: 999, flexShrink: 0, background: accent }}
/>
<Text size="sm" fw={600} c={strong} lh={1.2} truncate>
{destination ?? "—"}
</Text>
</Group>
</Stack>
);
}
return (
<Group gap={compact ? 6 : 8} wrap="nowrap" align="center" style={{ minWidth: 0 }}>