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

Freight feature/usermanagement
This commit is contained in:
marshal
2026-08-04 01:11:09 +03:00
committed by GitHub
48 changed files with 3687 additions and 181 deletions

View File

@@ -6,12 +6,14 @@ import {
Button,
Card,
Checkbox,
Divider,
Group,
Menu,
Modal,
Select,
SimpleGrid,
Stack,
Switch,
Text,
TextInput,
ThemeIcon,
@@ -46,6 +48,10 @@ import {
directionRowStyle,
} from "@/components/trainBuilder/trainStatus";
import BookingWindowSettingsModal from "@/components/trainScheduling/BookingWindowSettingsModal";
import CreateScheduleWindowFields, {
buildWindowRulePayload,
type WindowFormState,
} from "@/components/trainScheduling/CreateScheduleWindowFields";
import EditScheduleDateModal from "@/components/trainScheduling/EditScheduleDateModal";
import { showScheduleWarnings } from "@/components/trainScheduling/locomotiveOptions";
import {
@@ -59,6 +65,7 @@ import { useToast } from "@/hooks/use-toast";
import { useAuth } from "@/auth/useAuth";
import { canCreateSchedule, canUpdateSchedule } from "@/lib/permissions";
import type {
CreateScheduleWindowRulePayload,
FreightType,
TrainScheduleListFilters,
TrainScheduleListItem,
@@ -133,6 +140,10 @@ export default function TrainScheduleV2ListPage() {
const [scheduleDate, setScheduleDate] = useState("");
const [trainId, setTrainId] = useState("");
const [reverseWagonOrder, setReverseWagonOrder] = useState(false);
// Booking window for the schedule being created: off = inherit the live global
// rules (the default), on = the values in `windowForm` are frozen onto it.
const [configureWindow, setConfigureWindow] = useState(false);
const [windowForm, setWindowForm] = useState<WindowFormState | null>(null);
// 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(
@@ -535,6 +546,25 @@ export default function TrainScheduleV2ListPage() {
});
return;
}
// Only build the window override when the toggle is on — off means "inherit
// the global rules", which the API expresses as an absent windowRule.
let windowRule: CreateScheduleWindowRulePayload | undefined;
if (configureWindow) {
if (!windowForm) {
toast({ title: "Booking window settings are still loading", variant: "destructive" });
return;
}
const built = buildWindowRulePayload(
windowForm,
selectedRoute?.direction === "EXPORT",
);
if ("error" in built) {
toast({ title: built.error, variant: "destructive" });
return;
}
windowRule = built.payload;
}
try {
const created = await create.mutateAsync({
payload: {
@@ -542,11 +572,14 @@ export default function TrainScheduleV2ListPage() {
scheduleDate: new Date(scheduleDate).toISOString(),
trainId,
reverseWagonOrder,
...(windowRule ? { windowRule } : {}),
},
});
toast({ title: "Train schedule created" });
showScheduleWarnings(created.warnings);
setReverseWagonOrder(false);
setConfigureWindow(false);
setWindowForm(null);
setCreateOpen(false);
navigate(`/dashboard/operations/train-scheduling-v2/${created.id}`);
} catch (err) {
@@ -843,6 +876,22 @@ export default function TrainScheduleV2ListPage() {
checked={reverseWagonOrder}
onChange={(e) => setReverseWagonOrder(e.currentTarget.checked)}
/>
<Divider />
<Switch
label="Configure booking window for this schedule"
description="Off, this train follows the global booking rules. On, the settings below are frozen onto it and a later global-rules change won't move them."
checked={configureWindow}
onChange={(e) => setConfigureWindow(e.currentTarget.checked)}
/>
{configureWindow ? (
<CreateScheduleWindowFields
isExport={selectedRoute?.direction === "EXPORT"}
form={windowForm}
onChange={setWindowForm}
/>
) : null}
<Group justify="flex-end">
<Button variant="default" onClick={() => setCreateOpen(false)}>
Cancel