import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsDateString, IsIn, IsInt, IsOptional, IsString, IsUUID, Max, Min, MinLength, } from 'class-validator'; export class RequestChangesDto { @ApiProperty({ description: 'Staff note explaining what the customer must fix' }) @IsString() @MinLength(1) note!: string; } export class AcceptIntakeDto { @ApiProperty({ description: 'How many days the contract stays valid, counted from the accept date. ' + 'The contract is valid from now through now + validityDays.', minimum: 1, maximum: 365, example: 30, }) @IsInt() @Min(1) @Max(365) validityDays!: number; } export class StaffRejectDto { @ApiProperty() @IsString() @MinLength(1) reason!: string; } export class ApproveStepDto { @ApiProperty({ description: 'LINE_STAFF | DIRECTOR | CEO' }) @IsString() requiredRole!: string; } export class RejectStepDto { @ApiProperty() @IsString() @MinLength(1) reason!: string; } export class CancelBookingDto { @ApiProperty() @IsString() @MinLength(1) reason!: string; } export class RejectBookingDto { @ApiPropertyOptional({ description: 'Optional reason the customer rejected the price estimate', }) @IsOptional() @IsString() reason?: string; } export class ReviewDocumentDto { @ApiProperty({ description: 'The document fileKey being reviewed' }) @IsString() @MinLength(1) fileKey!: string; @ApiProperty({ enum: ['APPROVED', 'QUERIED'] }) @IsIn(['APPROVED', 'QUERIED']) status!: 'APPROVED' | 'QUERIED'; @ApiPropertyOptional({ description: 'Required when querying a document' }) @IsOptional() @IsString() note?: string; } export class RequestOperationDto { @ApiProperty({ description: 'The schedule day (train departure day) the customer selects for this ' + 'shipment. ISO date — the booking enters the batch pool for this route + day.', example: '2026-07-15', }) @IsDateString() scheduledDate!: string; @ApiPropertyOptional({ description: 'EXPORT rail only: the specific train (schedule id) the customer picked ' + 'from GET /bookings/:id/export-trains. The reserve path locks onto this ' + 'train instead of earliest-first; 409 if it no longer fits. Ignored for ' + 'import/domestic/road bookings.', }) @IsOptional() @IsUUID() trainScheduleId?: string; } export class OperationReviewDto { @ApiProperty({ description: 'The operations decision: ACCEPT enters the batch pool; REQUEST_CHANGES ' + 'returns it to the customer with a note. The booking price is computed ' + 'from the contract and cannot be adjusted by staff.', enum: ['ACCEPT', 'REQUEST_CHANGES'], }) @IsIn(['ACCEPT', 'REQUEST_CHANGES']) decision!: 'ACCEPT' | 'REQUEST_CHANGES'; @ApiPropertyOptional({ description: 'Required for REQUEST_CHANGES (what the customer must fix).', }) @IsOptional() @IsString() note?: string; } /** * A staff decision applied to BOTH halves of a consolidated pair. The two * bookings share a wagon, so they advance or cancel together — never one alone. */ export class PairedDecisionDto { @ApiProperty({ enum: ["accept", "cancel", "operationAccept", "requestChanges"], description: 'Which staff decision to apply to both bookings.', }) @IsIn(["accept", "cancel", "operationAccept", "requestChanges"]) decision!: "accept" | "cancel" | "operationAccept" | "requestChanges"; @ApiPropertyOptional({ description: "Cancellation reason (decision=cancel)." }) @IsOptional() @IsString() reason?: string; @ApiPropertyOptional({ description: "Message to the customer (decision=requestChanges).", }) @IsOptional() @IsString() note?: string; @ApiPropertyOptional({ description: "Contract validity window in days (decision=accept).", }) @IsOptional() @IsInt() @Min(1) validityDays?: number; }