import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { warehouseService } from '@/services/warehouse.service'; import type { InspectionReportPayload, SaveAllocationRulePayload, SaveFeeRulePayload, WarehouseInvoiceFilter, PayInvoicePayload, InventoryFilter, InventoryInquiryFilter, LoadInventoryPayload, MoveInventoryPayload, ReceiveInventoryPayload, ReleaseOrderPayload, DeliverInventoryPayload, BulkReceivePayload, BulkInspectPayload, ReserveInventoryPayload, SaveWarehousePayload, SaveYardPayload, SaveZonePayload, WarehouseFilter, } from '@/types/warehouse'; export const warehouseKeys = { all: ['warehouses'] as const, list: (filter?: WarehouseFilter) => ['warehouses', 'list', filter ?? {}] as const, facilities: () => ['warehouses', 'facilities'] as const, detail: (id: string) => ['warehouses', 'detail', id] as const, yards: (warehouseId: string) => ['warehouses', warehouseId, 'yards'] as const, allYards: () => ['warehouse-yards', 'all'] as const, zones: (yardId: string) => ['warehouse-yards', yardId, 'zones'] as const, allZones: () => ['warehouse-zones', 'all'] as const, inventory: (filter?: InventoryFilter) => ['warehouse-inventory', 'list', filter ?? {}] as const, dashboardSummary: (filter?: InventoryFilter) => ['warehouse-dashboard', 'summary', filter ?? {}] as const, inquiry: (filter: InventoryInquiryFilter) => ['warehouse-inventory', 'inquiry', filter] as const, }; // ── Warehouses ───────────────────────────────────────────────────────────── export function useWarehouses(filter?: WarehouseFilter) { return useQuery({ queryKey: warehouseKeys.list(filter), queryFn: () => warehouseService.list(filter).then((r) => r.data), }); } export function useWarehouse(id?: string) { return useQuery({ queryKey: warehouseKeys.detail(id ?? ''), queryFn: () => warehouseService.getById(id as string).then((r) => r.data), enabled: Boolean(id), }); } export function useWarehouseFacilities() { return useQuery({ queryKey: warehouseKeys.facilities(), queryFn: () => warehouseService.listFacilities().then((r) => r.data.items), }); } export function useCreateWarehouse() { const qc = useQueryClient(); return useMutation({ mutationFn: (payload: SaveWarehousePayload) => warehouseService.create(payload), onSuccess: () => qc.invalidateQueries({ queryKey: warehouseKeys.all }), }); } export function useUpdateWarehouse() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ id, payload }: { id: string; payload: Partial }) => warehouseService.update(id, payload), onSuccess: (_, { id }) => { qc.invalidateQueries({ queryKey: warehouseKeys.all }); qc.invalidateQueries({ queryKey: warehouseKeys.detail(id) }); }, }); } // ── Yards ──────────────────────────────────────────────────────────────── export function useWarehouseYards(warehouseId?: string) { return useQuery({ queryKey: warehouseKeys.yards(warehouseId ?? ''), queryFn: () => warehouseService.listYards(warehouseId as string).then((r) => r.data), enabled: Boolean(warehouseId), }); } export function useAllWarehouseYards() { return useQuery({ queryKey: warehouseKeys.allYards(), queryFn: () => warehouseService.listAllYards().then((r) => r.data), }); } export function useCreateYard() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ warehouseId, payload }: { warehouseId: string; payload: SaveYardPayload }) => warehouseService.createYard(warehouseId, payload), onSuccess: (_, { warehouseId }) => { qc.invalidateQueries({ queryKey: warehouseKeys.yards(warehouseId) }); qc.invalidateQueries({ queryKey: warehouseKeys.detail(warehouseId) }); }, }); } export function useUpdateYard() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ id, payload }: { id: string; payload: Partial }) => warehouseService.updateYard(id, payload), onSuccess: () => qc.invalidateQueries({ queryKey: warehouseKeys.all }), }); } // ── Zones ────────────────────────────────────────────────────────────────── export function useWarehouseZones(yardId?: string) { return useQuery({ queryKey: warehouseKeys.zones(yardId ?? ''), queryFn: () => warehouseService.listZones(yardId as string).then((r) => r.data), enabled: Boolean(yardId), }); } export function useAllWarehouseZones() { return useQuery({ queryKey: warehouseKeys.allZones(), queryFn: () => warehouseService.listAllZones().then((r) => r.data), }); } /** Live per-zone occupancy for the heatmap (optionally scoped to one yard). */ export function useZoneOccupancy(yardId?: string) { return useQuery({ queryKey: ['warehouse-zones', 'occupancy', yardId ?? 'all'], queryFn: () => warehouseService.zoneOccupancy(yardId).then((r) => r.data), }); } /** At-a-glance warehouse ops counters for the KPI strip. */ export function useWarehouseOpsStats() { return useQuery({ queryKey: ['warehouse-inventory', 'ops-stats'], queryFn: () => warehouseService.opsStats().then((r) => r.data), }); } /** Server-side received-vs-dispatched throughput time series. */ export function useWarehouseThroughput(granularity: 'week' | 'month' | 'year') { return useQuery({ queryKey: ['warehouse-inventory', 'throughput', granularity], queryFn: () => warehouseService.throughput(granularity).then((r) => r.data), }); } /** Live per-item fee accrual (storage/demurrage) with alerts. */ export function useAccrualDashboard(billingCurrency?: 'ETB' | 'USD') { return useQuery({ queryKey: ['warehouse-fees', 'accrual-dashboard', billingCurrency ?? 'USD'], queryFn: () => warehouseService.accrualDashboard(billingCurrency).then((r) => r.data), }); } export function useCreateZone() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ yardId, payload }: { yardId: string; payload: SaveZonePayload }) => warehouseService.createZone(yardId, payload), onSuccess: (_, { yardId }) => qc.invalidateQueries({ queryKey: warehouseKeys.zones(yardId) }), }); } export function useUpdateZone() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ id, payload }: { id: string; payload: Partial }) => warehouseService.updateZone(id, payload), onSuccess: () => qc.invalidateQueries({ queryKey: ['warehouse-yards'] }), }); } // ── Inventory ────────────────────────────────────────────────────────────── export function useWarehouseInventory(filter?: InventoryFilter) { return useQuery({ queryKey: warehouseKeys.inventory(filter), queryFn: () => warehouseService.listInventory(filter).then((r) => r.data), }); } export function useWarehouseDashboardSummary(filter?: InventoryFilter) { return useQuery({ queryKey: warehouseKeys.dashboardSummary(filter), queryFn: () => warehouseService.getDashboardSummary(filter).then((r) => r.data), }); } export function useReceiveInventory() { const qc = useQueryClient(); return useMutation({ mutationFn: (payload: ReceiveInventoryPayload) => warehouseService.receiveInventory(payload), onSuccess: () => { qc.invalidateQueries({ queryKey: ['warehouse-inventory'] }); qc.invalidateQueries({ queryKey: warehouseKeys.all }); qc.invalidateQueries({ queryKey: ['warehouse-dashboard'] }); }, }); } function useInventoryMutation(fn: (args: TArgs) => Promise) { const qc = useQueryClient(); return useMutation({ mutationFn: fn, onSuccess: () => { qc.invalidateQueries({ queryKey: ['warehouse-inventory'] }); qc.invalidateQueries({ queryKey: ['warehouse-loadings'] }); qc.invalidateQueries({ queryKey: warehouseKeys.all }); }, }); } export const useStoreInventory = () => useInventoryMutation((id: string) => warehouseService.store(id)); export const useReserveInventory = () => useInventoryMutation((payload: ReserveInventoryPayload) => warehouseService.reserve(payload)); export const useMarkReadyForLoading = () => useInventoryMutation((id: string) => warehouseService.markReadyForLoading(id)); export const useLoadInventory = () => useInventoryMutation((args: { id: string; payload: LoadInventoryPayload }) => warehouseService.load(args.id, args.payload), ); export const useDispatchInventory = () => useInventoryMutation((id: string) => warehouseService.dispatch(id)); export const useMoveInventory = () => useInventoryMutation((args: { id: string; payload: MoveInventoryPayload }) => warehouseService.move(args.id, args.payload), ); // ── Import branch (READY_FOR_PICKUP → DELIVERED) ─────────────────────────── export const useMarkReadyForPickup = () => useInventoryMutation((id: string) => warehouseService.markReadyForPickup(id)); export const useReleaseInventory = () => useInventoryMutation((args: { id: string; payload: ReleaseOrderPayload }) => warehouseService.release(args.id, args.payload), ); export const useDeliverInventory = () => useInventoryMutation((args: { id: string; payload: DeliverInventoryPayload }) => warehouseService.deliver(args.id, args.payload), ); // ── Receive (Import/Export bulk) ─────────────────────────────────────────── /** * All not-yet-received PAID bookings, classified IMPORT/EXPORT by route, in one call. * Both Receive tabs share this single query (same key) — only one HTTP request fires — * then filter client-side by direction. */ export function useEligibleBookings(enabled = true) { return useQuery({ queryKey: ['warehouse-inventory', 'eligible-bookings'], queryFn: () => warehouseService.eligibleBookings().then((r) => r.data), enabled, }); } export const useBulkReceive = () => useInventoryMutation((payload: BulkReceivePayload) => warehouseService.receiveBulk(payload)); export const useBulkMarkInspected = () => useInventoryMutation((payload: BulkInspectPayload) => warehouseService.bulkMarkInspected(payload)); export function useReadyToLoadExport(enabled = true) { return useQuery({ queryKey: ['warehouse-inventory', 'ready-to-load-export'], queryFn: () => warehouseService.readyToLoadExport().then((r) => r.data), enabled, }); } export function useLoadedExport(enabled = true) { return useQuery({ queryKey: ['warehouse-inventory', 'loaded-export'], queryFn: () => warehouseService.loadedExport().then((r) => r.data), enabled, }); } export const useBulkDispatchExport = () => useInventoryMutation((inventoryIds: string[]) => warehouseService.bulkDispatchExport(inventoryIds)); /** Arrived IMPORT trains (route-derived). Read-only. */ export function useImportArriveQueue(enabled = true) { return useQuery({ queryKey: ['warehouse-inventory', 'import-arrive-queue'], queryFn: () => warehouseService.importArriveQueue().then((r) => r.data), enabled, }); } /** Assigned bookings/items for an arrived import train. Read-only. */ export function useImportTrainItems(scheduleId?: string) { return useQuery({ queryKey: ['warehouse-inventory', 'import-train-items', scheduleId], queryFn: () => warehouseService.importTrainItems(scheduleId as string).then((r) => r.data), enabled: Boolean(scheduleId), }); } /** Unload all eligible assigned bookings of an ARRIVED import train (→ UNLOADED). */ export const useAutoUnloadArrivedBookings = () => useInventoryMutation((payload: { scheduleId: string; warehouseId?: string; assignments?: { bookingId: string; warehouseId: string; yardId: string; zoneId: string }[]; }) => warehouseService.autoUnloadArrivedBookings(payload), ); /** Arrived EXPORT trains at Djibouti-side ports. Read-only. */ export function useExportDjiboutiArrivalQueue(enabled = true) { return useQuery({ queryKey: ['warehouse-inventory', 'export-djibouti-arrival-queue'], queryFn: () => warehouseService.exportDjiboutiArrivalQueue().then((r) => r.data), enabled, }); } /** Assigned export bookings/items for a Djibouti-side arrived export train. Read-only. */ export function useExportDjiboutiTrainItems(scheduleId?: string) { return useQuery({ queryKey: ['warehouse-inventory', 'export-djibouti-train-items', scheduleId], queryFn: () => warehouseService.exportDjiboutiTrainItems(scheduleId as string).then((r) => r.data), enabled: Boolean(scheduleId), }); } /** Unload eligible export items assigned to an arrived Djibouti-side train. */ export const useAutoUnloadExportAtDjibouti = () => { const qc = useQueryClient(); return useMutation({ mutationFn: (scheduleId: string) => warehouseService.autoUnloadExportAtDjibouti(scheduleId), onSuccess: () => { qc.invalidateQueries({ queryKey: ['warehouse-inventory'] }); qc.invalidateQueries({ queryKey: ['warehouse-loadings'] }); qc.invalidateQueries({ queryKey: warehouseKeys.all }); qc.invalidateQueries({ queryKey: ['interchange-documents'] }); }, }); }; /** IMPORT inventory in the Unloaded Queue (UNLOADED / destination inspection). Read-only. */ export function useImportUnloadedQueue(enabled = true) { return useQuery({ queryKey: ['warehouse-inventory', 'import-unloaded-queue'], queryFn: () => warehouseService.importUnloadedQueue().then((r) => r.data), enabled, }); } /** IMPORT inventory that is PICKUP_READY (READY_FOR_PICKUP) awaiting pickup/dispatch. Read-only. */ export function useImportPickupReadyQueue(enabled = true) { return useQuery({ queryKey: ['warehouse-inventory', 'import-pickup-ready-queue'], queryFn: () => warehouseService.importPickupReadyQueue().then((r) => r.data), enabled, }); } // ── Loading (Batch 3) ──────────────────────────────────────────────────────── export function useLoadableWagons(enabled = true) { return useQuery({ queryKey: ['warehouse', 'loadable-wagons'], queryFn: () => warehouseService.loadableWagons().then((r) => r.data), enabled, }); } export function useWarehouseLoadings(params?: { bookingId?: string; wagonId?: string }) { return useQuery({ queryKey: ['warehouse-loadings', params ?? {}], queryFn: () => warehouseService.loadings(params).then((r) => r.data), }); } export function useBookingSchedule(bookingId?: string) { return useQuery({ queryKey: ['warehouse', 'booking-schedule', bookingId ?? ''], queryFn: () => warehouseService.bookingSchedule(bookingId as string).then((r) => r.data), enabled: Boolean(bookingId), }); } export function useInventoryMovements(id?: string) { return useQuery({ queryKey: ['warehouse-inventory', id, 'movements'], queryFn: () => warehouseService.movements(id as string).then((r) => r.data), enabled: Boolean(id), }); } export function useInventoryActivity(id?: string) { return useQuery({ queryKey: ['warehouse-inventory', id, 'activity'], queryFn: () => warehouseService.activity(id as string).then((r) => r.data), enabled: Boolean(id), }); } export function useWarehouseDashboard() { return useQuery({ queryKey: ['warehouses', 'dashboard'], queryFn: () => warehouseService.dashboard().then((r) => r.data), }); } export function useInventoryInquiry(filter: InventoryInquiryFilter, enabled = true) { return useQuery({ queryKey: warehouseKeys.inquiry(filter), queryFn: () => warehouseService.inquiry(filter).then((r) => r.data), enabled, }); } // ── Batch 4.5: Arrival / Unload / Inspection ──────────────────────────────── export function useArrivalQueue() { return useQuery({ queryKey: ['warehouse-inventory', 'arrival-queue'], queryFn: () => warehouseService.arrivalQueue().then((r) => r.data), }); } function useArrivalInvalidation() { const qc = useQueryClient(); return () => { qc.invalidateQueries({ queryKey: ['warehouse-inventory'] }); qc.invalidateQueries({ queryKey: warehouseKeys.all }); }; } export function useAutoUnloadArrived() { const onSuccess = useArrivalInvalidation(); return useMutation({ mutationFn: () => warehouseService.autoUnloadArrived(), onSuccess }); } export function useAutoLoadReady() { const onSuccess = useArrivalInvalidation(); return useMutation({ mutationFn: () => warehouseService.autoLoadReady(), onSuccess }); } export function useUnloadBooking() { const onSuccess = useArrivalInvalidation(); return useMutation({ mutationFn: (args: { bookingId: string; payload?: Record }) => warehouseService.unloadBooking(args.bookingId, args.payload), onSuccess, }); } export function useInspectionReports(inventoryId?: string) { return useQuery({ queryKey: ['warehouse-inventory', inventoryId, 'inspection-reports'], queryFn: () => warehouseService.listInspectionReports(inventoryId as string).then((r) => r.data), enabled: Boolean(inventoryId), }); } export function useCreateInspectionReport() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ inventoryId, payload }: { inventoryId: string; payload: InspectionReportPayload }) => warehouseService.createInspectionReport(inventoryId, payload).then((r) => r.data), onSuccess: (_, { inventoryId }) => { qc.invalidateQueries({ queryKey: ['warehouse-inventory', inventoryId, 'inspection-reports'] }); qc.invalidateQueries({ queryKey: ['warehouse-inventory'] }); }, }); } export function useUploadInspectionAttachments() { return useMutation({ mutationFn: ({ reportId, files }: { reportId: string; files: File[] }) => warehouseService.uploadInspectionAttachments(reportId, files), }); } // ── Batch 5: Allocation + Fee rules / preview ─────────────────────────────── export function useAllocationRules() { return useQuery({ queryKey: ['warehouse-allocation-rules'], queryFn: () => warehouseService.listAllocationRules().then((r) => r.data), }); } export function useFeeRules() { return useQuery({ queryKey: ['warehouse-fee-rules'], queryFn: () => warehouseService.listFeeRules().then((r) => r.data), }); } function useRuleMutation(fn: (args: TArgs) => Promise, keys: string[]) { const qc = useQueryClient(); return useMutation({ mutationFn: fn, onSuccess: () => keys.forEach((k) => qc.invalidateQueries({ queryKey: [k] })), }); } export const useCreateAllocationRule = () => useRuleMutation( (payload: SaveAllocationRulePayload) => warehouseService.createAllocationRule(payload), ['warehouse-allocation-rules'], ); export const useUpdateAllocationRule = () => useRuleMutation( (args: { id: string; payload: Partial }) => warehouseService.updateAllocationRule(args.id, args.payload), ['warehouse-allocation-rules'], ); export const useDeleteAllocationRule = () => useRuleMutation((id: string) => warehouseService.deleteAllocationRule(id), ['warehouse-allocation-rules']); export const useCreateFeeRule = () => useRuleMutation((payload: SaveFeeRulePayload) => warehouseService.createFeeRule(payload), ['warehouse-fee-rules']); export const useUpdateFeeRule = () => useRuleMutation( (args: { id: string; payload: Partial }) => warehouseService.updateFeeRule(args.id, args.payload), ['warehouse-fee-rules'], ); export const useDeleteFeeRule = () => useRuleMutation((id: string) => warehouseService.deleteFeeRule(id), ['warehouse-fee-rules']); export function useFeePreview(inventoryId?: string, billingCurrency: 'ETB' | 'USD' = 'USD') { return useQuery({ queryKey: ['warehouse-inventory', inventoryId, 'fee-preview', billingCurrency], queryFn: () => warehouseService.feePreview(inventoryId as string, billingCurrency).then((r) => r.data), enabled: Boolean(inventoryId), }); } // ── Batch 6: Warehouse fee invoices ───────────────────────────────────────── export function useWarehouseInvoices(filter?: WarehouseInvoiceFilter) { return useQuery({ queryKey: ['warehouse-fee-invoices', filter ?? {}], queryFn: () => warehouseService.listInvoices(filter).then((r) => r.data), }); } export function useWarehouseInvoice(id?: string) { return useQuery({ queryKey: ['warehouse-fee-invoices', 'detail', id], queryFn: () => warehouseService.getInvoice(id as string).then((r) => r.data), enabled: Boolean(id), }); } export function useInvoicesForInventory(inventoryId?: string) { return useQuery({ queryKey: ['warehouse-inventory', inventoryId, 'fee-invoices'], queryFn: () => warehouseService.invoicesForInventory(inventoryId as string).then((r) => r.data), enabled: Boolean(inventoryId), }); } function useInvoiceInvalidation() { const qc = useQueryClient(); return () => { qc.invalidateQueries({ queryKey: ['warehouse-fee-invoices'] }); qc.invalidateQueries({ queryKey: ['warehouse-inventory'] }); }; } export function useGenerateInvoice() { const onSuccess = useInvoiceInvalidation(); return useMutation({ mutationFn: ({ inventoryId, confirmZero, billingCurrency, }: { inventoryId: string; confirmZero?: boolean; billingCurrency?: 'ETB' | 'USD'; }) => warehouseService.generateInvoice(inventoryId, confirmZero, billingCurrency).then((r) => r.data), onSuccess, }); } export function useCancelInvoice() { const onSuccess = useInvoiceInvalidation(); return useMutation({ mutationFn: (id: string) => warehouseService.cancelInvoice(id), onSuccess }); } export function usePayInvoice() { const onSuccess = useInvoiceInvalidation(); return useMutation({ mutationFn: ({ id, payload }: { id: string; payload: PayInvoicePayload }) => warehouseService.payInvoice(id, payload), onSuccess, }); } export function useGateClearance() { const onSuccess = useInvoiceInvalidation(); return useMutation({ mutationFn: (inventoryId: string) => warehouseService.gateClearance(inventoryId), onSuccess }); }