Add contract booking windows feature

This commit is contained in:
Marshal
2026-07-03 15:01:16 +00:00
parent c977deb460
commit 7eae1a920e
18 changed files with 637 additions and 80 deletions

View File

@@ -29,22 +29,40 @@ export default function TrainSchedulingGlobalRulesPage() {
}, [toast]);
const handleSave = async () => {
// Every field must hold a real number — an empty box (cleared but not
// refilled) must not silently save as 0. Collect the numeric payload and
// reject if any value is blank or NaN.
const fields: (keyof TrainSchedulingGlobalRules)[] = [
"maxTrainLengthMeters",
"maxTrainWeightTons",
"maxWagonsPerTrain",
"max20ftContainerWeightTons",
"max20ftPairWeightDiffTons",
"importWindowLeadDays",
"exportBookingLeadHours",
"windowOpenHour",
"windowDurationHours",
"docReviewMinutes",
"paymentWindowMinutes",
"reopenDelayMinutes",
];
const payload: Partial<Record<keyof TrainSchedulingGlobalRules, number>> = {};
for (const key of fields) {
const raw = form[key];
const num = raw === "" || raw == null ? NaN : Number(raw);
if (!Number.isFinite(num)) {
toast({
title: "All fields are required — fill every value before saving.",
variant: "destructive",
});
return;
}
payload[key] = num;
}
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),
importWindowLeadDays: Number(form.importWindowLeadDays),
exportBookingLeadHours: Number(form.exportBookingLeadHours),
windowOpenHour: Number(form.windowOpenHour),
windowDurationHours: Number(form.windowDurationHours),
docReviewMinutes: Number(form.docReviewMinutes),
paymentWindowMinutes: Number(form.paymentWindowMinutes),
reopenDelayMinutes: Number(form.reopenDelayMinutes),
});
const updated = await trainSchedulingService.updateGlobalRules(payload);
setForm(updated);
toast({ title: "Train scheduling rules saved" });
} catch {
@@ -71,6 +89,7 @@ export default function TrainSchedulingGlobalRulesPage() {
setForm((current) => ({ ...current, maxTrainLengthMeters: value }))
}
min={1}
clampBehavior="strict"
disabled={loading}
/>
<NumberInput
@@ -81,6 +100,7 @@ export default function TrainSchedulingGlobalRulesPage() {
setForm((current) => ({ ...current, maxTrainWeightTons: value }))
}
min={1}
clampBehavior="strict"
disabled={loading}
/>
<NumberInput
@@ -90,6 +110,7 @@ export default function TrainSchedulingGlobalRulesPage() {
setForm((current) => ({ ...current, maxWagonsPerTrain: value }))
}
min={1}
clampBehavior="strict"
disabled={loading}
/>
<NumberInput
@@ -103,6 +124,7 @@ export default function TrainSchedulingGlobalRulesPage() {
}))
}
min={0.001}
clampBehavior="strict"
disabled={loading}
/>
<NumberInput
@@ -116,6 +138,7 @@ export default function TrainSchedulingGlobalRulesPage() {
}))
}
min={0}
clampBehavior="strict"
disabled={loading}
/>
</Stack>
@@ -135,6 +158,7 @@ export default function TrainSchedulingGlobalRulesPage() {
setForm((current) => ({ ...current, importWindowLeadDays: value }))
}
min={0}
clampBehavior="strict"
disabled={loading}
/>
<NumberInput
@@ -145,6 +169,7 @@ export default function TrainSchedulingGlobalRulesPage() {
setForm((current) => ({ ...current, exportBookingLeadHours: value }))
}
min={1}
clampBehavior="strict"
disabled={loading}
/>
<NumberInput
@@ -156,6 +181,7 @@ export default function TrainSchedulingGlobalRulesPage() {
}
min={0}
max={23}
clampBehavior="strict"
disabled={loading}
/>
<NumberInput
@@ -167,6 +193,7 @@ export default function TrainSchedulingGlobalRulesPage() {
min={0.25}
max={12}
step={0.25}
clampBehavior="strict"
disabled={loading}
/>
<NumberInput
@@ -177,6 +204,7 @@ export default function TrainSchedulingGlobalRulesPage() {
setForm((current) => ({ ...current, docReviewMinutes: value }))
}
min={0}
clampBehavior="strict"
disabled={loading}
/>
<NumberInput
@@ -187,6 +215,7 @@ export default function TrainSchedulingGlobalRulesPage() {
setForm((current) => ({ ...current, paymentWindowMinutes: value }))
}
min={1}
clampBehavior="strict"
disabled={loading}
/>
<NumberInput
@@ -197,6 +226,7 @@ export default function TrainSchedulingGlobalRulesPage() {
setForm((current) => ({ ...current, reopenDelayMinutes: value }))
}
min={1}
clampBehavior="strict"
disabled={loading}
/>
<Group justify="flex-end">