diff --git a/apps/edr-freight-web/backoffice/src/components/warehouses/ReceiveInventoryModal.tsx b/apps/edr-freight-web/backoffice/src/components/warehouses/ReceiveInventoryModal.tsx index 83c7fcbcd..3e3877236 100644 --- a/apps/edr-freight-web/backoffice/src/components/warehouses/ReceiveInventoryModal.tsx +++ b/apps/edr-freight-web/backoffice/src/components/warehouses/ReceiveInventoryModal.tsx @@ -646,9 +646,15 @@ function TruckEntranceFields({ function LocationSelects({ value, onChange, + allowedYardTypes, + allowedZoneTypes, }: { value: Location; onChange: (next: Location) => void; + /** When non-empty, only yards of these types are offered (matched to freight). */ + allowedYardTypes?: string[]; + /** When non-empty, only zones of these types are offered. */ + allowedZoneTypes?: string[]; }) { const warehousesQuery = useQuery( api.warehouses.list.queryOptions({ input: { filter: { status: 'ACTIVE' } } }), @@ -674,15 +680,17 @@ function LocationSelects({ () => (yardsQuery.data ?? []) .filter((y) => y.status === 'ACTIVE') + .filter((y) => !allowedYardTypes?.length || allowedYardTypes.includes((y as { type?: string }).type ?? '')) .map((y) => ({ value: y.id, label: `${y.name} (${y.code})` })), - [yardsQuery.data], + [yardsQuery.data, allowedYardTypes], ); const zoneOptions = useMemo( () => (zonesQuery.data ?? []) .filter((z) => z.status === 'ACTIVE') + .filter((z) => !allowedZoneTypes?.length || allowedZoneTypes.includes((z as { type?: string }).type ?? '')) .map((z) => ({ value: z.id, label: `${z.name} (${z.code})` })), - [zonesQuery.data], + [zonesQuery.data, allowedZoneTypes], ); return ( @@ -1760,6 +1768,29 @@ const importLocationTypesForFreight = (freightType: string | null | undefined) = const isImportContainerFreight = (freightType: string | null | undefined) => (freightType ?? '').toUpperCase() === 'CONTAINER'; +/** + * Yard/zone types valid for the freight being received — used to filter the receive + * location pickers so the yard list matches the cargo. Container freight → container + * yards only; bulk / break-bulk → bulk, general-cargo, hazardous, or cold-storage. + * Union across the given freight types; empty input → no restriction (show all). + */ +const yardZoneTypesForFreights = (freightTypes: Array) => { + const yardTypes = new Set(); + const zoneTypes = new Set(); + for (const freightType of freightTypes) { + const normalized = (freightType ?? '').toUpperCase(); + if (!normalized) continue; + if (normalized === 'CONTAINER') { + yardTypes.add('CONTAINER_YARD'); + zoneTypes.add('CONTAINER_ZONE'); + } else { + ['BULK_YARD', 'GENERAL_CARGO_YARD', 'HAZARDOUS_YARD', 'COLD_STORAGE_YARD'].forEach((t) => yardTypes.add(t)); + ['BULK_ZONE', 'GENERAL_CARGO_ZONE', 'HAZARDOUS_ZONE', 'COLD_STORAGE_ZONE'].forEach((t) => zoneTypes.add(t)); + } + } + return { yardTypes: [...yardTypes], zoneTypes: [...zoneTypes] }; +}; + const isImportUnloadPending = (item: ImportTrainItem) => !item.currentStatus || item.currentStatus === 'RECEIVED'; @@ -2888,6 +2919,24 @@ export function WarehouseFlowWorkbench({ ); const activeDirection = direction === 'BOTH' ? tab : direction; + // Match the yard/zone list to the freight being received (container → container + // yards, etc). Same query key as the export tab, so React Query dedupes it. + const { data: eligibleForLocation = [] } = useQuery( + api.warehouses.eligibleBookings.queryOptions({ + input: { direction: activeDirection }, + enabled: enabled && activeDirection === 'EXPORT', + }), + ); + const { yardTypes: allowedYardTypes, zoneTypes: allowedZoneTypes } = useMemo( + () => + yardZoneTypesForFreights( + eligibleForLocation + .filter((r) => r.direction === activeDirection) + .map((r) => r.freightType), + ), + [eligibleForLocation, activeDirection], + ); + useEffect(() => { if (enabled) setLocation({ warehouseId: '', yardId: '', zoneId: '' }); }, [enabled, direction]); @@ -2895,7 +2944,12 @@ export function WarehouseFlowWorkbench({ return ( {activeDirection === 'EXPORT' && ( - + )} {direction === 'BOTH' ? (