import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsString, IsEmail, IsNumber, Min, IsOptional, IsEnum, IsArray, ValidateNested, IsUUID, IsDateString, MinLength, MaxLength, } from 'class-validator'; import { Type } from 'class-transformer'; /** * Create CSV Booking DTO * * Request body for creating a new CSV-based booking request * This is sent by the user after selecting a rate from CSV search results */ export class CreateCsvBookingDto { @ApiProperty({ description: 'Carrier/Company name', example: 'SSC Consolidation', }) @IsString() @MinLength(2) @MaxLength(200) carrierName: string; @ApiProperty({ description: 'Carrier email address for booking request', example: 'bookings@sscconsolidation.com', }) @IsEmail() carrierEmail: string; @ApiProperty({ description: 'Origin port code (UN/LOCODE)', example: 'NLRTM', }) @IsString() @MinLength(5) @MaxLength(5) origin: string; @ApiProperty({ description: 'Destination port code (UN/LOCODE)', example: 'USNYC', }) @IsString() @MinLength(5) @MaxLength(5) destination: string; @ApiProperty({ description: 'Volume in cubic meters (CBM)', example: 25.5, minimum: 0.01, }) @IsNumber() @Min(0.01) volumeCBM: number; @ApiProperty({ description: 'Weight in kilograms', example: 3500, minimum: 1, }) @IsNumber() @Min(1) weightKG: number; @ApiProperty({ description: 'Number of pallets', example: 10, minimum: 0, }) @IsNumber() @Min(0) palletCount: number; @ApiProperty({ description: 'Price in USD', example: 1850.5, minimum: 0, }) @IsNumber() @Min(0) priceUSD: number; @ApiProperty({ description: 'Price in EUR', example: 1665.45, minimum: 0, }) @IsNumber() @Min(0) priceEUR: number; @ApiProperty({ description: 'Primary currency', enum: ['USD', 'EUR'], example: 'USD', }) @IsEnum(['USD', 'EUR']) primaryCurrency: string; @ApiProperty({ description: 'Transit time in days', example: 28, minimum: 1, }) @IsNumber() @Min(1) transitDays: number; @ApiProperty({ description: 'Container type', example: 'LCL', }) @IsString() @MinLength(2) @MaxLength(50) containerType: string; @ApiPropertyOptional({ description: 'Additional notes or requirements', example: 'Please handle with care - fragile goods', }) @IsOptional() @IsString() @MaxLength(1000) notes?: string; // Documents will be handled via file upload interceptor // Not included in DTO validation but processed separately } /** * Document DTO for response */ export class CsvBookingDocumentDto { @ApiProperty({ description: 'Document unique ID', example: '123e4567-e89b-12d3-a456-426614174000', }) id: string; @ApiProperty({ description: 'Document type', enum: [ 'BILL_OF_LADING', 'PACKING_LIST', 'COMMERCIAL_INVOICE', 'CERTIFICATE_OF_ORIGIN', 'OTHER', ], example: 'BILL_OF_LADING', }) type: string; @ApiProperty({ description: 'Original file name', example: 'bill-of-lading.pdf', }) fileName: string; @ApiProperty({ description: 'File storage path or URL', example: '/uploads/documents/123e4567-e89b-12d3-a456-426614174000.pdf', }) filePath: string; @ApiProperty({ description: 'File MIME type', example: 'application/pdf', }) mimeType: string; @ApiProperty({ description: 'File size in bytes', example: 245678, }) size: number; @ApiProperty({ description: 'Upload timestamp', example: '2025-10-23T14:30:00Z', }) uploadedAt: Date; } /** * CSV Booking Response DTO * * Response when creating or retrieving a CSV booking */ export class CsvBookingResponseDto { @ApiProperty({ description: 'Booking unique ID', example: '123e4567-e89b-12d3-a456-426614174000', }) id: string; @ApiProperty({ description: 'User ID who created the booking', example: '987fcdeb-51a2-43e8-9c6d-8b9a1c2d3e4f', }) userId: string; @ApiProperty({ description: 'Organization ID', example: 'a1234567-0000-4000-8000-000000000001', }) organizationId: string; @ApiProperty({ description: 'Carrier/Company name', example: 'SSC Consolidation', }) carrierName: string; @ApiProperty({ description: 'Carrier email address', example: 'bookings@sscconsolidation.com', }) carrierEmail: string; @ApiProperty({ description: 'Origin port code', example: 'NLRTM', }) origin: string; @ApiProperty({ description: 'Destination port code', example: 'USNYC', }) destination: string; @ApiProperty({ description: 'Volume in CBM', example: 25.5, }) volumeCBM: number; @ApiProperty({ description: 'Weight in KG', example: 3500, }) weightKG: number; @ApiProperty({ description: 'Number of pallets', example: 10, }) palletCount: number; @ApiProperty({ description: 'Price in USD', example: 1850.5, }) priceUSD: number; @ApiProperty({ description: 'Price in EUR', example: 1665.45, }) priceEUR: number; @ApiProperty({ description: 'Primary currency', enum: ['USD', 'EUR'], example: 'USD', }) primaryCurrency: string; @ApiProperty({ description: 'Transit time in days', example: 28, }) transitDays: number; @ApiProperty({ description: 'Container type', example: 'LCL', }) containerType: string; @ApiProperty({ description: 'Booking status', enum: ['PENDING', 'ACCEPTED', 'REJECTED', 'CANCELLED'], example: 'PENDING', }) status: string; @ApiProperty({ description: 'Uploaded documents', type: [CsvBookingDocumentDto], }) documents: CsvBookingDocumentDto[]; @ApiProperty({ description: 'Confirmation token for accept/reject actions', example: 'abc123-def456-ghi789', }) confirmationToken: string; @ApiProperty({ description: 'Booking request timestamp', example: '2025-10-23T14:30:00Z', }) requestedAt: Date; @ApiProperty({ description: 'Response timestamp (when accepted/rejected)', example: '2025-10-24T09:15:00Z', nullable: true, }) respondedAt: Date | null; @ApiPropertyOptional({ description: 'Additional notes', example: 'Please handle with care', }) notes?: string; @ApiPropertyOptional({ description: 'Rejection reason (if rejected)', example: 'No capacity available for requested dates', }) rejectionReason?: string; @ApiProperty({ description: 'Route description (origin → destination)', example: 'NLRTM → USNYC', }) routeDescription: string; @ApiProperty({ description: 'Whether the booking is expired (7+ days pending)', example: false, }) isExpired: boolean; @ApiProperty({ description: 'Price in the primary currency', example: 1850.5, }) price: number; } /** * Update CSV Booking Status DTO * * Request body for accepting/rejecting a booking */ export class UpdateCsvBookingStatusDto { @ApiPropertyOptional({ description: 'Rejection reason (required when rejecting)', example: 'No capacity available', }) @IsOptional() @IsString() @MaxLength(500) rejectionReason?: string; } /** * CSV Booking List Response DTO * * Paginated list of bookings */ export class CsvBookingListResponseDto { @ApiProperty({ description: 'Array of bookings', type: [CsvBookingResponseDto], }) bookings: CsvBookingResponseDto[]; @ApiProperty({ description: 'Total number of bookings', example: 42, }) total: number; @ApiProperty({ description: 'Current page number', example: 1, }) page: number; @ApiProperty({ description: 'Number of items per page', example: 10, }) limit: number; @ApiProperty({ description: 'Total number of pages', example: 5, }) totalPages: number; } /** * CSV Booking Statistics DTO * * Statistics for user's or organization's bookings */ export class CsvBookingStatsDto { @ApiProperty({ description: 'Number of pending bookings', example: 5, }) pending: number; @ApiProperty({ description: 'Number of accepted bookings', example: 12, }) accepted: number; @ApiProperty({ description: 'Number of rejected bookings', example: 2, }) rejected: number; @ApiProperty({ description: 'Number of cancelled bookings', example: 1, }) cancelled: number; @ApiProperty({ description: 'Total number of bookings', example: 20, }) total: number; }