booking operations and trains scheduling also allocations

This commit is contained in:
marshal
2026-06-10 00:48:32 +03:00
parent 675975bc08
commit 5774d7db9d
180 changed files with 13423 additions and 3877 deletions

View File

@@ -0,0 +1,121 @@
import { useEffect, useState } from "react";
import { Button, Card, Group, NumberInput, Stack, Text, Title } from "@mantine/core";
import { trainSchedulingService } from "@/services/trainScheduling.service";
import { useToast } from "@/hooks/use-toast";
import type { TrainSchedulingGlobalRules } from "@/types/trainScheduling";
export default function TrainSchedulingGlobalRulesPage() {
const { toast } = useToast();
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [form, setForm] = useState<Partial<TrainSchedulingGlobalRules>>({});
useEffect(() => {
void (async () => {
try {
const rules = await trainSchedulingService.getGlobalRules();
setForm(rules);
} catch {
toast({ title: "Failed to load train scheduling rules", variant: "destructive" });
} finally {
setLoading(false);
}
})();
}, [toast]);
const handleSave = async () => {
setSaving(true);
try {
const updated = await trainSchedulingService.updateGlobalRules({
maxTrainLengthMeters: Number(form.maxTrainLengthMeters),
maxTrainWeightTons: Number(form.maxTrainWeightTons),
maxWagonsPerTrain: Number(form.maxWagonsPerTrain),
max20ftContainerWeightTons: Number(form.max20ftContainerWeightTons),
max20ftPairWeightDiffTons: Number(form.max20ftPairWeightDiffTons),
});
setForm(updated);
toast({ title: "Train scheduling rules saved" });
} catch {
toast({ title: "Failed to save rules", variant: "destructive" });
} finally {
setSaving(false);
}
};
return (
<Stack gap="lg" maw={720}>
<Stack gap={4}>
<Title order={3}>Train scheduling rules</Title>
<Text size="sm" c="dimmed">
Global limits applied when previewing and assigning bookings to trains.
</Text>
</Stack>
<Card radius="xl" padding="lg" withBorder>
<Stack gap="md">
<NumberInput
label="Max train length (m)"
description="Sum of all wagon lengths must not exceed this"
value={form.maxTrainLengthMeters ?? ""}
onChange={(value) =>
setForm((current) => ({ ...current, maxTrainLengthMeters: Number(value) }))
}
min={1}
disabled={loading}
/>
<NumberInput
label="Max train weight (T)"
description="Total container and bulk cargo weight must not exceed this"
value={form.maxTrainWeightTons ?? ""}
onChange={(value) =>
setForm((current) => ({ ...current, maxTrainWeightTons: Number(value) }))
}
min={1}
disabled={loading}
/>
<NumberInput
label="Max wagons per train"
value={form.maxWagonsPerTrain ?? ""}
onChange={(value) =>
setForm((current) => ({ ...current, maxWagonsPerTrain: Number(value) }))
}
min={1}
disabled={loading}
/>
<NumberInput
label="Max 20ft container weight (T)"
description="Each individual 20ft container gross weight limit"
value={form.max20ftContainerWeightTons ?? ""}
onChange={(value) =>
setForm((current) => ({
...current,
max20ftContainerWeightTons: Number(value),
}))
}
min={0.001}
disabled={loading}
/>
<NumberInput
label="Max 20ft pair weight difference (T)"
description="When two 20ft containers share a wagon, |weight1 weight2| must not exceed this"
value={form.max20ftPairWeightDiffTons ?? ""}
onChange={(value) =>
setForm((current) => ({
...current,
max20ftPairWeightDiffTons: Number(value),
}))
}
min={0}
disabled={loading}
/>
<Group justify="flex-end">
<Button color="teal" loading={saving} disabled={loading} onClick={() => void handleSave()}>
Save rules
</Button>
</Group>
</Stack>
</Card>
</Stack>
);
}