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

feat: enhance train scheduling and contract management features
This commit is contained in:
marshal
2026-08-26 00:46:17 +03:00
committed by GitHub
67 changed files with 2998 additions and 255 deletions

View File

@@ -17,6 +17,7 @@ import {
Text,
ThemeIcon,
Title,
Tooltip,
} from "@mantine/core";
import { isAxiosError } from "axios";
import {
@@ -65,6 +66,7 @@ import { LegLoadBoardPanel } from "@/components/trainScheduling/LegLoadBoardPane
import { LoadEmptyContainersModal } from "@/components/trainScheduling/LoadEmptyContainersModal";
import MergeScheduleTrainModal from "@/components/trainScheduling/MergeScheduleTrainModal";
import ScheduleHistoryPanel from "@/components/trainScheduling/ScheduleHistoryPanel";
import { StationWorkControls } from "@/components/trainScheduling/StationWorkControls";
// import { ImportLoadingConfirmationPanel } from "@/components/trainScheduling/ImportLoadingConfirmationPanel";
import { RescheduleTrainDialog } from "@/components/trainScheduling/RescheduleTrainDialog";
import BookingWindowSettingsModal from "@/components/trainScheduling/BookingWindowSettingsModal";
@@ -490,6 +492,17 @@ export default function TrainScheduleV2DetailPage() {
const dispatchLeftCount = pendingOriginBoarders.filter(
(b) => !b.isGovernment && !dispatchLoadedIds.has(b.id),
).length;
// Origin loading time window: dispatch (which marks the ticked boarders
// loaded) is server-rejected until "Start loading" was clicked for the
// origin yard, so the button mirrors that gate.
const originLoadingLog = originYardId
? schedule.stationWorkLogs?.[originYardId]?.loading
: undefined;
const originLoadingStarted = Boolean(originLoadingLog?.startedAt);
const dispatchBoardersKept = pendingOriginBoarders.some(
(b) => b.isGovernment || dispatchLoadedIds.has(b.id),
);
const dispatchNeedsLoadingStart = dispatchBoardersKept && !originLoadingStarted;
const finalizeStep = hasContainerStep ? 3 : 2;
const canModifyBookings = canEditBookings && !["DISPATCHED", "ARRIVED"].includes(schedule.status);
@@ -936,6 +949,27 @@ export default function TrainScheduleV2DetailPage() {
</Stack>
</Group>
</Paper>
{originYardId ? (
<Paper p="md" radius="lg" withBorder>
<Stack gap={6}>
<Text fw={600} size="sm">
Loading at {schedule.originStation?.label ?? "the origin yard"}
</Text>
<StationWorkControls
scheduleId={scheduleId}
yardId={originYardId}
phase="loading"
log={originLoadingLog}
/>
{dispatchNeedsLoadingStart ? (
<Text size="xs" c="dimmed">
Start loading before dispatching the ticked bookings are marked
loaded at dispatch, which needs an open loading window.
</Text>
) : null}
</Stack>
</Paper>
) : null}
<Group>
{canDispatch ? (
<Button
@@ -1560,6 +1594,14 @@ export default function TrainScheduleV2DetailPage() {
Cargo boarding at {schedule.originStation?.label ?? "the origin yard"}
tick what was loaded
</Text>
{originYardId ? (
<StationWorkControls
scheduleId={scheduleId}
yardId={originYardId}
phase="loading"
log={originLoadingLog}
/>
) : null}
<Text size="xs" c="dimmed">
Unticked bookings are left behind: removed from this train, their
wagons freed, and the booking returned to the pool for a later
@@ -1656,14 +1698,20 @@ export default function TrainScheduleV2DetailPage() {
>
Cancel
</Button>
<Button
color="edr-green"
leftSection={<Send size={16} />}
loading={dispatch.isPending}
onClick={() => void runDispatch()}
<Tooltip
label="Start loading at the origin station first — dispatch marks the ticked bookings loaded"
disabled={!dispatchNeedsLoadingStart}
>
{hasDispatchWarnings ? "Dispatch anyway" : "Dispatch train"}
</Button>
<Button
color="edr-green"
leftSection={<Send size={16} />}
loading={dispatch.isPending}
disabled={dispatchNeedsLoadingStart}
onClick={() => void runDispatch()}
>
{hasDispatchWarnings ? "Dispatch anyway" : "Dispatch train"}
</Button>
</Tooltip>
</Group>
</Stack>
</Modal>