mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
export flow and fix intercity issue
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import { Badge, Box, Group, Loader, Stack, Text, UnstyledButton } from "@mantine/core";
|
||||
import { CheckCircle2, TrainFront } from "lucide-react";
|
||||
|
||||
import type { Freight } from "@edr/types";
|
||||
|
||||
const BORDER = "#E6ECF2";
|
||||
const SELECTED = "#0E7A5F";
|
||||
|
||||
function departureLabel(iso: string): string {
|
||||
const d = new Date(iso);
|
||||
return d.toLocaleString("en-GB", {
|
||||
weekday: "short",
|
||||
day: "2-digit",
|
||||
month: "short",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
timeZone: "Africa/Addis_Ababa",
|
||||
});
|
||||
}
|
||||
|
||||
function closesLabel(iso: string | null): string | null {
|
||||
if (!iso) return null;
|
||||
return new Date(iso).toLocaleString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "short",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
timeZone: "Africa/Addis_Ababa",
|
||||
});
|
||||
}
|
||||
|
||||
export interface ExportTrainPickerProps {
|
||||
options: Freight.ExportTrainOption[];
|
||||
loading: boolean;
|
||||
value: string;
|
||||
onChange: (scheduleId: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export shipment-day train picker: one card per export train that day, with
|
||||
* live free-wagon space per wagon type for THIS booking's cargo. Full or
|
||||
* not-yet-open trains render disabled — the pick locks the booking onto that
|
||||
* train when the operation request is submitted.
|
||||
*/
|
||||
export function ExportTrainPicker({
|
||||
options,
|
||||
loading,
|
||||
value,
|
||||
onChange,
|
||||
}: ExportTrainPickerProps) {
|
||||
if (loading) {
|
||||
return (
|
||||
<Group gap="xs" mt="sm">
|
||||
<Loader size="xs" />
|
||||
<Text fz="12px" c="dimmed">
|
||||
Checking trains for this day…
|
||||
</Text>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
if (!options.length) return null;
|
||||
|
||||
return (
|
||||
<Box mt="sm">
|
||||
<Text fz="13px" fw={700} c="#10202F" mb={6}>
|
||||
Choose your train
|
||||
</Text>
|
||||
<Stack gap="xs">
|
||||
{options.map((option) => {
|
||||
const bookable = option.isOpen && option.fits;
|
||||
const selected = value === option.scheduleId;
|
||||
const closes = closesLabel(option.bookingClosesAt);
|
||||
return (
|
||||
<UnstyledButton
|
||||
key={option.scheduleId}
|
||||
onClick={() => bookable && onChange(option.scheduleId)}
|
||||
disabled={!bookable}
|
||||
style={{
|
||||
border: `1.5px solid ${selected ? SELECTED : BORDER}`,
|
||||
borderRadius: 10,
|
||||
padding: "10px 12px",
|
||||
opacity: bookable ? 1 : 0.55,
|
||||
cursor: bookable ? "pointer" : "not-allowed",
|
||||
background: selected ? "#F2FAF7" : "#FFFFFF",
|
||||
}}
|
||||
>
|
||||
<Group justify="space-between" align="flex-start" wrap="nowrap">
|
||||
<Group gap="xs" align="flex-start" wrap="nowrap">
|
||||
<TrainFront size={16} color={selected ? SELECTED : "#5B6B7A"} />
|
||||
<Box>
|
||||
<Text fz="13px" fw={600} c="#10202F">
|
||||
Departs {departureLabel(option.departure)} EAT
|
||||
</Text>
|
||||
<Text fz="12px" c="dimmed">
|
||||
{option.freeWagons} wagon{option.freeWagons === 1 ? "" : "s"} free
|
||||
for your cargo · you need {option.neededWagons}
|
||||
{closes ? ` · booking closes ${closes} EAT` : ""}
|
||||
</Text>
|
||||
<Group gap={6} mt={4}>
|
||||
{option.byWagonType.map((t) => (
|
||||
<Badge
|
||||
key={t.wagonTypeId ?? "default"}
|
||||
size="sm"
|
||||
variant="light"
|
||||
color={t.freeWagons > 0 ? "teal" : "gray"}
|
||||
>
|
||||
{t.code ?? t.name ?? "Wagon"}: {t.freeWagons} free
|
||||
</Badge>
|
||||
))}
|
||||
</Group>
|
||||
</Box>
|
||||
</Group>
|
||||
{selected ? (
|
||||
<CheckCircle2 size={18} color={SELECTED} />
|
||||
) : !option.isOpen ? (
|
||||
<Badge size="sm" color="gray" variant="light">
|
||||
Not open
|
||||
</Badge>
|
||||
) : !option.fits ? (
|
||||
<Badge size="sm" color="red" variant="light">
|
||||
Too little space
|
||||
</Badge>
|
||||
) : null}
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { ExportTrainPicker } from "./ExportTrainPicker";
|
||||
export type { ExportTrainPickerProps } from "./ExportTrainPicker";
|
||||
@@ -24,6 +24,8 @@ export { useFileViewer } from "./hooks/useFileViewer";
|
||||
|
||||
export { OperationDatePicker } from "./components/OperationDatePicker";
|
||||
export type { OperationDatePickerProps } from "./components/OperationDatePicker";
|
||||
export { ExportTrainPicker } from "./components/ExportTrainPicker";
|
||||
export type { ExportTrainPickerProps } from "./components/ExportTrainPicker";
|
||||
|
||||
export { CountdownTimer } from "./components/CountdownTimer";
|
||||
export type { CountdownTimerProps } from "./components/CountdownTimer";
|
||||
|
||||
Reference in New Issue
Block a user