mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 22:18:12 +00:00
remove reopen delay minutes from global rules and update related types
- Removed the field from and related components. - Updated to reflect the removal of the reopen delay input field. - Modified to include new train number fields: and . - Added interface to manage active schedules with trade direction. - Introduced interface to track wagon shortages in bookings. - Updated logic to ensure consistent UI state representation. - Created migrations to drop the column and add and columns to the table. - Added tests for the new booking window display logic and wagon planning functionality.
This commit is contained in:
@@ -244,6 +244,21 @@ export default function TrainScheduleV2DetailPage() {
|
||||
return [];
|
||||
}, [previewResult?.wagonPlan, schedule?.trainSet?.wagons]);
|
||||
|
||||
// EXPORT schedules render the consist back-to-front (the train turns around
|
||||
// for the return run) — DISPLAY ONLY: stored sequenceNos, allocations,
|
||||
// documents, and the adjust-consist / placement flows keep the as-built order.
|
||||
const isExportDisplay = schedule?.direction === "EXPORT";
|
||||
const displayWagonPlanOriented = useMemo(
|
||||
() => (isExportDisplay ? [...displayWagonPlan].reverse() : displayWagonPlan),
|
||||
[displayWagonPlan, isExportDisplay],
|
||||
);
|
||||
const diagramWagons = useMemo(() => {
|
||||
const source = schedule?.trainSet?.wagons?.length
|
||||
? schedule.trainSet.wagons
|
||||
: displayWagonPlan;
|
||||
return isExportDisplay ? [...source].reverse() : source;
|
||||
}, [schedule?.trainSet?.wagons, displayWagonPlan, isExportDisplay]);
|
||||
|
||||
const runPreview = useCallback(
|
||||
async (options?: { silent?: boolean; advanceStep?: boolean }) => {
|
||||
if (!schedule || !scheduleId) return null;
|
||||
@@ -683,7 +698,12 @@ export default function TrainScheduleV2DetailPage() {
|
||||
fleetAvailability={previewResult?.fleetAvailability}
|
||||
deferredBookings={previewResult?.deferredBookings}
|
||||
/>
|
||||
<WagonPlanGrid wagonPlan={displayWagonPlan} freightType={freightType} />
|
||||
{isExportDisplay && displayWagonPlanOriented.length ? (
|
||||
<Text size="xs" c="dimmed">
|
||||
Shown rear-first (export direction) — positions keep their original numbers.
|
||||
</Text>
|
||||
) : null}
|
||||
<WagonPlanGrid wagonPlan={displayWagonPlanOriented} freightType={freightType} />
|
||||
{canEditBookings && (previewResult || displayWagonPlan.length) ? (
|
||||
<Group>
|
||||
{!hasContainerStep ? (
|
||||
@@ -760,15 +780,16 @@ export default function TrainScheduleV2DetailPage() {
|
||||
<TrainCompositionDiagram
|
||||
locomotive={schedule.trainSet?.locomotive}
|
||||
locomotives={locomotives}
|
||||
wagons={
|
||||
schedule.trainSet?.wagons?.length
|
||||
? schedule.trainSet.wagons
|
||||
: displayWagonPlan
|
||||
}
|
||||
wagons={diagramWagons}
|
||||
freightType={freightType}
|
||||
trainNumber={schedule.train ? schedule.train.code : schedule.trainNumber}
|
||||
trainNumber={schedule.trainNumber ?? schedule.train?.code ?? null}
|
||||
totalLengthMeters={schedule.trainSet?.totalLengthMeters}
|
||||
/>
|
||||
{isExportDisplay && diagramWagons.length ? (
|
||||
<Text size="xs" c="dimmed">
|
||||
Shown rear-first (export direction) — positions keep their original numbers.
|
||||
</Text>
|
||||
) : null}
|
||||
<Paper
|
||||
p="lg"
|
||||
radius="lg"
|
||||
@@ -885,6 +906,11 @@ export default function TrainScheduleV2DetailPage() {
|
||||
{schedule.trainNumber}
|
||||
</Badge>
|
||||
) : null}
|
||||
{schedule.train ? (
|
||||
<Text size="xs" c="dimmed" ff="monospace">
|
||||
Train {schedule.train.code}
|
||||
</Text>
|
||||
) : null}
|
||||
</Group>
|
||||
<Box maw={340}>
|
||||
<RouteCorridor
|
||||
|
||||
@@ -327,19 +327,24 @@ export default function TrainScheduleV2ListPage() {
|
||||
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.
|
||||
// Schedules created from the Train Builder show the direction-matched
|
||||
// run number first (falling back to the train code); legacy rows fall
|
||||
// back to their locomotive set.
|
||||
if (row.original.train) {
|
||||
const subtitle = [row.original.trainNumber ? row.original.train.code : null,
|
||||
row.original.train.trainName]
|
||||
.filter(Boolean)
|
||||
.join(" · ");
|
||||
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}
|
||||
{row.original.trainNumber ?? row.original.train.code}
|
||||
</Text>
|
||||
{row.original.train.trainName ? (
|
||||
{subtitle ? (
|
||||
<Text size="xs" c="dimmed" lh={1.2}>
|
||||
{row.original.train.trainName}
|
||||
{subtitle}
|
||||
</Text>
|
||||
) : null}
|
||||
</Stack>
|
||||
@@ -758,14 +763,21 @@ export default function TrainScheduleV2ListPage() {
|
||||
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)` : ""
|
||||
}`,
|
||||
}))}
|
||||
data={(trainsQuery.data ?? []).map((train) => {
|
||||
// Route direction picks which of the train's typed pair this run uses.
|
||||
const runNumber =
|
||||
selectedRoute?.direction === "IMPORT"
|
||||
? train.importTrainNumber
|
||||
: train.exportTrainNumber;
|
||||
return {
|
||||
value: train.id,
|
||||
label: `${train.code}${train.trainName ? ` — ${train.trainName}` : ""}${
|
||||
runNumber ? ` · runs as ${runNumber}` : ""
|
||||
} · ${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
|
||||
|
||||
@@ -62,7 +62,6 @@ export default function TrainSchedulingGlobalRulesPage() {
|
||||
"windowDurationHours",
|
||||
"docReviewMinutes",
|
||||
"paymentWindowMinutes",
|
||||
"reopenDelayMinutes",
|
||||
];
|
||||
const payload: Partial<Record<keyof TrainSchedulingGlobalRules, number>> = {};
|
||||
for (const key of fields) {
|
||||
@@ -261,17 +260,6 @@ export default function TrainSchedulingGlobalRulesPage() {
|
||||
min={1}
|
||||
disabled={loading}
|
||||
/>
|
||||
<DurationField
|
||||
label="Reopen delay"
|
||||
description="Delay after window close before reopening when the train is not full (90 min = 11:00 close → 12:30 reopen)"
|
||||
value={form.reopenDelayMinutes ?? ""}
|
||||
nativeUnit="minutes"
|
||||
onChange={(value) =>
|
||||
setForm((current) => ({ ...current, reopenDelayMinutes: value }))
|
||||
}
|
||||
min={1}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Group justify="flex-end">
|
||||
<Button loading={saving} disabled={loading} onClick={() => void handleSave()}>
|
||||
Save rules
|
||||
|
||||
Reference in New Issue
Block a user