mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 09:30:59 +00:00
122 lines
4.2 KiB
TypeScript
122 lines
4.2 KiB
TypeScript
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>
|
||
);
|
||
}
|