import { useMemo } from "react"; import { Badge, Button, Card, Group, Paper, Progress, Select, SimpleGrid, Stack, Text, TextInput, } from "@mantine/core"; import { CheckCircle2, Container } from "lucide-react"; import type { ContainerPlacement, ContainerUnitRow } from "@/types/trainScheduling"; import { autoFillPlacements, unitKey, validateLocalPlacements } from "./containerPlacement.util"; export function ContainerPlacementGrid({ units, containerSlots, placements, onChange, }: { units: ContainerUnitRow[]; containerSlots: number[]; placements: ContainerPlacement[]; onChange: (placements: ContainerPlacement[]) => void; }) { const placementMap = useMemo(() => { const map = new Map(); for (const placement of placements) { map.set(unitKey(placement.bookingContainerId, placement.unitIndex), placement); } return map; }, [placements]); const issues = useMemo(() => validateLocalPlacements(units, placements), [units, placements]); const completedCount = useMemo( () => units.filter((unit) => { const placement = placementMap.get(unitKey(unit.bookingContainerId, unit.unitIndex)); return placement?.sequenceNo && placement.containerNumber?.trim(); }).length, [units, placementMap], ); const slotOptions = containerSlots.map((seq) => ({ value: String(seq), label: `Wagon #${seq}`, })); const updatePlacement = (unit: ContainerUnitRow, patch: Partial) => { const key = unitKey(unit.bookingContainerId, unit.unitIndex); const existing = placementMap.get(key); const next: ContainerPlacement = { bookingContainerId: unit.bookingContainerId, unitIndex: unit.unitIndex, sequenceNo: existing?.sequenceNo ?? containerSlots[0] ?? 1, containerNumber: existing?.containerNumber, sealNumber: existing?.sealNumber, ...patch, }; onChange([ ...placements.filter( (p) => !(p.bookingContainerId === unit.bookingContainerId && p.unitIndex === unit.unitIndex), ), next, ]); }; if (!units.length) { return ( No container units in this selection. ); } const progress = units.length ? Math.round((completedCount / units.length) * 100) : 0; return ( Container assignment Map each booking unit to a wagon slot and enter the container number. One wagon fits either 1×40ft or 2×20ft containers. {completedCount} of {units.length} units complete {progress}% {issues.length ? ( {issues.map((issue) => ( {issue} ))} ) : ( } > All units mapped )} {units.map((unit) => { const key = unitKey(unit.bookingContainerId, unit.unitIndex); const placement = placementMap.get(key); const isComplete = placement?.sequenceNo && placement.containerNumber?.trim(); return ( {unit.bookingReference} {unit.label} · {unit.containerTypeCode} · {unit.grossWeightTons}T {isComplete ? "Ready" : "Pending"}