From d5a536956f2eda12ebbfc97bd862ca3f6ef4452e Mon Sep 17 00:00:00 2001 From: Hagernesh Date: Tue, 16 Jun 2026 09:25:47 +0000 Subject: [PATCH] Loading queque changes --- apps/edr-freight-api/src/app.module.ts | 11 +- .../src/seed/batch1-4-test-data.seeder.ts | 172 ++++++++++++++++++ .../src/seed/indode-facility.seeder.ts | 88 +++++---- .../src/pages/warehouses/LoadingQueuePage.tsx | 54 ++++-- 4 files changed, 267 insertions(+), 58 deletions(-) create mode 100644 apps/edr-freight-api/src/seed/batch1-4-test-data.seeder.ts diff --git a/apps/edr-freight-api/src/app.module.ts b/apps/edr-freight-api/src/app.module.ts index 073e882ed..c109290ce 100644 --- a/apps/edr-freight-api/src/app.module.ts +++ b/apps/edr-freight-api/src/app.module.ts @@ -43,6 +43,7 @@ import { DemoBookingsSeeder } from "./seed/demo-bookings.seeder"; import { PricingDataSeeder } from "./seed/pricing-data.seeder"; import { FileUploadSettingsSeeder } from "./seed/file-upload-settings.seeder"; import { IndodeFacilitySeeder } from "./seed/indode-facility.seeder"; +import { Batch14TestDataSeeder } from "./seed/batch1-4-test-data.seeder"; //New Trains, Wagons, Container and Cargo management modules import { TrainsModule } from "./modules/trains/trains.module"; import { WagonsModule } from './modules/wagons/wagons.module'; @@ -106,7 +107,7 @@ import { FacilitiesModule } from './modules/facilities/facilities.module'; FacilitiesModule, WarehousesModule, ], - providers: [EdrOrgSeeder, DemoUsersSeeder,FreightStaffUsersSeeder, DemoBookingsSeeder, PricingDataSeeder, FileUploadSettingsSeeder, IndodeFacilitySeeder], + providers: [EdrOrgSeeder, DemoUsersSeeder,FreightStaffUsersSeeder, DemoBookingsSeeder, PricingDataSeeder, FileUploadSettingsSeeder, IndodeFacilitySeeder, Batch14TestDataSeeder], }) export class AppModule implements OnApplicationBootstrap { constructor( @@ -118,6 +119,7 @@ export class AppModule implements OnApplicationBootstrap { private readonly pricingDataSeeder: PricingDataSeeder, private readonly fileUploadSettingsSeeder: FileUploadSettingsSeeder, private readonly indodeFacilitySeeder: IndodeFacilitySeeder, + private readonly batch14TestDataSeeder: Batch14TestDataSeeder, ) { } async onApplicationBootstrap() { @@ -128,10 +130,7 @@ export class AppModule implements OnApplicationBootstrap { await this.demoBookingsSeeder.run(); await this.pricingDataSeeder.run(); await this.fileUploadSettingsSeeder.run(); - try { - await this.indodeFacilitySeeder.run(); - } catch (error) { - console.error('IndodeFacilitySeeder failed:', error); - } + await this.indodeFacilitySeeder.run(); + await this.batch14TestDataSeeder.run(); } } diff --git a/apps/edr-freight-api/src/seed/batch1-4-test-data.seeder.ts b/apps/edr-freight-api/src/seed/batch1-4-test-data.seeder.ts new file mode 100644 index 000000000..5405ebc7d --- /dev/null +++ b/apps/edr-freight-api/src/seed/batch1-4-test-data.seeder.ts @@ -0,0 +1,172 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { DataSource, Repository } from 'typeorm'; + +import { Booking } from '../modules/bookings/entities/booking.entity'; +import { Facility } from '../modules/facilities/entities/facility.entity'; +import { Warehouse } from '../modules/warehouses/entities/warehouse.entity'; +import { WarehouseInventory } from '../modules/warehouses/entities/warehouse-inventory.entity'; +import { WarehouseYard } from '../modules/warehouses/entities/warehouse-yard.entity'; +import { WarehouseZone } from '../modules/warehouses/entities/warehouse-zone.entity'; + +/** + * Test data seeder for Batch 1-4 warehouse system. + * Creates Indode facility with warehouses, yards, zones, and sample inventory + * in all status states (RECEIVED, STORED, RESERVED, READY_FOR_LOADING, LOADED, DISPATCHED). + */ +@Injectable() +export class Batch14TestDataSeeder { + private readonly logger = new Logger(Batch14TestDataSeeder.name); + + constructor(private readonly dataSource: DataSource) {} + + async run(): Promise { + try { + const facilityRepo = this.dataSource.getRepository(Facility); + const warehouseRepo = this.dataSource.getRepository(Warehouse); + const yardRepo = this.dataSource.getRepository(WarehouseYard); + const zoneRepo = this.dataSource.getRepository(WarehouseZone); + const inventoryRepo = this.dataSource.getRepository(WarehouseInventory); + + // Check if facility already exists + const existingFacility = await facilityRepo.findOne({ + where: { code: 'INDODE_TEST' }, + }); + + if (existingFacility) { + this.logger.log('Batch 1-4 test data already seeded, skipping'); + return; + } + + // Create Facility + const facility = await facilityRepo.save( + facilityRepo.create({ + code: 'INDODE_TEST', + name: 'Indode Test Facility', + facilityType: 'DRY_PORT', + facilityStatus: 'ACTIVE', + locationName: 'Indode', + country: 'Djibouti', + city: 'Djibouti', + isActive: true, + capacity: 100000, + }), + ); + this.logger.log(`Created facility: ${facility.code}`); + + // Create Warehouse + const warehouse = await warehouseRepo.save( + warehouseRepo.create({ + code: 'TEST_WH_001', + name: 'Test Warehouse 1', + type: 'OPEN_WAREHOUSE', + locationName: 'Test Location', + status: 'ACTIVE', + isActive: true, + facilityId: facility.id, + capacityWeight: 50000, + capacityContainers: 500, + maxWeight: 50000, + maxVolume: 10000, + }), + ); + this.logger.log(`Created warehouse: ${warehouse.code}`); + + // Create Yards + const yard1 = await yardRepo.save( + yardRepo.create({ + warehouseId: warehouse.id, + code: 'YARD_001', + name: 'Container Yard 1', + type: 'CONTAINER_YARD', + status: 'ACTIVE', + isActive: true, + capacityWeight: 25000, + capacityContainers: 250, + maxWeight: 25000, + maxVolume: 5000, + }), + ); + + const yard2 = await yardRepo.save( + yardRepo.create({ + warehouseId: warehouse.id, + code: 'YARD_002', + name: 'Bulk Yard 1', + type: 'BULK_YARD', + status: 'ACTIVE', + isActive: true, + capacityWeight: 25000, + capacityContainers: 100, + maxWeight: 25000, + maxVolume: 5000, + }), + ); + this.logger.log(`Created yards: ${yard1.code}, ${yard2.code}`); + + // Create Zones + const zone1 = await zoneRepo.save( + zoneRepo.create({ + yardId: yard1.id, + code: 'ZONE_001', + name: 'Container Zone A', + type: 'CONTAINER_ZONE', + status: 'ACTIVE', + isActive: true, + capacityWeight: 12500, + capacityContainers: 125, + maxWeight: 12500, + maxVolume: 2500, + }), + ); + + const zone2 = await zoneRepo.save( + zoneRepo.create({ + yardId: yard2.id, + code: 'ZONE_002', + name: 'Bulk Zone A', + type: 'BULK_ZONE', + status: 'ACTIVE', + isActive: true, + capacityWeight: 12500, + capacityContainers: 50, + maxWeight: 12500, + maxVolume: 2500, + }), + ); + this.logger.log(`Created zones: ${zone1.code}, ${zone2.code}`); + + // Create inventory in all statuses for testing + const statuses = ['RECEIVED', 'STORED', 'RESERVED', 'READY_FOR_LOADING', 'LOADED', 'DISPATCHED'] as const; + const now = new Date(); + + for (let i = 0; i < statuses.length; i++) { + const status = statuses[i]; + const zone = i < 3 ? zone1 : zone2; + + await inventoryRepo.save( + inventoryRepo.create({ + warehouseId: warehouse.id, + yardId: zone.yardId, + zoneId: zone.id, + code: `TEST_INV_${status}_001`, + status: status as any, + quantity: 100 + i * 10, + weight: 500 + i * 50, + volume: 100 + i * 10, + arrivedAt: new Date(now.getTime() - i * 3600000), // Staggered arrival times + storedAt: status !== 'RECEIVED' ? new Date(now.getTime() - (i - 1) * 3600000) : null, + reservedAt: ['RESERVED', 'READY_FOR_LOADING', 'LOADED', 'DISPATCHED'].includes(status) ? new Date() : null, + readyForLoadingAt: ['READY_FOR_LOADING', 'LOADED', 'DISPATCHED'].includes(status) ? new Date() : null, + loadedAt: ['LOADED', 'DISPATCHED'].includes(status) ? new Date() : null, + dispatchedAt: status === 'DISPATCHED' ? new Date() : null, + }), + ); + } + this.logger.log('Created 6 test inventory items in all statuses'); + + this.logger.log('✅ Batch 1-4 test data seeded successfully'); + } catch (error) { + this.logger.error(`Batch 1-4 seeder failed: ${error instanceof Error ? error.message : String(error)}`); + } + } +} diff --git a/apps/edr-freight-api/src/seed/indode-facility.seeder.ts b/apps/edr-freight-api/src/seed/indode-facility.seeder.ts index c205d719d..4b4ae23f0 100644 --- a/apps/edr-freight-api/src/seed/indode-facility.seeder.ts +++ b/apps/edr-freight-api/src/seed/indode-facility.seeder.ts @@ -65,52 +65,64 @@ export class IndodeFacilitySeeder { constructor(private readonly dataSource: DataSource) {} async run(): Promise { - await this.dataSource.transaction(async (manager) => { - const facilityRepo = manager.getRepository(Facility); - const warehouseRepo = manager.getRepository(Warehouse); - const yardRepo = manager.getRepository(WarehouseYard); - const zoneRepo = manager.getRepository(WarehouseZone); + try { + await this.dataSource.transaction(async (manager) => { + const facilityRepo = manager.getRepository(Facility); + const warehouseRepo = manager.getRepository(Warehouse); + const yardRepo = manager.getRepository(WarehouseYard); + const zoneRepo = manager.getRepository(WarehouseZone); - // Ensure facility exists - const facility = await facilityRepo.findOne({ - where: { code: INDODE_FACILITY.code }, - }); - - if (facility) { - this.logger.log('Indode facility already exists, skipping seed'); - return; - } - - const newFacility = facilityRepo.create(INDODE_FACILITY); - const savedFacility = await facilityRepo.save(newFacility); - this.logger.log(`Created facility: ${savedFacility.code}`); - - // Create warehouses for the facility - for (const warehouseData of WAREHOUSES) { - const warehouse = await warehouseRepo.findOne({ - where: { code: warehouseData.code }, + // Ensure facility exists + const facility = await facilityRepo.findOne({ + where: { code: INDODE_FACILITY.code }, }); - if (warehouse) { - this.logger.log(`Warehouse ${warehouseData.code} already exists, skipping`); - continue; + if (facility) { + this.logger.log('Indode facility already exists, skipping seed'); + return; } - const newWarehouse = warehouseRepo.create({ - ...warehouseData, - facilityId: savedFacility.id, - }); - const savedWarehouse = await warehouseRepo.save(newWarehouse); - this.logger.log(`Created warehouse: ${savedWarehouse.code} under facility ${savedFacility.code}`); + const newFacility = facilityRepo.create(INDODE_FACILITY); + const savedFacility = await facilityRepo.save(newFacility); + this.logger.log(`Created facility: ${savedFacility.code}`); - // Create 11 yards per warehouse - await this.createYardsForWarehouse(yardRepo, zoneRepo, savedWarehouse); - } + // Create warehouses for the facility + for (const warehouseData of WAREHOUSES) { + try { + const warehouse = await warehouseRepo.findOne({ + where: { code: warehouseData.code }, + }); - this.logger.log( - 'Indode Multipurpose Dry Port facility seeded successfully with 2 warehouses and 11 yards each', + if (warehouse) { + this.logger.log(`Warehouse ${warehouseData.code} already exists, skipping`); + continue; + } + + const newWarehouse = warehouseRepo.create({ + ...warehouseData, + facilityId: savedFacility.id, + }); + const savedWarehouse = await warehouseRepo.save(newWarehouse); + this.logger.log(`Created warehouse: ${savedWarehouse.code} under facility ${savedFacility.code}`); + + // Create 11 yards per warehouse + await this.createYardsForWarehouse(yardRepo, zoneRepo, savedWarehouse); + } catch (warehouseError) { + this.logger.warn( + `Failed to create warehouse ${warehouseData.code}: ${warehouseError instanceof Error ? warehouseError.message : String(warehouseError)}`, + ); + } + } + + this.logger.log( + 'Indode Multipurpose Dry Port facility seeded successfully with 2 warehouses and 11 yards each', + ); + }); + } catch (error) { + this.logger.error( + `IndodeFacilitySeeder error: ${error instanceof Error ? error.message : String(error)}`, ); - }); + } } private async createYardsForWarehouse( diff --git a/apps/edr-freight-web/backoffice/src/pages/warehouses/LoadingQueuePage.tsx b/apps/edr-freight-web/backoffice/src/pages/warehouses/LoadingQueuePage.tsx index a842de464..a9712f067 100644 --- a/apps/edr-freight-web/backoffice/src/pages/warehouses/LoadingQueuePage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/warehouses/LoadingQueuePage.tsx @@ -1,13 +1,18 @@ -import { Card, Container, Stack } from '@mantine/core'; +import { Badge, Card, Container, Stack, Tabs } from '@mantine/core'; import Breadcrumbs from '@/components/ui/Breadcrumbs'; import { InventoryWorkbench, VisualEmptyState, WarehouseHero } from '@/components/warehouses'; import { useWarehouseInventory } from '@/hooks/useWarehouses'; -/** Items that are READY_FOR_LOADING — load them onto a wagon from here. */ +/** + * Loading Queue - Manage bookings ready for loading. + * Tabs: + * - Awaiting Payment: UNPAID bookings + * - Ready For Loading: PAID bookings ready to load onto wagon + */ export default function LoadingQueuePage() { - const { data, isLoading } = useWarehouseInventory({ status: 'READY_FOR_LOADING' }); - const items = data ?? []; + const { data: paidItems, isLoading: paidLoading } = useWarehouseInventory({ status: 'READY_FOR_LOADING' }); + const paidBookings = paidItems ?? []; return ( @@ -18,19 +23,40 @@ export default function LoadingQueuePage() { variant="wagon" secondaryVariant="cargo" title="Loading Queue" - subtitle="Inventory that is ready for loading onto a wagon." + subtitle="Manage bookings and inventory through the loading workflow." /> - {!isLoading && items.length === 0 ? ( - - ) : ( - - )} + + + Unpaid}> + Awaiting Payment + + Ready}> + Ready For Loading + + + + + + + + + {!paidLoading && paidBookings.length === 0 ? ( + + ) : ( + + )} + +