import { Fragment, useEffect, useMemo, useState, type MouseEvent } from 'react'; import { ActionIcon, Alert, Badge, Button, Checkbox, Group, Loader, Modal, NumberInput, ScrollArea, Select, Stack, Table, Tabs, Text, Textarea, TextInput, Tooltip, } from '@mantine/core'; import { ChevronDown, ChevronRight, ClipboardCheck, Eye, FileText, History, Info, PackageCheck, PackageOpen, PackageSearch, Send, Search, Train, Truck, } from 'lucide-react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { api } from '@/services/api'; import { QUERY_KEYS } from '@/constants/QUERY_KEYS'; import { useToast } from '@/hooks/use-toast'; import { useInventoryInquiry } from '@/hooks/useWarehouses'; import { firstMileService } from '@/services/first-mile.service'; import { warehouseService } from '@/services/warehouse.service'; import type { EligibleBooking, InventoryInquiryFilter, InventoryInquiryResult, ImportTrain, ImportTrainItem, ImportUnloadedItem, ReadyToLoadRow, ReceiveInventoryPayload, TruckEntrancePayload, WarehouseInventoryItem, } from '@/types/warehouse'; import { BookingSelect } from './BookingSelect'; import { DeliverInventoryModal } from './DeliverInventoryModal'; import { FeePreviewModal } from './FeePreviewModal'; import { InspectionReportModal } from './InspectionReportModal'; import { InventoryDetailModal } from './InventoryDetailModal'; import { InventoryHistoryModal } from './InventoryHistoryModal'; import { InventoryWorkbench } from './InventoryWorkbench'; import { InventoryInquiryDetailModal } from './InventoryInquiryDetailModal'; import { ReleaseOrderModal } from './ReleaseOrderModal'; import { WarehouseInquiryTable } from './WarehouseInquiryTable'; import { extractErrorMessage, formatDate, formatNumber, inventoryStatusOptions } from './options'; import { openPdfBlob } from './pdf'; import '@/components/overview/overview.css'; interface ReceiveInventoryModalProps { opened: boolean; onClose: () => void; /** When supplied the modal locks to a single booking (legacy single-receive). */ bookingId?: string; bookingLabel?: string; onReceived?: () => void; } function GrnDocumentButton({ inventoryId, grnNumber }: { inventoryId: string; grnNumber?: string | null }) { const { toast } = useToast(); const [loading, setLoading] = useState(false); const openDocument = async (event: MouseEvent) => { event.stopPropagation(); if (!grnNumber) { toast({ variant: 'destructive', title: 'GRN document unavailable', description: 'This item has no GRN number yet.' }); return; } setLoading(true); const pdfWindow = window.open('', '_blank'); try { const response = await warehouseService.downloadGrnDocument(inventoryId); const opened = openPdfBlob(response.data, `grn-${grnNumber}.pdf`, pdfWindow); toast({ title: opened ? 'GRN document opened' : 'GRN document downloaded' }); } catch (error) { pdfWindow?.close(); toast({ variant: 'destructive', title: 'GRN document failed', description: extractErrorMessage(error) }); } finally { setLoading(false); } }; return ( ); } interface Location { warehouseId: string; yardId: string; zoneId: string; } interface TruckEntranceFormState { ownerName: string; consigneeDetails: string; edrDigitalBookingId: string; tin: string; customerPhone: string; truckPlateNumber: string; trailerPlateNumber: string; assignedEquipmentNumber: string; customsSealNumber: string; declarationNumber: string; incoterms: string; hsCodes: string; itemCode: string; itemDescription: string; packagingType: string; unitCount: number | ''; grossWeightKg: number | ''; weighingRequired: boolean | null; netWeightKg: number | ''; volumeDimensions: string; conditionAtReceipt: string; damagedRejectedQuantity: number | ''; warehouseCodeLocation: string; driverName: string; driverPhone: string; driverLicenseNumber: string; truckType: string; entranceTareWeightKg: number | ''; exitTareWeightKg: number | ''; driverSignatoryName: string; warehouseManagerName: string; } interface LockedTruckEntranceFields { ownerName?: boolean; consigneeDetails?: boolean; tin?: boolean; edrDigitalBookingId?: boolean; customerPhone?: boolean; truckPlateNumber?: boolean; trailerPlateNumber?: boolean; assignedEquipmentNumber?: boolean; itemDescription?: boolean; packagingType?: boolean; unitCount?: boolean; grossWeightKg?: boolean; driverName?: boolean; driverPhone?: boolean; driverLicenseNumber?: boolean; truckType?: boolean; } type PackagingFreightType = 'CONTAINER' | 'BULK' | 'MIXED'; const emptyTruckEntrance = (): TruckEntranceFormState => ({ ownerName: '', consigneeDetails: '', edrDigitalBookingId: '', tin: '', customerPhone: '', truckPlateNumber: '', trailerPlateNumber: '', assignedEquipmentNumber: '', customsSealNumber: '', declarationNumber: '', incoterms: '', hsCodes: '', itemCode: '', itemDescription: '', packagingType: '', unitCount: '', grossWeightKg: '', weighingRequired: null, netWeightKg: '', volumeDimensions: '', conditionAtReceipt: '', damagedRejectedQuantity: '', warehouseCodeLocation: '', driverName: '', driverPhone: '', driverLicenseNumber: '', truckType: '', entranceTareWeightKg: '', exitTareWeightKg: '', driverSignatoryName: '', warehouseManagerName: '', }); const toTruckEntrancePayload = (form: TruckEntranceFormState): TruckEntrancePayload => ({ ownerName: form.ownerName.trim() || undefined, consigneeDetails: form.consigneeDetails.trim() || undefined, edrDigitalBookingId: form.edrDigitalBookingId.trim() || undefined, tin: form.tin.trim() || undefined, customerPhone: form.customerPhone.trim() || undefined, truckPlateNumber: form.truckPlateNumber.trim(), trailerPlateNumber: form.trailerPlateNumber.trim() || undefined, assignedEquipmentNumber: form.assignedEquipmentNumber.trim() || undefined, customsSealNumber: form.customsSealNumber.trim() || undefined, declarationNumber: form.declarationNumber.trim() || undefined, incoterms: form.incoterms.trim() || undefined, hsCodes: form.hsCodes.trim() || undefined, itemCode: form.itemCode.trim() || undefined, itemDescription: form.itemDescription.trim() || undefined, packagingType: form.packagingType.trim() || undefined, unitCount: form.unitCount === '' ? undefined : Number(form.unitCount), weighingRequired: form.weighingRequired ?? undefined, grossWeightKg: form.weighingRequired && form.grossWeightKg !== '' ? Number(form.grossWeightKg) : undefined, netWeightKg: form.netWeightKg === '' ? undefined : Number(form.netWeightKg), volumeDimensions: form.volumeDimensions.trim() || undefined, conditionAtReceipt: form.conditionAtReceipt.trim() || undefined, damagedRejectedQuantity: form.damagedRejectedQuantity === '' ? undefined : Number(form.damagedRejectedQuantity), warehouseCodeLocation: form.warehouseCodeLocation.trim() || undefined, driverName: form.driverName.trim(), driverPhone: form.driverPhone.trim(), driverLicenseNumber: form.driverLicenseNumber.trim() || undefined, truckType: form.truckType.trim() || undefined, entranceTareWeightKg: form.entranceTareWeightKg === '' ? undefined : Number(form.entranceTareWeightKg), exitTareWeightKg: form.weighingRequired && form.exitTareWeightKg !== '' ? Number(form.exitTareWeightKg) : undefined, driverSignatoryName: form.driverSignatoryName.trim() || undefined, warehouseManagerName: form.warehouseManagerName.trim() || undefined, }); const commonNonEmptyValue = (values: Array) => { const unique = [...new Set(values.map((value) => value?.trim()).filter(Boolean))] as string[]; return unique.length === 1 ? unique[0] : ''; }; const truckEntranceFromBookings = (bookings: EligibleBooking[]): { form: TruckEntranceFormState; lockedFields: LockedTruckEntranceFields; packagingFreightType: PackagingFreightType; } => { const ownerName = commonNonEmptyValue(bookings.map((booking) => booking.customer)); const tin = commonNonEmptyValue(bookings.map((booking) => booking.customerTin)); const customerPhone = commonNonEmptyValue(bookings.map((booking) => booking.customerPhone)); const consigneeDetails = commonNonEmptyValue(bookings.map((booking) => booking.customer)); const assignedEquipmentNumber = commonNonEmptyValue( bookings.map((booking) => booking.customerTruckContainerNumber || booking.containerNumber), ); const itemDescription = commonNonEmptyValue(bookings.map((booking) => booking.cargoDescription ?? booking.cargo)); const packagingType = commonNonEmptyValue(bookings.map((booking) => booking.containerPackagingType)); const truckPlateNumber = commonNonEmptyValue( bookings.map((booking) => booking.firstMileTruckPlateNumber || booking.customerTruckPlateNumber), ); const trailerPlateNumber = commonNonEmptyValue(bookings.map((booking) => booking.firstMileTrailerPlateNumber)); const driverName = commonNonEmptyValue( bookings.map((booking) => booking.firstMileDriverName || booking.customerTruckDriverName), ); const driverPhone = commonNonEmptyValue(bookings.map((booking) => booking.firstMileDriverPhone)); const driverLicenseNumber = commonNonEmptyValue(bookings.map((booking) => booking.firstMileDriverLicenseNumber)); const truckType = commonNonEmptyValue( bookings.map((booking) => booking.firstMileTruckType || booking.customerTruckType), ); const edrDigitalBookingId = bookings.length === 1 ? bookings[0]?.reference ?? bookings[0]?.id ?? '' : commonNonEmptyValue(bookings.map((booking) => booking.reference)); const unitCount = bookings.length === 1 && bookings[0]?.containerQuantity != null ? Number(bookings[0].containerQuantity) : ''; const freightTypes = [...new Set(bookings.map((booking) => booking.freightType).filter(Boolean))]; const packagingFreightType = freightTypes.length === 1 && freightTypes[0] === 'CONTAINER' ? 'CONTAINER' : freightTypes.length === 1 && freightTypes[0] === 'BULK' ? 'BULK' : 'MIXED'; return { form: { ...emptyTruckEntrance(), ownerName, consigneeDetails, tin, customerPhone, edrDigitalBookingId, assignedEquipmentNumber, itemDescription, packagingType, unitCount, grossWeightKg: '', truckPlateNumber, trailerPlateNumber, driverName, driverPhone, driverLicenseNumber, truckType, }, lockedFields: { ownerName: Boolean(ownerName), consigneeDetails: Boolean(consigneeDetails), tin: Boolean(tin), edrDigitalBookingId: Boolean(edrDigitalBookingId), customerPhone: Boolean(customerPhone), assignedEquipmentNumber: Boolean(assignedEquipmentNumber), itemDescription: Boolean(itemDescription), packagingType: Boolean(packagingType), unitCount: unitCount !== '', grossWeightKg: false, truckPlateNumber: Boolean(truckPlateNumber), trailerPlateNumber: Boolean(trailerPlateNumber), driverName: Boolean(driverName), driverPhone: Boolean(driverPhone), driverLicenseNumber: Boolean(driverLicenseNumber), truckType: Boolean(truckType), }, packagingFreightType, }; }; const BULK_PACKAGING_TYPE_OPTIONS = [ { value: 'BAG', label: 'Bag' }, { value: 'SACK', label: 'Sack' }, { value: 'BALE', label: 'Bale' }, { value: 'CARTON', label: 'Carton' }, { value: 'CRATE', label: 'Crate' }, { value: 'DRUM', label: 'Drum' }, { value: 'BARREL', label: 'Barrel' }, { value: 'PALLET', label: 'Pallet' }, { value: 'LOOSE_BULK', label: 'Loose bulk' }, { value: 'OTHER', label: 'Other' }, ]; const CONTAINER_PACKAGING_TYPE_OPTIONS = [ { value: 'CONTAINER_20FT', label: '20 ft container' }, { value: 'CONTAINER_40FT', label: '40 ft container' }, { value: 'CONTAINER_45FT', label: '45 ft container' }, { value: 'REEFER_CONTAINER', label: 'Reefer container' }, { value: 'TANK_CONTAINER', label: 'Tank container' }, { value: 'FLAT_RACK_CONTAINER', label: 'Flat rack container' }, { value: 'OPEN_TOP_CONTAINER', label: 'Open top container' }, { value: 'OTHER_CONTAINER', label: 'Other container' }, ]; const packagingOptionsFor = (freightType: PackagingFreightType) => freightType === 'CONTAINER' ? CONTAINER_PACKAGING_TYPE_OPTIONS : freightType === 'BULK' ? BULK_PACKAGING_TYPE_OPTIONS : [...CONTAINER_PACKAGING_TYPE_OPTIONS, ...BULK_PACKAGING_TYPE_OPTIONS]; function TruckEntranceFields({ value, onChange, lockedFields, packagingFreightType = 'MIXED', allowTruckWeighing = true, }: { value: TruckEntranceFormState; onChange: (next: TruckEntranceFormState) => void; lockedFields?: LockedTruckEntranceFields; packagingFreightType?: PackagingFreightType; allowTruckWeighing?: boolean; }) { const packagingOptions = packagingOptionsFor(packagingFreightType); const quantityLabel = packagingFreightType === 'CONTAINER' ? 'Container quantity' : packagingFreightType === 'BULK' ? 'Unit count' : 'Quantity'; return ( Customer and cargo ownership onChange({ ...value, ownerName: e.currentTarget.value })} /> onChange({ ...value, consigneeDetails: e.currentTarget.value })} /> onChange({ ...value, edrDigitalBookingId: e.currentTarget.value })} /> onChange({ ...value, tin: e.currentTarget.value })} /> onChange({ ...value, customerPhone: e.currentTarget.value })} /> Transport and equipment tracking onChange({ ...value, truckPlateNumber: e.currentTarget.value })} /> onChange({ ...value, trailerPlateNumber: e.currentTarget.value })} /> onChange({ ...value, assignedEquipmentNumber: e.currentTarget.value })} /> onChange({ ...value, customsSealNumber: e.currentTarget.value })} /> onChange({ ...value, driverName: e.currentTarget.value })} /> onChange({ ...value, driverPhone: e.currentTarget.value })} /> onChange({ ...value, driverLicenseNumber: e.currentTarget.value })} /> onChange({ ...value, truckType: e.currentTarget.value })} /> {allowTruckWeighing ? ( <> onChange({ ...value, packagingType: v ?? '' })} /> onChange({ ...value, unitCount: v === '' ? '' : Number(v) })} /> onChange({ ...value, netWeightKg: v === '' ? '' : Number(v) })} /> onChange({ ...value, volumeDimensions: e.currentTarget.value })} /> Quality and inspection onChange({ ...value, conditionAtReceipt: e.currentTarget.value })} /> onChange({ ...value, damagedRejectedQuantity: v === '' ? '' : Number(v) })} /> onChange({ ...value, warehouseCodeLocation: e.currentTarget.value })} /> onChange({ ...value, driverSignatoryName: e.currentTarget.value })} /> onChange({ ...value, warehouseManagerName: e.currentTarget.value })} /> ); } /** Cascading Warehouse → Yard → Zone selectors (ACTIVE only). */ function LocationSelects({ value, onChange, }: { value: Location; onChange: (next: Location) => void; }) { const warehousesQuery = useQuery( api.warehouses.list.queryOptions({ input: { filter: { status: 'ACTIVE' } } }), ); const yardsQuery = useQuery( api.warehouses.listYards.queryOptions({ input: { warehouseId: value.warehouseId ?? '' }, enabled: Boolean(value.warehouseId), }), ); const zonesQuery = useQuery( api.warehouses.listZones.queryOptions({ input: { yardId: value.yardId ?? '' }, enabled: Boolean(value.yardId), }), ); const warehouseOptions = useMemo( () => (warehousesQuery.data ?? []).map((w) => ({ value: w.id, label: `${w.name} (${w.code})` })), [warehousesQuery.data], ); const yardOptions = useMemo( () => (yardsQuery.data ?? []) .filter((y) => y.status === 'ACTIVE') .map((y) => ({ value: y.id, label: `${y.name} (${y.code})` })), [yardsQuery.data], ); const zoneOptions = useMemo( () => (zonesQuery.data ?? []) .filter((z) => z.status === 'ACTIVE') .map((z) => ({ value: z.id, label: `${z.name} (${z.code})` })), [zonesQuery.data], ); return ( onChange({ ...value, yardId: v ?? '', zoneId: '' })} /> setDraft((filter) => ({ ...filter, status: value as InventoryInquiryFilter['status'] }))} w={190} /> {isFetching ? ( ) : !hasSearch ? ( Search by booking reference, container number, cargo or status to locate inventory. ) : results.length === 0 ? ( No inventory found for the current filters. ) : ( )} setViewResult(null)} result={viewResult} /> ); } function ImportWarehouseTabs({ enabled, onChanged }: { enabled: boolean; onChanged?: () => void }) { const [activeTab, setActiveTab] = useState('arrive-queue'); const { data: arriveRows = [] } = useQuery(api.warehouses.importArriveQueue.queryOptions({ enabled })); const { data: unloadedRows = [] } = useQuery(api.warehouses.importUnloadedQueue.queryOptions({ enabled })); const { data: dispatchRows = [] } = useQuery(api.warehouses.importPickupReadyQueue.queryOptions({ enabled })); const tabs: WarehouseQueueTab[] = [ { value: 'arrive-queue', label: 'Arrival Queue', icon: , count: arriveRows.length, }, { value: 'unloaded-queue', label: 'Unloaded Queue', icon: , count: unloadedRows.length, }, { value: 'dispatch-queue', label: 'Dispatch Queue', icon: , count: dispatchRows.length, }, { value: 'locate-booking', label: 'Locate Booking', icon: , }, ]; return ( {activeTab === 'arrive-queue' && ( )} {activeTab === 'unloaded-queue' && ( )} {activeTab === 'dispatch-queue' && ( )} {activeTab === 'locate-booking' && ( )} ); } function ExportWarehouseTabs({ enabled, location, onChanged, }: { enabled: boolean; location: Location; onChanged?: () => void; }) { const [activeTab, setActiveTab] = useState('receive-queue'); const { data: eligibleRows = [] } = useQuery( api.warehouses.eligibleBookings.queryOptions({ input: { direction: 'EXPORT' }, enabled, }), ); const { data: receivedRows = [] } = useQuery(api.warehouses.receivedExport.queryOptions({ enabled })); const { data: readyRows = [] } = useQuery(api.warehouses.readyToLoadExport.queryOptions({ enabled })); const { data: loadedRows = [] } = useQuery(api.warehouses.loadedExport.queryOptions({ enabled })); const exportEligibleCount = useMemo( () => eligibleRows.filter((row) => row.direction === 'EXPORT').length, [eligibleRows], ); const tabs: WarehouseQueueTab[] = [ { value: 'receive-queue', label: 'Receive to Warehouse', icon: , count: exportEligibleCount, }, { value: 'received', label: 'Received', icon: , count: receivedRows.length, }, { value: 'ready-to-load', label: 'Ready To Load', icon: , count: readyRows.length, }, { value: 'loaded', label: 'Loaded', icon: , count: loadedRows.length, }, { value: 'dispatch-queue', label: 'Dispatch Queue', icon: , count: loadedRows.length, }, { value: 'locate-booking', label: 'Locate Booking', icon: , }, ]; return ( {activeTab === 'receive-queue' && ( )} {activeTab === 'received' && ( )} {activeTab === 'ready-to-load' && ( )} {activeTab === 'loaded' && ( )} {activeTab === 'dispatch-queue' && ( )} {activeTab === 'locate-booking' && ( )} ); } export function WarehouseFlowWorkbench({ direction = 'BOTH', enabled = true, onChanged, }: WarehouseFlowWorkbenchProps) { const [location, setLocation] = useState({ warehouseId: '', yardId: '', zoneId: '' }); const [tab, setTab] = useState>( direction === 'EXPORT' ? 'EXPORT' : 'IMPORT', ); const activeDirection = direction === 'BOTH' ? tab : direction; useEffect(() => { if (enabled) setLocation({ warehouseId: '', yardId: '', zoneId: '' }); }, [enabled, direction]); return ( {activeDirection === 'EXPORT' && ( )} {direction === 'BOTH' ? ( setTab((v as 'IMPORT' | 'EXPORT') ?? 'IMPORT')}> }> Import }> Export ) : activeDirection === 'IMPORT' ? ( ) : ( )} ); } /** New bulk Receive: Import / Export tabs with eligible PAID bookings. */ function BulkReceiveModal({ opened, onClose, onReceived }: ReceiveInventoryModalProps) { return ( ); } interface SingleFormState { warehouseId: string; yardId: string; zoneId: string; quantity: number | ''; weight: number | ''; volume: number | ''; notes: string; } /** Legacy single-booking receive — used when a specific bookingId is supplied. */ function SingleBookingReceiveModal({ opened, onClose, bookingId, bookingLabel, onReceived, }: ReceiveInventoryModalProps) { const { toast } = useToast(); const receiveMutation = useMutation( api.warehouses.receiveInventory.mutationOptions(), ); const [selectedBooking, setSelectedBooking] = useState(bookingId ?? ''); const [form, setForm] = useState({ warehouseId: '', yardId: '', zoneId: '', quantity: '', weight: '', volume: '', notes: '', }); const [truckForm, setTruckForm] = useState(emptyTruckEntrance()); useEffect(() => { if (opened) { setSelectedBooking(bookingId ?? ''); setForm({ warehouseId: '', yardId: '', zoneId: '', quantity: '', weight: '', volume: '', notes: '' }); setTruckForm(emptyTruckEntrance()); } }, [opened, bookingId]); const location: Location = { warehouseId: form.warehouseId, yardId: form.yardId, zoneId: form.zoneId }; const handleSubmit = async () => { if (!selectedBooking.trim()) { toast({ variant: 'destructive', title: 'Booking is required' }); return; } if (!form.warehouseId || !form.yardId || !form.zoneId) { toast({ variant: 'destructive', title: 'Select warehouse, yard and zone' }); return; } if (!truckForm.truckPlateNumber.trim() || !truckForm.driverName.trim() || !truckForm.driverPhone.trim() || truckForm.entranceTareWeightKg === '') { toast({ variant: 'destructive', title: 'Truck, driver and entrance tare weight are required' }); return; } const payload: ReceiveInventoryPayload = { bookingId: selectedBooking.trim(), warehouseId: form.warehouseId, yardId: form.yardId, zoneId: form.zoneId, quantity: 0, weight: 0, volume: form.volume === '' ? undefined : Number(form.volume), notes: form.notes.trim() || undefined, truckEntrance: toTruckEntrancePayload(truckForm), }; try { await receiveMutation.mutateAsync(payload); toast({ title: 'Inventory received' }); onReceived?.(); onClose(); } catch (error) { toast({ variant: 'destructive', title: 'Receive failed', description: extractErrorMessage(error) }); } }; return ( {bookingId ? ( ) : ( )} setForm((f) => ({ ...f, ...next }))} /> } color="blue" variant="light"> Customer, TIN, phone, quantity and weight are pulled from the selected booking when the GRN is generated. setForm((f) => ({ ...f, volume: value === '' ? '' : Number(value) }))} />