mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 14:15:44 +00:00
train
This commit is contained in:
@@ -7,7 +7,6 @@ import {
|
||||
Group,
|
||||
Menu,
|
||||
Modal,
|
||||
MultiSelect,
|
||||
Select,
|
||||
SimpleGrid,
|
||||
Stack,
|
||||
@@ -41,10 +40,7 @@ import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles";
|
||||
import { FreightTypeBadge } from "@/components/trainScheduling/ScheduleStatusBadge";
|
||||
import BookingWindowSettingsModal from "@/components/trainScheduling/BookingWindowSettingsModal";
|
||||
import EditScheduleDateModal from "@/components/trainScheduling/EditScheduleDateModal";
|
||||
import {
|
||||
locomotiveOption,
|
||||
showScheduleWarnings,
|
||||
} from "@/components/trainScheduling/locomotiveOptions";
|
||||
import { showScheduleWarnings } from "@/components/trainScheduling/locomotiveOptions";
|
||||
import {
|
||||
RouteCorridor,
|
||||
StatusPill,
|
||||
@@ -119,7 +115,7 @@ export default function TrainScheduleV2ListPage() {
|
||||
useState<TrainScheduleListItem | null>(null);
|
||||
const [routeId, setRouteId] = useState("");
|
||||
const [scheduleDate, setScheduleDate] = useState("");
|
||||
const [locomotiveIds, setLocomotiveIds] = useState<string[]>([]);
|
||||
const [trainId, setTrainId] = useState("");
|
||||
// Recomputed each time the create modal opens so a long-lived tab can't keep
|
||||
// offering a stale "now" as the earliest selectable departure.
|
||||
const minScheduleDate = useMemo(
|
||||
@@ -186,9 +182,10 @@ export default function TrainScheduleV2ListPage() {
|
||||
const routesQuery = useQuery(
|
||||
api.routes.list.queryOptions({ input: { status: "AVAILABLE" } }),
|
||||
);
|
||||
const locomotivesQuery = useQuery(
|
||||
api.trainScheduling.availableLocomotives.queryOptions({
|
||||
input: { routeId: routeId || undefined },
|
||||
const trainsQuery = useQuery(
|
||||
api.trainScheduling.availableTrains.queryOptions({
|
||||
input: { routeId },
|
||||
enabled: Boolean(routeId),
|
||||
}),
|
||||
);
|
||||
const create = useMutation(api.trainScheduling.createSchedule.mutationOptions());
|
||||
@@ -203,17 +200,17 @@ export default function TrainScheduleV2ListPage() {
|
||||
|
||||
const selectedRoute = activeRoutes.find((r) => r.id === routeId);
|
||||
|
||||
const locomotiveYardHint = useMemo(() => {
|
||||
const trainYardHint = useMemo(() => {
|
||||
if (!selectedRoute) return "Select a route first";
|
||||
const originLabel =
|
||||
selectedRoute.originYard?.label ??
|
||||
selectedRoute.originYard?.code ??
|
||||
"the route origin yard";
|
||||
return `All in-service locomotives are shown — those not yet at ${originLabel} or already on future schedules are flagged`;
|
||||
return `All schedulable built trains are shown — those not yet at ${originLabel} or already on future schedules are flagged`;
|
||||
}, [selectedRoute]);
|
||||
|
||||
useEffect(() => {
|
||||
setLocomotiveIds([]);
|
||||
setTrainId("");
|
||||
}, [routeId]);
|
||||
|
||||
// Filtering, sorting, and paging all happen server-side — `schedules` IS the
|
||||
@@ -326,10 +323,29 @@ export default function TrainScheduleV2ListPage() {
|
||||
cell: ({ row }) => <FreightTypeBadge freightType={row.original.freightType} />,
|
||||
},
|
||||
{
|
||||
id: "loco",
|
||||
header: "Locomotives",
|
||||
id: "train",
|
||||
header: "Train",
|
||||
meta: { headerClassName, cellClassName },
|
||||
cell: ({ row }) => {
|
||||
// Schedules created from the Train Builder carry the train code;
|
||||
// legacy rows fall back to their locomotive set.
|
||||
if (row.original.train) {
|
||||
return (
|
||||
<Group gap={6} wrap="nowrap">
|
||||
<Train size={14} color="var(--mantine-color-gray-5)" />
|
||||
<Stack gap={0}>
|
||||
<Text size="sm" fw={600} ff="monospace" lh={1.2}>
|
||||
{row.original.train.code}
|
||||
</Text>
|
||||
{row.original.train.trainName ? (
|
||||
<Text size="xs" c="dimmed" lh={1.2}>
|
||||
{row.original.train.trainName}
|
||||
</Text>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
const locos =
|
||||
row.original.locomotives && row.original.locomotives.length > 0
|
||||
? row.original.locomotives
|
||||
@@ -456,9 +472,9 @@ export default function TrainScheduleV2ListPage() {
|
||||
}, [navigate, cancel.isPending, cancel, toast]);
|
||||
|
||||
const handleCreate = async () => {
|
||||
if (!routeId || !scheduleDate || locomotiveIds.length < 2) {
|
||||
if (!routeId || !scheduleDate || !trainId) {
|
||||
toast({
|
||||
title: "Select route, date, and at least two locomotives",
|
||||
title: "Select route, date, and the train to run",
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
@@ -475,7 +491,7 @@ export default function TrainScheduleV2ListPage() {
|
||||
payload: {
|
||||
routeId,
|
||||
scheduleDate: new Date(scheduleDate).toISOString(),
|
||||
locomotiveIds,
|
||||
trainId,
|
||||
},
|
||||
});
|
||||
toast({ title: "Train schedule created" });
|
||||
@@ -728,7 +744,7 @@ export default function TrainScheduleV2ListPage() {
|
||||
/>
|
||||
{routeId ? (
|
||||
<Text size="xs" c="dimmed">
|
||||
{locomotiveYardHint}
|
||||
{trainYardHint}
|
||||
</Text>
|
||||
) : null}
|
||||
<TextInput
|
||||
@@ -738,24 +754,26 @@ export default function TrainScheduleV2ListPage() {
|
||||
value={scheduleDate}
|
||||
onChange={(e) => setScheduleDate(e.currentTarget.value)}
|
||||
/>
|
||||
<MultiSelect
|
||||
label="Locomotives"
|
||||
description="A train must be pulled by at least two locomotives (front and back)"
|
||||
placeholder={
|
||||
routeId ? "Select at least two locomotives" : "Select a route first"
|
||||
}
|
||||
data={(locomotivesQuery.data ?? []).map((l) => locomotiveOption(l))}
|
||||
value={locomotiveIds}
|
||||
onChange={setLocomotiveIds}
|
||||
<Select
|
||||
label="Train"
|
||||
description="A built train (Train Builder) runs this departure with its locomotives and wagons"
|
||||
placeholder={routeId ? "Select a train" : "Select a route first"}
|
||||
data={(trainsQuery.data ?? []).map((train) => ({
|
||||
value: train.id,
|
||||
label: `${train.code}${train.trainName ? ` — ${train.trainName}` : ""} · ${
|
||||
train.locomotives.length
|
||||
} locos · ${train.wagonCount} wagons${train.atOriginYard ? "" : " · not at origin yard"}${
|
||||
train.futureScheduleCount ? ` · ${train.futureScheduleCount} future run(s)` : ""
|
||||
}`,
|
||||
}))}
|
||||
value={trainId || null}
|
||||
onChange={(v) => setTrainId(v ?? "")}
|
||||
searchable
|
||||
disabled={!routeId}
|
||||
error={
|
||||
locomotiveIds.length > 0 && locomotiveIds.length < 2
|
||||
? "Select at least two locomotives"
|
||||
: undefined
|
||||
}
|
||||
nothingFoundMessage={
|
||||
routeId ? "No available locomotives for this corridor" : "Select a route first"
|
||||
routeId
|
||||
? "No built trains yet — assemble one in the Train Builder first"
|
||||
: "Select a route first"
|
||||
}
|
||||
/>
|
||||
<Group justify="flex-end">
|
||||
|
||||
Reference in New Issue
Block a user