mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
126 lines
4.4 KiB
TypeScript
126 lines
4.4 KiB
TypeScript
import { Package } from "lucide-react";
|
|
import { SimpleGrid, Divider, Box, Table, Text, Badge } from "@mantine/core";
|
|
|
|
import type { BookingDetail } from "@/types/booking";
|
|
import { cargoTonsAndItems } from "@/utils/cargoWeight";
|
|
|
|
import { SectionCard } from "./SectionCard";
|
|
import { MetricTile } from "./MetricTile";
|
|
|
|
export interface BookingCargoCardProps {
|
|
booking: BookingDetail;
|
|
}
|
|
|
|
/** Cargo specs + container manifest table. */
|
|
export function BookingCargoCard({ booking }: BookingCargoCardProps) {
|
|
const containers = booking.bookingContainers ?? [];
|
|
const { tons, items } = cargoTonsAndItems(booking);
|
|
|
|
// Booking-level flags OR any container line carrying a count — the flag can
|
|
// lag the lines (per-line opt-ins), so either alone must light the tile.
|
|
const isHazardous =
|
|
booking.isHazardous ||
|
|
containers.some((c) => Number(c.hazardousQuantity ?? 0) > 0);
|
|
const isReefer =
|
|
booking.isReefer ||
|
|
containers.some((c) => Number(c.reeferQuantity ?? 0) > 0);
|
|
const showHandlingColumns = containers.some(
|
|
(c) =>
|
|
Number(c.hazardousQuantity ?? 0) > 0 || Number(c.reeferQuantity ?? 0) > 0,
|
|
);
|
|
|
|
return (
|
|
<SectionCard icon={Package} title="Cargo specifications" accent="orange">
|
|
<SimpleGrid cols={{ base: 1, sm: 3 }} spacing="sm">
|
|
<MetricTile
|
|
label="Cargo type"
|
|
value={booking.cargoType?.label ?? booking.freightType}
|
|
/>
|
|
<MetricTile label="Total VGM" value={`${tons} tons`} />
|
|
{items != null && <MetricTile label="Items" value={`${items}`} />}
|
|
<MetricTile
|
|
label="Hazardous"
|
|
value={isHazardous ? "Yes" : "No"}
|
|
highlight={isHazardous}
|
|
/>
|
|
<MetricTile
|
|
label="Refrigerated"
|
|
value={isReefer ? "Yes" : "No"}
|
|
highlight={isReefer}
|
|
/>
|
|
</SimpleGrid>
|
|
|
|
{/* Handling that changes how the yard treats the shipment is flagged
|
|
loudly, not buried in the grid. */}
|
|
{(isHazardous || isReefer) && (
|
|
<Box mt="sm">
|
|
{isHazardous && (
|
|
<Badge color="red" variant="filled" radius="sm" mr={8}>
|
|
Hazardous cargo
|
|
</Badge>
|
|
)}
|
|
{isReefer && (
|
|
<Badge color="blue" variant="filled" radius="sm">
|
|
Refrigerated cargo
|
|
</Badge>
|
|
)}
|
|
</Box>
|
|
)}
|
|
|
|
{containers.length > 0 && (
|
|
<>
|
|
<Divider my="lg" color="var(--mantine-color-gray-2)" />
|
|
<Box style={{ overflowX: "auto" }}>
|
|
<Table verticalSpacing="sm" horizontalSpacing="md" highlightOnHover>
|
|
<Table.Thead>
|
|
<Table.Tr>
|
|
<Table.Th>Container type</Table.Th>
|
|
<Table.Th>Qty</Table.Th>
|
|
<Table.Th>VGM / unit</Table.Th>
|
|
{showHandlingColumns && <Table.Th>Hazardous</Table.Th>}
|
|
{showHandlingColumns && <Table.Th>Reefer</Table.Th>}
|
|
</Table.Tr>
|
|
</Table.Thead>
|
|
<Table.Tbody>
|
|
{containers.map((c) => (
|
|
<Table.Tr key={c.id}>
|
|
<Table.Td>
|
|
<Text fw={600} size="sm">
|
|
{c.containerType?.label ?? c.containerType?.code ?? c.containerTypeId}
|
|
</Text>
|
|
</Table.Td>
|
|
<Table.Td>{c.quantity}</Table.Td>
|
|
<Table.Td>{c.vgmPerUnitTons} t</Table.Td>
|
|
{showHandlingColumns && (
|
|
<Table.Td>
|
|
{Number(c.hazardousQuantity ?? 0) > 0 ? (
|
|
<Text fw={700} c="red" size="sm">
|
|
{c.hazardousQuantity}
|
|
</Text>
|
|
) : (
|
|
"—"
|
|
)}
|
|
</Table.Td>
|
|
)}
|
|
{showHandlingColumns && (
|
|
<Table.Td>
|
|
{Number(c.reeferQuantity ?? 0) > 0 ? (
|
|
<Text fw={700} c="blue" size="sm">
|
|
{c.reeferQuantity}
|
|
</Text>
|
|
) : (
|
|
"—"
|
|
)}
|
|
</Table.Td>
|
|
)}
|
|
</Table.Tr>
|
|
))}
|
|
</Table.Tbody>
|
|
</Table>
|
|
</Box>
|
|
</>
|
|
)}
|
|
</SectionCard>
|
|
);
|
|
}
|