mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 22:30:55 +00:00
173 lines
4.7 KiB
TypeScript
173 lines
4.7 KiB
TypeScript
import { useMemo, useState } from "react";
|
|
import { PackageCheck } from "lucide-react";
|
|
import { Badge, Button, Checkbox, Group, Loader, Paper, Stack, Text } from "@mantine/core";
|
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
import toast from "react-hot-toast";
|
|
|
|
import { api } from "@/services/api";
|
|
import type {
|
|
ImportLoadingBooking,
|
|
ImportLoadingBookingsResponse,
|
|
LoadingStatus,
|
|
} from "@/types/trainScheduling";
|
|
|
|
function ImportLoadingBookingRow({
|
|
booking,
|
|
selected,
|
|
onToggle,
|
|
}: {
|
|
booking: ImportLoadingBooking;
|
|
selected: boolean;
|
|
onToggle: () => void;
|
|
}) {
|
|
return (
|
|
<Group
|
|
align="flex-start"
|
|
wrap="nowrap"
|
|
p="sm"
|
|
style={{
|
|
border: `1px solid ${
|
|
selected ? "var(--mantine-color-edr-green-3)" : "var(--mantine-color-gray-2)"
|
|
}`,
|
|
borderRadius: 12,
|
|
background: selected ? "var(--mantine-color-edr-green-0)" : "white",
|
|
transition: "border-color 120ms ease, background 120ms ease",
|
|
}}
|
|
>
|
|
<Checkbox checked={selected} onChange={onToggle} mt={4} color="edr-green" />
|
|
<Stack gap={4} style={{ flex: 1 }}>
|
|
<Group gap="xs" wrap="wrap">
|
|
<PackageCheck size={14} />
|
|
<Text fw={600} size="sm">
|
|
{booking.reference ?? booking.id}
|
|
</Text>
|
|
<Badge
|
|
variant="light"
|
|
size="xs"
|
|
color={booking.loadingStatus === "LOADED" ? "edr-green" : "gray"}
|
|
>
|
|
{booking.loadingStatus}
|
|
</Badge>
|
|
</Group>
|
|
<Text size="xs" c="dimmed">
|
|
{booking.customer ?? "Unknown customer"}
|
|
</Text>
|
|
<Text size="xs" c="dimmed">
|
|
{booking.weightTons}T
|
|
</Text>
|
|
</Stack>
|
|
</Group>
|
|
);
|
|
}
|
|
|
|
export function ImportLoadingConfirmationPanel({
|
|
scheduleId,
|
|
items,
|
|
isLoading,
|
|
}: {
|
|
scheduleId: string;
|
|
items: ImportLoadingBooking[];
|
|
isLoading?: boolean;
|
|
}) {
|
|
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
|
const queryClient = useQueryClient();
|
|
|
|
const updateStatus = useMutation<
|
|
ImportLoadingBookingsResponse,
|
|
Error,
|
|
{ id: string; bookingIds: string[]; loadingStatus: LoadingStatus }
|
|
>({
|
|
...api.trainScheduling.updateImportLoadingStatus.mutationOptions(),
|
|
onSuccess: () => {
|
|
setSelectedIds([]);
|
|
queryClient.invalidateQueries({
|
|
queryKey: api.trainScheduling.importLoadingBookings.queryKey({ id: scheduleId }),
|
|
});
|
|
},
|
|
onError: (error) => {
|
|
toast.error(error instanceof Error ? error.message : "Could not update loading status");
|
|
},
|
|
});
|
|
|
|
const toggle = (id: string) => {
|
|
setSelectedIds((prev) =>
|
|
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id],
|
|
);
|
|
};
|
|
|
|
const allIds = useMemo(() => items.map((b) => b.id), [items]);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<Group justify="center" py="md">
|
|
<Loader size="sm" />
|
|
<Text size="sm" c="dimmed">
|
|
Loading import bookings…
|
|
</Text>
|
|
</Group>
|
|
);
|
|
}
|
|
|
|
if (!items.length) {
|
|
return (
|
|
<Paper p="lg" radius="lg" withBorder bg="gray.0">
|
|
<Text size="sm" c="dimmed" ta="center">
|
|
No paid import bookings with wagons allocated on this schedule
|
|
</Text>
|
|
</Paper>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Stack gap="md">
|
|
<Group justify="space-between" wrap="wrap">
|
|
<Text size="sm" fw={500}>
|
|
Import bookings ({items.length})
|
|
</Text>
|
|
<Group gap="sm">
|
|
<Button variant="light" size="compact-sm" onClick={() => setSelectedIds(allIds)}>
|
|
Select all
|
|
</Button>
|
|
<Button variant="subtle" size="compact-sm" onClick={() => setSelectedIds([])}>
|
|
Clear
|
|
</Button>
|
|
</Group>
|
|
</Group>
|
|
|
|
<Stack gap="sm">
|
|
{items.map((booking) => (
|
|
<ImportLoadingBookingRow
|
|
key={booking.id}
|
|
booking={booking}
|
|
selected={selectedIds.includes(booking.id)}
|
|
onToggle={() => toggle(booking.id)}
|
|
/>
|
|
))}
|
|
</Stack>
|
|
|
|
<Group gap="sm">
|
|
<Button
|
|
color="edr-green"
|
|
disabled={!selectedIds.length}
|
|
loading={updateStatus.isPending}
|
|
onClick={() =>
|
|
updateStatus.mutate({ id: scheduleId, bookingIds: selectedIds, loadingStatus: "LOADED" })
|
|
}
|
|
>
|
|
Mark loaded
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
disabled={!selectedIds.length}
|
|
loading={updateStatus.isPending}
|
|
onClick={() =>
|
|
updateStatus.mutate({ id: scheduleId, bookingIds: selectedIds, loadingStatus: "UNLOADED" })
|
|
}
|
|
>
|
|
Mark unloaded
|
|
</Button>
|
|
</Group>
|
|
</Stack>
|
|
);
|
|
}
|