Merge pull request #966 from Tria-plc/freight_feature/usermanagement

add dispute functionality for contract duty and implement collection…
This commit is contained in:
marshal
2026-07-26 21:24:17 +03:00
committed by GitHub
2 changed files with 4 additions and 106 deletions

View File

@@ -20,17 +20,15 @@ import { useDebouncedValue } from "@mantine/hooks";
import { isAxiosError } from "axios"; import { isAxiosError } from "axios";
import { import {
ArrowRight, ArrowRight,
// Ban, — used only by the commented-out "Cancel schedule" row action Ban,
CalendarClock, CalendarClock,
Clock, Clock,
Eye, Eye,
Lock,
MoreHorizontal, MoreHorizontal,
Navigation, Navigation,
Pencil, Pencil,
Play, Play,
Send, Send,
Unlock,
Train, Train,
Weight, Weight,
} from "lucide-react"; } from "lucide-react";
@@ -125,12 +123,10 @@ export default function TrainScheduleV2ListPage() {
const [sortDir, setSortDir] = useState<"desc" | "asc">("desc"); const [sortDir, setSortDir] = useState<"desc" | "asc">("desc");
const [createOpen, setCreateOpen] = useState(false); const [createOpen, setCreateOpen] = useState(false);
const [windowSettingsId, setWindowSettingsId] = useState<string | null>(null); const [windowSettingsId, setWindowSettingsId] = useState<string | null>(null);
// Dispatching a train and closing its booking window are both irreversible // Dispatch is irreversible from this screen, so it goes through an explicit
// from this screen, so each goes through an explicit confirmation. // confirmation.
const [dispatchTarget, setDispatchTarget] = const [dispatchTarget, setDispatchTarget] =
useState<TrainScheduleListItem | null>(null); useState<TrainScheduleListItem | null>(null);
const [windowTarget, setWindowTarget] =
useState<TrainScheduleListItem | null>(null);
const [editDateSchedule, setEditDateSchedule] = const [editDateSchedule, setEditDateSchedule] =
useState<TrainScheduleListItem | null>(null); useState<TrainScheduleListItem | null>(null);
const [routeId, setRouteId] = useState(""); const [routeId, setRouteId] = useState("");
@@ -213,10 +209,7 @@ export default function TrainScheduleV2ListPage() {
const dispatchSchedule = useMutation( const dispatchSchedule = useMutation(
api.trainScheduling.dispatchSchedule.mutationOptions(), api.trainScheduling.dispatchSchedule.mutationOptions(),
); );
const setWindow = useMutation( const cancel = useMutation(api.trainScheduling.cancelSchedule.mutationOptions());
api.trainScheduling.setBookingWindow.mutationOptions(),
);
// const cancel = useMutation(api.trainScheduling.cancelSchedule.mutationOptions());
// Intercity (same-country / DOMESTIC) routes cannot be scheduled yet — the // Intercity (same-country / DOMESTIC) routes cannot be scheduled yet — the
// API rejects them, so keep them out of the picker entirely. // API rejects them, so keep them out of the picker entirely.
@@ -484,27 +477,6 @@ export default function TrainScheduleV2ListPage() {
Booking window settings Booking window settings
</Menu.Item> </Menu.Item>
) : null} ) : null}
{/* Stop / resume taking bookings on this departure without
opening it. FULL is a capacity state the engine owns, so
only an explicitly OPEN/CLOSED window is toggled here. */}
{canUpdate &&
["DRAFT", "SCHEDULED"].includes(schedule.status) &&
schedule.bookingWindowStatus !== "FULL" ? (
<Menu.Item
leftSection={
schedule.bookingWindowStatus === "CLOSED" ? (
<Unlock size={15} />
) : (
<Lock size={15} />
)
}
onClick={() => setWindowTarget(schedule)}
>
{schedule.bookingWindowStatus === "CLOSED"
? "Open booking window"
: "Close booking window"}
</Menu.Item>
) : null}
{/* Start the run. Same transition as the detail page's {/* Start the run. Same transition as the detail page's
Dispatch button — that page also shows unassigned-wagon Dispatch button — that page also shows unassigned-wagon
and not-loaded warnings, so it stays the fuller surface. */} and not-loaded warnings, so it stays the fuller surface. */}
@@ -516,9 +488,6 @@ export default function TrainScheduleV2ListPage() {
Start (dispatch) train Start (dispatch) train
</Menu.Item> </Menu.Item>
) : null} ) : null}
{/* Cancel schedule — hidden for now (frontend only; the
cancelSchedule mutation is untouched). Restore by
uncommenting.
{["DRAFT", "SCHEDULED"].includes(schedule.status) ? ( {["DRAFT", "SCHEDULED"].includes(schedule.status) ? (
<Menu.Item <Menu.Item
color="red" color="red"
@@ -542,7 +511,6 @@ export default function TrainScheduleV2ListPage() {
Cancel schedule Cancel schedule
</Menu.Item> </Menu.Item>
) : null} ) : null}
*/}
</Menu.Dropdown> </Menu.Dropdown>
</Menu> </Menu>
</Group> </Group>
@@ -902,74 +870,6 @@ export default function TrainScheduleV2ListPage() {
onSaved={() => void schedulesQuery.refetch()} onSaved={() => void schedulesQuery.refetch()}
/> />
<Modal
opened={windowTarget != null}
onClose={() => setWindowTarget(null)}
title={
windowTarget?.bookingWindowStatus === "CLOSED"
? "Open booking window?"
: "Close booking window?"
}
centered
radius="md"
>
<Stack gap="md">
<Text size="sm" c="dimmed">
{windowTarget?.bookingWindowStatus === "CLOSED" ? (
<>
Customers will be able to book onto{" "}
<Text span fw={600} c="dark">
{windowTarget?.trainNumber ?? windowTarget?.reference ?? "this departure"}
</Text>{" "}
again, up to its remaining capacity.
</>
) : (
<>
No further bookings will be accepted onto{" "}
<Text span fw={600} c="dark">
{windowTarget?.trainNumber ?? windowTarget?.reference ?? "this departure"}
</Text>
. Bookings already aboard are unaffected, and you can reopen the
window from this menu.
</>
)}
</Text>
<Group justify="flex-end" gap="sm">
<Button variant="default" onClick={() => setWindowTarget(null)}>
Cancel
</Button>
<Button
color={windowTarget?.bookingWindowStatus === "CLOSED" ? "edr-green" : "orange"}
loading={setWindow.isPending}
onClick={async () => {
if (!windowTarget) return;
const next =
windowTarget.bookingWindowStatus === "CLOSED" ? "OPEN" : "CLOSED";
try {
await setWindow.mutateAsync({ id: windowTarget.id, status: next });
toast({
title:
next === "OPEN" ? "Booking window opened" : "Booking window closed",
});
setWindowTarget(null);
void schedulesQuery.refetch();
} catch (err) {
toast({
title: "Could not update the booking window",
description: parseError(err, "Update failed"),
variant: "destructive",
});
}
}}
>
{windowTarget?.bookingWindowStatus === "CLOSED"
? "Open window"
: "Close window"}
</Button>
</Group>
</Stack>
</Modal>
<Modal <Modal
opened={dispatchTarget != null} opened={dispatchTarget != null}
onClose={() => setDispatchTarget(null)} onClose={() => setDispatchTarget(null)}

View File

@@ -203,8 +203,6 @@ export interface TrainScheduleListItem {
totalLengthMeters: number; totalLengthMeters: number;
bookingsCount: number; bookingsCount: number;
status: TrainScheduleStatus | string; status: TrainScheduleStatus | string;
/** Whether the departure is still taking bookings (orthogonal to `status`). */
bookingWindowStatus?: "OPEN" | "FULL" | "CLOSED" | string;
} }
export type TrainScheduleSortField = export type TrainScheduleSortField =