import { useState } from 'react'; import { Badge, Button, Card, Divider, Group, Stack, Text, Tooltip } from '@mantine/core'; import { PackagePlus, Train as TrainIcon, Warehouse as WarehouseIcon } from 'lucide-react'; import { useQuery } from '@tanstack/react-query'; import { api } from '@/services/api'; import { InventoryStatusBadge } from './badges'; import { FreightVisual } from './FreightVisual'; import { formatDate } from './options'; import { ReceiveInventoryModal } from './ReceiveInventoryModal'; interface WarehouseInfoCardProps { bookingId: string; bookingReference?: string; /** * Booking payment status. Export cargo is received into the warehouse only * after the booking is paid — receiving an unpaid booking starts storage and * GRN against cargo the customer has not settled. Optional so existing callers * that do not have the booking to hand keep their current behaviour. */ paymentStatus?: string | null; /** * IMPORT | EXPORT | DOMESTIC. The payment gate is export-only: import cargo * arrives OFF a train, so blocking its receive would strand cargo already at * the yard. */ tradeDirection?: string | null; } function Row({ label, value }: { label: string; value: React.ReactNode }) { return ( {label} {value} ); } export function WarehouseInfoCard({ bookingId, bookingReference, paymentStatus, tradeDirection, }: WarehouseInfoCardProps) { const [modalOpen, setModalOpen] = useState(false); const { data, isLoading } = useQuery( api.warehouses.listInventory.queryOptions({ input: { filter: { bookingId } } }), ); const { data: scheduleView } = useQuery( api.warehouses.bookingSchedule.queryOptions({ input: { bookingId }, enabled: Boolean(bookingId), }), ); const items = data ?? []; const latest = items[0]; const schedule = scheduleView?.schedule; const wagon = scheduleView?.wagon; const isLoadedOrDispatched = latest?.status === 'LOADED' || latest?.status === 'DISPATCHED'; // Export only, and only when we were actually told the status — an absent prop // means the caller cannot answer, and guessing "unpaid" would disable a valid // action. Mirrors the server guard on receive(). const awaitingPayment = tradeDirection?.toUpperCase() === 'EXPORT' && paymentStatus != null && paymentStatus.toUpperCase() !== 'PAID'; return ( Warehouse Information {items.length > 0 && ( {items.length} item{items.length > 1 ? 's' : ''} )} {isLoading ? ( Loading… ) : !latest ? ( This booking has not been received at any warehouse yet. ) : ( } /> {isLoadedOrDispatched && ( <> )} )} {schedule && ( <> Train schedule (read-only) } labelPosition="left" /> {schedule.status} } /> {wagon?.wagonNumber && } {wagon?.sequenceNo != null && } )} {/* span wrapper so the tooltip still fires on the disabled button */} setModalOpen(false)} bookingId={bookingId} bookingLabel={bookingReference} /> ); }