mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 21:08:12 +00:00
add goverment booking
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useMemo } from "react";
|
||||
import { ArrowRight, Package } from "lucide-react";
|
||||
import { ArrowRight, Landmark, Package } from "lucide-react";
|
||||
import {
|
||||
Accordion,
|
||||
Badge,
|
||||
@@ -21,11 +21,13 @@ function EligibleBookingRow({
|
||||
freightType,
|
||||
selected,
|
||||
onToggle,
|
||||
onSwitch,
|
||||
}: {
|
||||
booking: EligibleContainerBooking;
|
||||
freightType?: FreightType;
|
||||
selected: boolean;
|
||||
onToggle: () => void;
|
||||
onSwitch?: (booking: EligibleContainerBooking) => void;
|
||||
}) {
|
||||
const resolvedFreightType = booking.freightType ?? freightType;
|
||||
const isBulk = resolvedFreightType === "BULK";
|
||||
@@ -61,6 +63,26 @@ function EligibleBookingRow({
|
||||
{booking.schedulingStatus}
|
||||
</Badge>
|
||||
) : null}
|
||||
{booking.isGovernment ? (
|
||||
<Badge
|
||||
variant="light"
|
||||
size="xs"
|
||||
color="yellow"
|
||||
leftSection={<Landmark size={10} />}
|
||||
>
|
||||
Government
|
||||
</Badge>
|
||||
) : null}
|
||||
{booking.isGovernment && onSwitch ? (
|
||||
<Button
|
||||
variant="light"
|
||||
color="yellow"
|
||||
size="compact-xs"
|
||||
onClick={() => onSwitch(booking)}
|
||||
>
|
||||
Switch onto train
|
||||
</Button>
|
||||
) : null}
|
||||
</Group>
|
||||
<Text size="xs" c="dimmed">
|
||||
{booking.customer}
|
||||
@@ -102,6 +124,7 @@ export function EligibleBookingsPanel({
|
||||
onSelectionChange,
|
||||
assignedIds = [],
|
||||
freightType,
|
||||
onSwitch,
|
||||
}: {
|
||||
items: EligibleContainerBooking[];
|
||||
isLoading?: boolean;
|
||||
@@ -109,6 +132,7 @@ export function EligibleBookingsPanel({
|
||||
onSelectionChange: (ids: string[]) => void;
|
||||
assignedIds?: string[];
|
||||
freightType?: FreightType;
|
||||
onSwitch?: (booking: EligibleContainerBooking) => void;
|
||||
}) {
|
||||
const assignedSet = useMemo(() => new Set(assignedIds), [assignedIds]);
|
||||
|
||||
@@ -230,6 +254,7 @@ export function EligibleBookingsPanel({
|
||||
freightType={freightType}
|
||||
selected={selectedIds.includes(booking.id)}
|
||||
onToggle={() => toggle(booking.id)}
|
||||
onSwitch={onSwitch}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Tabs,
|
||||
Text,
|
||||
} from "@mantine/core";
|
||||
import { ArrowRight, Package, Train } from "lucide-react";
|
||||
import { ArrowRight, Landmark, Package, Train } from "lucide-react";
|
||||
|
||||
import type { EligibleContainerBooking, FreightType } from "@/types/trainScheduling";
|
||||
|
||||
@@ -17,6 +17,7 @@ export type AssignedBookingRow = {
|
||||
id: string;
|
||||
reference: string;
|
||||
weightTons?: number;
|
||||
isGovernment?: boolean;
|
||||
};
|
||||
|
||||
export function ScheduleBookingsStep({
|
||||
@@ -29,6 +30,7 @@ export function ScheduleBookingsStep({
|
||||
freightType,
|
||||
canRemove,
|
||||
onRemove,
|
||||
onSwitch,
|
||||
}: {
|
||||
assignedBookings: AssignedBookingRow[];
|
||||
eligibleItems: EligibleContainerBooking[];
|
||||
@@ -39,6 +41,7 @@ export function ScheduleBookingsStep({
|
||||
freightType?: FreightType;
|
||||
canRemove?: boolean;
|
||||
onRemove?: (bookingId: string) => void;
|
||||
onSwitch?: (booking: EligibleContainerBooking) => void;
|
||||
}) {
|
||||
return (
|
||||
<Paper p="md" radius="lg" withBorder style={{ borderColor: "var(--mantine-color-gray-2)" }}>
|
||||
@@ -91,6 +94,16 @@ export function ScheduleBookingsStep({
|
||||
{booking.weightTons}T
|
||||
</Badge>
|
||||
) : null}
|
||||
{booking.isGovernment ? (
|
||||
<Badge
|
||||
variant="light"
|
||||
size="xs"
|
||||
color="yellow"
|
||||
leftSection={<Landmark size={10} />}
|
||||
>
|
||||
Government
|
||||
</Badge>
|
||||
) : null}
|
||||
</Group>
|
||||
<Group gap={6}>
|
||||
<Text size="xs" c="dimmed">
|
||||
@@ -130,6 +143,7 @@ export function ScheduleBookingsStep({
|
||||
onSelectionChange={onSelectionChange}
|
||||
assignedIds={assignedIds}
|
||||
freightType={freightType}
|
||||
onSwitch={onSwitch}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user