import { IsString, IsUUID, IsOptional, ValidateNested, IsArray, IsEmail, Matches, MinLength, } from 'class-validator'; import { Type } from 'class-transformer'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; export class AddressDto { @ApiProperty({ example: '123 Main Street' }) @IsString() @MinLength(5, { message: 'Street must be at least 5 characters' }) street: string; @ApiProperty({ example: 'Rotterdam' }) @IsString() @MinLength(2, { message: 'City must be at least 2 characters' }) city: string; @ApiProperty({ example: '3000 AB' }) @IsString() postalCode: string; @ApiProperty({ example: 'NL', description: 'ISO 3166-1 alpha-2 country code' }) @IsString() @Matches(/^[A-Z]{2}$/, { message: 'Country must be a valid 2-letter ISO country code' }) country: string; } export class PartyDto { @ApiProperty({ example: 'Acme Corporation' }) @IsString() @MinLength(2, { message: 'Name must be at least 2 characters' }) name: string; @ApiProperty({ type: AddressDto }) @ValidateNested() @Type(() => AddressDto) address: AddressDto; @ApiProperty({ example: 'John Doe' }) @IsString() @MinLength(2, { message: 'Contact name must be at least 2 characters' }) contactName: string; @ApiProperty({ example: 'john.doe@acme.com' }) @IsEmail({}, { message: 'Contact email must be a valid email address' }) contactEmail: string; @ApiProperty({ example: '+31612345678' }) @IsString() @Matches(/^\+?[1-9]\d{1,14}$/, { message: 'Contact phone must be a valid international phone number', }) contactPhone: string; } export class ContainerDto { @ApiProperty({ example: '40HC', description: 'Container type' }) @IsString() type: string; @ApiPropertyOptional({ example: 'ABCU1234567', description: 'Container number (11 characters)' }) @IsOptional() @IsString() @Matches(/^[A-Z]{4}\d{7}$/, { message: 'Container number must be 4 letters followed by 7 digits', }) containerNumber?: string; @ApiPropertyOptional({ example: 22000, description: 'Verified Gross Mass in kg' }) @IsOptional() vgm?: number; @ApiPropertyOptional({ example: -18, description: 'Temperature in Celsius (for reefer containers)', }) @IsOptional() temperature?: number; @ApiPropertyOptional({ example: 'SEAL123456', description: 'Seal number' }) @IsOptional() @IsString() sealNumber?: string; } export class CreateBookingRequestDto { @ApiProperty({ example: '550e8400-e29b-41d4-a716-446655440000', description: 'Rate quote ID from previous search', }) @IsUUID(4, { message: 'Rate quote ID must be a valid UUID' }) rateQuoteId: string; @ApiProperty({ type: PartyDto, description: 'Shipper details' }) @ValidateNested() @Type(() => PartyDto) shipper: PartyDto; @ApiProperty({ type: PartyDto, description: 'Consignee details' }) @ValidateNested() @Type(() => PartyDto) consignee: PartyDto; @ApiProperty({ example: 'Electronics and consumer goods', description: 'Cargo description', }) @IsString() @MinLength(10, { message: 'Cargo description must be at least 10 characters' }) cargoDescription: string; @ApiProperty({ type: [ContainerDto], description: 'Container details (can be empty for initial booking)', }) @IsArray() @ValidateNested({ each: true }) @Type(() => ContainerDto) containers: ContainerDto[]; @ApiPropertyOptional({ example: 'Please handle with care. Delivery before 5 PM.', description: 'Special instructions for the carrier', }) @IsOptional() @IsString() specialInstructions?: string; }