mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 07:51:02 +00:00
- Updated OverviewContractsTabPanel to include a new donut chart for freight type distribution. - Modified OverviewOperationsTabPanel to improve data visualization with additional charts and refactored data handling. - Introduced CreateScheduleWindowFields component for configuring booking windows in train scheduling. - Added new API endpoints for allocation candidates and booking allocation in trainScheduling.service. - Enhanced BookingRequestsPage to support allocation of paid bookings with a modal for selecting alternative dates. - Updated QUERY_KEYS and URLS constants to accommodate new operations and features. - Improved type definitions for overview and train scheduling to support new functionalities.
190 lines
5.2 KiB
TypeScript
190 lines
5.2 KiB
TypeScript
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
import { Type } from 'class-transformer';
|
|
import {
|
|
ArrayMinSize,
|
|
IsArray,
|
|
IsBoolean,
|
|
IsDateString,
|
|
IsInt,
|
|
IsNumber,
|
|
IsOptional,
|
|
IsUUID,
|
|
Max,
|
|
Min,
|
|
ValidateNested,
|
|
} from 'class-validator';
|
|
|
|
/**
|
|
* Per-schedule booking-window rule chosen AT CREATION, instead of inheriting the
|
|
* live global rules. Mirrors {@link UpdateScheduleWindowRuleDto}, plus the
|
|
* booking-close offset (which the post-creation override deliberately never
|
|
* touches). Every field is optional — an omitted field falls back to the global
|
|
* value, so staff can override just the one knob they care about.
|
|
*/
|
|
export class CreateScheduleWindowRuleDto {
|
|
@ApiPropertyOptional({ example: 8, description: 'Local EAT hour the booking desk opens each day' })
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(0)
|
|
@Max(23)
|
|
windowOpenHour?: number;
|
|
|
|
@ApiPropertyOptional({
|
|
example: 17,
|
|
description:
|
|
'Local EAT hour the booking desk shuts each day. Equal to windowOpenHour = 24-hour desk',
|
|
})
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(0)
|
|
@Max(23)
|
|
windowCloseHour?: number;
|
|
|
|
@ApiPropertyOptional({ example: 3, description: 'How long each booking cycle stays open, in hours' })
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsNumber()
|
|
@Min(0.0166)
|
|
@Max(12)
|
|
windowDurationHours?: number;
|
|
|
|
@ApiPropertyOptional({ example: 30, description: 'Max staff document-review minutes after the window closes' })
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(0)
|
|
docReviewMinutes?: number;
|
|
|
|
@ApiPropertyOptional({ example: 60, description: 'Customer payment window minutes' })
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(1)
|
|
paymentWindowMinutes?: number;
|
|
|
|
@ApiPropertyOptional({
|
|
example: 3,
|
|
description: 'Days before departure the IMPORT/DOMESTIC booking window starts',
|
|
})
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(0)
|
|
importWindowLeadDays?: number;
|
|
|
|
@ApiPropertyOptional({
|
|
example: 24,
|
|
description: 'Hours before departure the single FCFS EXPORT window opens',
|
|
})
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(1)
|
|
exportBookingLeadHours?: number;
|
|
|
|
@ApiPropertyOptional({
|
|
example: 180,
|
|
nullable: true,
|
|
description:
|
|
'Minutes before departure the booking window closes; 0/null = close at departure. ' +
|
|
'Only the offset matching the schedule direction is used (import offset for ' +
|
|
'IMPORT/DOMESTIC, export offset for EXPORT).',
|
|
})
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(0)
|
|
importCloseOffsetMinutes?: number | null;
|
|
|
|
@ApiPropertyOptional({
|
|
example: 1440,
|
|
nullable: true,
|
|
description: 'Minutes before departure an EXPORT booking window closes; 0/null = at departure',
|
|
})
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(0)
|
|
exportCloseOffsetMinutes?: number | null;
|
|
}
|
|
|
|
export class CreateContainerTrainScheduleDto {
|
|
@ApiProperty({ format: 'uuid' })
|
|
@IsUUID()
|
|
routeId!: string;
|
|
|
|
@ApiProperty({ example: '2026-06-20T08:00:00.000Z' })
|
|
@IsDateString()
|
|
scheduleDate!: string;
|
|
|
|
@ApiPropertyOptional({
|
|
format: 'uuid',
|
|
description:
|
|
'Built train (Train Builder) to run this departure — its locomotive set is used. Provide either trainId or locomotiveIds.',
|
|
})
|
|
@IsOptional()
|
|
@IsUUID()
|
|
trainId?: string;
|
|
|
|
@ApiPropertyOptional({
|
|
type: [String],
|
|
format: 'uuid',
|
|
description:
|
|
'Hand-picked locomotives pulling the train (minimum 1). Ignored when trainId is provided.',
|
|
})
|
|
@IsOptional()
|
|
@IsArray()
|
|
@ArrayMinSize(1, { message: 'A train must be pulled by at least one locomotive' })
|
|
@IsUUID('all', { each: true })
|
|
locomotiveIds?: string[];
|
|
|
|
@ApiPropertyOptional({ description: 'Maximum total booking weight allowed on this train' })
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsNumber()
|
|
@Min(1)
|
|
maxTrainWeightTons?: number;
|
|
|
|
@ApiPropertyOptional({ description: 'Maximum total wagon length allowed on this train' })
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsNumber()
|
|
@Min(1)
|
|
maxTrainLengthMeters?: number;
|
|
|
|
@ApiPropertyOptional({ description: 'Maximum wagons allowed on this train' })
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(1)
|
|
maxWagonsPerTrain?: number;
|
|
|
|
@ApiPropertyOptional({
|
|
description:
|
|
'Reverse the wagon order on this train: the physically-last wagon becomes ' +
|
|
'position 1. Frozen on the schedule; applied every time the wagon plan is ' +
|
|
'rebuilt so the stored train order and the schedule order stay in sync.',
|
|
default: false,
|
|
})
|
|
@IsOptional()
|
|
@IsBoolean()
|
|
reverseWagonOrder?: boolean;
|
|
|
|
@ApiPropertyOptional({
|
|
type: CreateScheduleWindowRuleDto,
|
|
description:
|
|
'Configure the booking window for THIS schedule instead of inheriting the live ' +
|
|
'global rules. Omit to use the global rules (the default). The values sent are ' +
|
|
'frozen onto the schedule as its rule snapshot, exactly as a post-creation ' +
|
|
'override would. Rejected for an IMPORT/DOMESTIC train that joins an existing ' +
|
|
'route+day group — those siblings share one window timeline, so edit the group ' +
|
|
"window instead of giving one member its own.",
|
|
})
|
|
@IsOptional()
|
|
@ValidateNested()
|
|
@Type(() => CreateScheduleWindowRuleDto)
|
|
windowRule?: CreateScheduleWindowRuleDto;
|
|
}
|