mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
144 lines
4.1 KiB
TypeScript
144 lines
4.1 KiB
TypeScript
import { useMemo, useState } from "react";
|
|
import {
|
|
Alert,
|
|
Badge,
|
|
Button,
|
|
Checkbox,
|
|
Group,
|
|
Modal,
|
|
Stack,
|
|
Text,
|
|
} from "@mantine/core";
|
|
import { Landmark } from "lucide-react";
|
|
|
|
import type {
|
|
EligibleContainerBooking,
|
|
TrainScheduleDetail,
|
|
} from "@/types/trainScheduling";
|
|
|
|
/**
|
|
* Government-priority switch confirmation: pick the assigned commercial
|
|
* bookings to take off the train so the government booking can have their
|
|
* wagons. Mount with key={govBooking.id} so selection resets per booking.
|
|
*/
|
|
export function SwitchGovernmentBookingModal({
|
|
opened,
|
|
onClose,
|
|
govBooking,
|
|
assignedBookings,
|
|
loading,
|
|
onConfirm,
|
|
}: {
|
|
opened: boolean;
|
|
onClose: () => void;
|
|
govBooking: EligibleContainerBooking | null;
|
|
assignedBookings: TrainScheduleDetail["bookings"];
|
|
loading?: boolean;
|
|
onConfirm: (removeBookingIds: string[]) => void;
|
|
}) {
|
|
const [selected, setSelected] = useState<string[]>([]);
|
|
|
|
const candidates = useMemo(
|
|
() => assignedBookings.filter((b) => !b.isGovernment && b.wagonAssigned),
|
|
[assignedBookings],
|
|
);
|
|
const freedWagons = candidates
|
|
.filter((b) => selected.includes(b.id))
|
|
.reduce((sum, b) => sum + (b.wagonsRequired ?? 0), 0);
|
|
|
|
const toggle = (id: string) =>
|
|
setSelected((ids) =>
|
|
ids.includes(id) ? ids.filter((x) => x !== id) : [...ids, id],
|
|
);
|
|
|
|
return (
|
|
<Modal
|
|
opened={opened}
|
|
onClose={onClose}
|
|
title={
|
|
<Group gap="xs">
|
|
<Landmark size={18} />
|
|
<Text fw={600}>Switch in government booking</Text>
|
|
</Group>
|
|
}
|
|
radius="lg"
|
|
size="lg"
|
|
>
|
|
<Stack gap="md">
|
|
<Alert color="yellow" radius="md" variant="light">
|
|
Government bookings have priority. Select the booking(s) to switch out —
|
|
together they must free at least as many wagons as{" "}
|
|
<Text span fw={600}>
|
|
{govBooking?.reference}
|
|
</Text>{" "}
|
|
needs. Switched-out customers are notified to rebook.
|
|
</Alert>
|
|
|
|
{candidates.length ? (
|
|
<Stack gap="xs">
|
|
{candidates.map((b) => (
|
|
<Group
|
|
key={b.id}
|
|
justify="space-between"
|
|
p="sm"
|
|
style={{
|
|
border: "1px solid var(--mantine-color-gray-2)",
|
|
borderRadius: 12,
|
|
}}
|
|
>
|
|
<Checkbox
|
|
color="edr-green"
|
|
checked={selected.includes(b.id)}
|
|
onChange={() => toggle(b.id)}
|
|
label={
|
|
<Group gap="xs">
|
|
<Text fw={600} size="sm">
|
|
{b.reference}
|
|
</Text>
|
|
<Text size="xs" c="dimmed">
|
|
{b.customer}
|
|
</Text>
|
|
</Group>
|
|
}
|
|
/>
|
|
<Group gap="xs">
|
|
<Badge variant="outline" size="xs" color="edr-green">
|
|
{b.weightTons}T
|
|
</Badge>
|
|
<Badge variant="light" size="xs">
|
|
{b.wagonsRequired ?? "?"} wagon{b.wagonsRequired === 1 ? "" : "s"}
|
|
</Badge>
|
|
</Group>
|
|
</Group>
|
|
))}
|
|
</Stack>
|
|
) : (
|
|
<Text size="sm" c="dimmed" ta="center" py="md">
|
|
No commercial bookings with wagons on this train to switch out.
|
|
</Text>
|
|
)}
|
|
|
|
<Group justify="space-between">
|
|
<Badge variant="light" color={selected.length ? "edr-green" : "gray"}>
|
|
Frees {freedWagons} wagon{freedWagons === 1 ? "" : "s"}
|
|
</Badge>
|
|
<Group gap="sm">
|
|
<Button variant="default" radius="md" onClick={onClose}>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
color="edr-green"
|
|
radius="md"
|
|
disabled={!selected.length}
|
|
loading={loading}
|
|
onClick={() => onConfirm(selected)}
|
|
>
|
|
Confirm switch
|
|
</Button>
|
|
</Group>
|
|
</Group>
|
|
</Stack>
|
|
</Modal>
|
|
);
|
|
}
|