diff --git a/apps/edr-freight-api/src/modules/bookings/bookings.controller.ts b/apps/edr-freight-api/src/modules/bookings/bookings.controller.ts index 98f8d3627..5f4be50c1 100644 --- a/apps/edr-freight-api/src/modules/bookings/bookings.controller.ts +++ b/apps/edr-freight-api/src/modules/bookings/bookings.controller.ts @@ -13,7 +13,13 @@ import { UseInterceptors, } from "@nestjs/common"; import { AnyFilesInterceptor } from "@nestjs/platform-express"; -import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from "@nestjs/swagger"; +import { + ApiBearerAuth, + ApiBody, + ApiConsumes, + ApiOperation, + ApiTags, +} from "@nestjs/swagger"; import { BookingsService } from "./bookings.service"; import { CreateBookingDto } from "./dto/create-booking.dto"; @@ -23,8 +29,9 @@ import { UpdateStatusDto } from "./dto/update-status.dto"; @ApiTags("bookings") @Controller("bookings") +@ApiBearerAuth() export class BookingsController { - constructor(private readonly bookingsService: BookingsService) {} + constructor(private readonly bookingsService: BookingsService) { } // ── 1. Create booking (multipart/form-data) ────────────────────────── @Post() @@ -46,7 +53,16 @@ export class BookingsController { @Body() dto: CreateBookingDto, @UploadedFiles() files: Express.Multer.File[], ) { - console.log('[BookingsController] Files received:', files?.length, files?.map(f => ({ fieldname: f.fieldname, originalname: f.originalname, size: f.size, mimetype: f.mimetype }))); + console.log( + "[BookingsController] Files received:", + files?.length, + files?.map((f) => ({ + fieldname: f.fieldname, + originalname: f.originalname, + size: f.size, + mimetype: f.mimetype, + })), + ); return this.bookingsService.create(dto, files ?? []); } @@ -56,7 +72,8 @@ export class BookingsController { @ApiConsumes("multipart/form-data") @ApiOperation({ summary: "Update a draft booking", - description: "Only DRAFT bookings can be updated. New files are merged into existing documents.", + description: + "Only DRAFT bookings can be updated. New files are merged into existing documents.", }) @ApiBody({ type: UpdateBookingDto }) update( @@ -141,7 +158,8 @@ export class BookingsController { @Delete(":id/consolidation") @ApiOperation({ summary: "Remove consolidation pairing", - description: "Unpairs both bookings and returns them to PENDING_CONSOLIDATION status.", + description: + "Unpairs both bookings and returns them to PENDING_CONSOLIDATION status.", }) removeConsolidation(@Param("id", ParseUUIDPipe) id: string) { return this.bookingsService.removeConsolidation(id); @@ -151,7 +169,8 @@ export class BookingsController { @Get(":id/consolidation") @ApiOperation({ summary: "Get consolidation details", - description: "Returns partner booking details and split billing information.", + description: + "Returns partner booking details and split billing information.", }) getConsolidationDetails(@Param("id", ParseUUIDPipe) id: string) { return this.bookingsService.getConsolidationDetails(id); diff --git a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/schema.ts b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/schema.ts index 7cb5844c4..5d094f059 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/schema.ts +++ b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/schema.ts @@ -141,9 +141,6 @@ export const BOOKING_DOCS_SETTING = { ], }; -const requiredString = (message: string) => - z.string().trim().min(1, { message }); - const fileValueSchema = z.union([ z.custom(), z.array(z.custom()), @@ -152,10 +149,10 @@ const fileValueSchema = z.union([ export const bookingFormSchema = z .object({ - contractType: z.enum(["new", "renewal", ""]), + contractType: z.enum(["new", "renewal"], "Select a contract type."), previousContractRef: z.string(), draftContractId: z.string(), - serviceType: z.enum(["rail", "rail_forwarding", ""]), + serviceType: z.enum(["rail", "rail_forwarding"], "Select a service type."), firstMileEnabled: z.boolean(), pickUpAddress: z.string(), lastMileEnabled: z.boolean(), @@ -163,9 +160,9 @@ export const bookingFormSchema = z equipmentReturn: z.enum(["with_return", "without_return"]), originYard: z.string(), destinationYard: z.string(), - cargoType: z.enum(["container", "bulk", ""]), + cargoType: z.enum(["container", "bulk"], "Select a cargo type."), cargoWeight: z.string(), - freightType: z.enum(["bulk", "break_bulk", ""]), + freightType: z.enum(["bulk", "break_bulk"]), bulkCommodity: z.string(), bulkCommodityOther: z.string(), breakBulkType: z.string(), @@ -191,14 +188,6 @@ export const bookingFormSchema = z termsAccepted: z.boolean(), }) .superRefine((data, ctx) => { - if (!data.contractType) { - ctx.addIssue({ - code: "custom", - path: ["contractType"], - message: "Select a contract type.", - }); - } - if (data.contractType === "new" && !data.draftContractId.trim()) { ctx.addIssue({ code: "custom", @@ -215,14 +204,6 @@ export const bookingFormSchema = z }); } - if (!data.serviceType) { - ctx.addIssue({ - code: "custom", - path: ["serviceType"], - message: "Select a service type.", - }); - } - if (data.firstMileEnabled && !data.pickUpAddress.trim()) { ctx.addIssue({ code: "custom", @@ -267,14 +248,6 @@ export const bookingFormSchema = z }); } - if (!data.cargoType) { - ctx.addIssue({ - code: "custom", - path: ["cargoType"], - message: "Select a cargo type.", - }); - } - if (data.cargoType === "bulk") { if (!data.freightType) { ctx.addIssue({ @@ -385,11 +358,9 @@ export const bookingFormSchema = z export type BookingFormValues = z.infer; -export const initialBookingFormValues: BookingFormValues = { - contractType: "", +export const initialBookingFormValues: Partial = { previousContractRef: "", draftContractId: "", - serviceType: "", firstMileEnabled: false, pickUpAddress: "", lastMileEnabled: false, @@ -397,9 +368,7 @@ export const initialBookingFormValues: BookingFormValues = { equipmentReturn: "with_return", originYard: "", destinationYard: "", - cargoType: "", cargoWeight: "", - freightType: "", bulkCommodity: "", bulkCommodityOther: "", breakBulkType: "", diff --git a/apps/edr-freight-web/portal/src/services/bookings.service.ts b/apps/edr-freight-web/portal/src/services/bookings.service.ts index dafb071f2..17ace13e8 100644 --- a/apps/edr-freight-web/portal/src/services/bookings.service.ts +++ b/apps/edr-freight-web/portal/src/services/bookings.service.ts @@ -2,13 +2,7 @@ import type { Freight, PaginatedResponse } from "@edr/types"; import { api } from "./crud"; -export interface CreateBookingPayload { - reference: string; - customerId: string; - scheduledDate: string; - totalAmount: number; - trainId?: string; -} +export type CreateBookingPayload = Freight.CreateBookingDto; export const bookingsService = { list: async (): Promise> => { diff --git a/apps/edr-freight-web/portal/src/services/consignments.service.ts b/apps/edr-freight-web/portal/src/services/consignments.service.ts index 44becc2e7..fdaf3a667 100644 --- a/apps/edr-freight-web/portal/src/services/consignments.service.ts +++ b/apps/edr-freight-web/portal/src/services/consignments.service.ts @@ -1,14 +1,14 @@ import type { Freight, PaginatedResponse } from "@edr/types"; -import { api } from "../utils/api"; +import { client } from "../utils/api"; export const consignmentsService = { list: async (): Promise> => { - const { data } = await api.get("/consignments"); + const { data } = await client.get("/consignments"); return data.data; }, get: async (id: string): Promise => { - const { data } = await api.get(`/consignments/${id}`); + const { data } = await client.get(`/consignments/${id}`); return data.data; }, }; diff --git a/packages/types/src/freight/index.ts b/packages/types/src/freight/index.ts index 52511b73a..a9f768b1c 100644 --- a/packages/types/src/freight/index.ts +++ b/packages/types/src/freight/index.ts @@ -124,3 +124,35 @@ export interface IInvoice extends BaseEntity { issuedAt: string; dueAt: string; } + +export interface CreateBookingDto { + reference: string; + customerId: string; + trainId?: string; + scheduledDate: string; + totalAmount: number; + paymentStatus?: string; + contractType: "NEW" | "RENEWAL"; + previousContractId?: string; + serviceType: "RAIL_ONLY" | "RAIL_AND_FORWARDING"; + + firstMileEnabled?: boolean; + firstMilePickupAddress?: string; + lastMileEnabled?: boolean; + lastMileDeliveryAddress?: string; + + equipmentReturn: "WITH_RETURN" | "WITHOUT_RETURN"; + originStation: string; + destinationStation: string; + cargoTotalWeightVgm: number; + + freightType: "BULK" | "BREAK_BULK"; + freightSubtype?: string; + + isHazardous?: boolean; + isRefrigerated?: boolean; + + tradeDirection: "IMPORT" | "EXPORT"; + paymentCurrency: string; + allowConsolidation?: boolean; +}