mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 15:18:11 +00:00
feat(warehouse): add warehouse management module (batch 1)
Backend (edr-freight-api): - Warehouse, WarehouseYard, WarehouseZone, WarehouseInventory entities - CRUD for warehouses/yards/zones with scoped code uniqueness - Inventory receive with location-hierarchy + capacity validation and transactional capacity-counter updates - inspect / ready-for-loading status transitions - inventory inquiry (booking/customer/container/cargo-type joins) - migration creating freight.warehouse* tables Frontend (freight backoffice): - types, service, react-query hooks, URL constants - reusable components: badges, filters, table/card views, inventory and inquiry tables, create/receive modals - pages: Warehouse list, detail (overview/yards/zones/inventory tabs), inventory, inventory inquiry - Warehouse Information card + Receive At Warehouse on booking detail - routes + sidebar nav; app-wide ErrorBoundary Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
162
apps/edr-freight-web/backoffice/src/hooks/useWarehouses.ts
Normal file
162
apps/edr-freight-web/backoffice/src/hooks/useWarehouses.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { warehouseService } from '@/services/warehouse.service';
|
||||
import type {
|
||||
InventoryFilter,
|
||||
InventoryInquiryFilter,
|
||||
ReceiveInventoryPayload,
|
||||
SaveWarehousePayload,
|
||||
SaveYardPayload,
|
||||
SaveZonePayload,
|
||||
WarehouseFilter,
|
||||
} from '@/types/warehouse';
|
||||
|
||||
export const warehouseKeys = {
|
||||
all: ['warehouses'] as const,
|
||||
list: (filter?: WarehouseFilter) => ['warehouses', 'list', filter ?? {}] as const,
|
||||
detail: (id: string) => ['warehouses', 'detail', id] as const,
|
||||
yards: (warehouseId: string) => ['warehouses', warehouseId, 'yards'] as const,
|
||||
zones: (yardId: string) => ['warehouse-yards', yardId, 'zones'] as const,
|
||||
inventory: (filter?: InventoryFilter) => ['warehouse-inventory', 'list', 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 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<SaveWarehousePayload> }) =>
|
||||
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 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<SaveYardPayload> }) =>
|
||||
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 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<SaveZonePayload> }) =>
|
||||
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 useReceiveInventory() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (payload: ReceiveInventoryPayload) => warehouseService.receiveInventory(payload),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['warehouse-inventory'] });
|
||||
qc.invalidateQueries({ queryKey: warehouseKeys.all });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useInspectInventory() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => warehouseService.inspectInventory(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['warehouse-inventory'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useMarkReadyForLoading() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => warehouseService.markReadyForLoading(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['warehouse-inventory'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useInventoryInquiry(filter: InventoryInquiryFilter, enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: warehouseKeys.inquiry(filter),
|
||||
queryFn: () => warehouseService.inquiry(filter).then((r) => r.data),
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user