enhance contract and booking services with server-side search and validation improvements

- Added  parameter to  and  for server-side free-text search on contract reference, company name, and booking details.
- Introduced new validation errors in  for container clashes and space issues when creating bookings.
- Implemented paginated dropdown settings retrieval in .
- Updated  to fetch active yards using a new method that handles pagination.
- Enhanced  with a  method to fetch all records by walking through pages.
- Refactored  to support filtering and pagination in schedule listings.
- Improved  to return a paginated list of facilities.
- Updated UI components in  and  to utilize debounced search inputs for better performance.
- Added alerts in  to inform users about booking constraints related to splits and capacity.
- Enhanced  to display notifications for split bookings and capacity usage.
This commit is contained in:
Marshal
2026-07-12 10:51:31 +00:00
parent 6695c5448e
commit 4b7f6d2548
108 changed files with 3187 additions and 1368 deletions

View File

@@ -1,15 +1,7 @@
import { ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import {
IsIn,
IsInt,
IsISO8601,
IsOptional,
IsString,
Max,
MaxLength,
Min,
} from 'class-validator';
import { IsIn, IsISO8601, IsOptional, IsString } from 'class-validator';
import { PaginationQueryDto } from '../../../common/dto/pagination-query.dto';
export const BATCH_BOARD_STATUSES = [
'DRAFT',
@@ -27,23 +19,13 @@ export const BATCH_BOARD_SORT_FIELDS = [
] as const;
export type BatchBoardSortField = (typeof BATCH_BOARD_SORT_FIELDS)[number];
/** Filters for the batch monitoring board list (import schedules, all statuses). */
export class BatchBoardQueryDto {
@ApiPropertyOptional({ default: 1, minimum: 1 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number;
@ApiPropertyOptional({ default: 12, minimum: 1, maximum: 100 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
@Max(100)
pageSize?: number;
/**
* Filters for the batch monitoring board list (import schedules, all statuses).
* `page`/`pageSize`/`search`/`sortOrder` come from the shared
* {@link PaginationQueryDto}; search matches train number, route yards,
* stations, or locomotive code (case-insensitive).
*/
export class BatchBoardQueryDto extends PaginationQueryDto {
@ApiPropertyOptional({
description:
'Comma-separated schedule statuses (DRAFT,SCHEDULED,DISPATCHED,ARRIVED,CANCELLED). Omit for all.',
@@ -58,15 +40,6 @@ export class BatchBoardQueryDto {
@IsIn(['OPEN', 'FULL', 'CLOSED'])
bookingWindowStatus?: 'OPEN' | 'FULL' | 'CLOSED';
@ApiPropertyOptional({
description:
'Case-insensitive match on train number, route yards, stations, or locomotive code.',
})
@IsOptional()
@IsString()
@MaxLength(120)
search?: string;
@ApiPropertyOptional({ description: 'Departure date lower bound (ISO 8601).' })
@IsOptional()
@IsISO8601()
@@ -91,9 +64,4 @@ export class BatchBoardQueryDto {
@IsOptional()
@IsIn(BATCH_BOARD_SORT_FIELDS as unknown as string[])
sortBy?: BatchBoardSortField;
@ApiPropertyOptional({ enum: ['ASC', 'DESC'], default: 'DESC' })
@IsOptional()
@IsIn(['ASC', 'DESC'])
sortOrder?: 'ASC' | 'DESC';
}

View File

@@ -0,0 +1,64 @@
import { ApiPropertyOptional } from '@nestjs/swagger';
import { IsIn, IsOptional, IsUUID } from 'class-validator';
import { PaginationQueryDto } from '../../../common/dto/pagination-query.dto';
import {
TRAIN_SCHEDULE_STATUSES,
TrainScheduleStatus,
} from '../../train-schedules/entities/train-schedule.entity';
export const TRAIN_SCHEDULE_SORT_FIELDS = [
'createdAt',
'scheduledDepartureDate',
'reference',
'trainNumber',
'status',
] as const;
export type TrainScheduleSortField = (typeof TRAIN_SCHEDULE_SORT_FIELDS)[number];
/**
* Schedules have no freight-type column — the type is DERIVED from the
* bookings aboard (see `resolveScheduleFreightType`): a single kind yields
* CONTAINER or BULK, both kinds yield MIXED, no bookings yield null (never
* matched by this filter).
*/
export const TRAIN_SCHEDULE_FREIGHT_TYPES = ['CONTAINER', 'BULK', 'MIXED'] as const;
export type TrainScheduleFreightType = (typeof TRAIN_SCHEDULE_FREIGHT_TYPES)[number];
/**
* Query for the train-schedule list (container/bulk boards). Pagination and
* free-text `search` come from the shared {@link PaginationQueryDto}; search
* matches schedule reference, train number, route yards, stations, or
* locomotive code (case-insensitive). The remaining fields are exact-match
* filters that the search never widens.
*/
export class ListTrainSchedulesQueryDto extends PaginationQueryDto {
@ApiPropertyOptional({ enum: TRAIN_SCHEDULE_SORT_FIELDS, default: 'createdAt' })
@IsOptional()
@IsIn(TRAIN_SCHEDULE_SORT_FIELDS as unknown as string[])
sortBy?: TrainScheduleSortField;
/** Lifecycle status (exact match). */
@ApiPropertyOptional({ enum: TRAIN_SCHEDULE_STATUSES })
@IsOptional()
@IsIn(TRAIN_SCHEDULE_STATUSES as unknown as string[])
status?: TrainScheduleStatus;
/** Derived freight type of the bookings aboard (exact match). */
@ApiPropertyOptional({ enum: TRAIN_SCHEDULE_FREIGHT_TYPES })
@IsOptional()
@IsIn(TRAIN_SCHEDULE_FREIGHT_TYPES as unknown as string[])
freightType?: TrainScheduleFreightType;
/** Origin station/yard id (exact match). */
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()
@IsUUID()
originStationId?: string;
/** Destination station/yard id (exact match). */
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()
@IsUUID()
destinationStationId?: string;
}