mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 16:00:56 +00:00
420 lines
13 KiB
TypeScript
420 lines
13 KiB
TypeScript
import { api as client } from '../auth/http';
|
|
import { unwrap } from '@/utils/endpoint';
|
|
import { URL_CONSTANTS } from '@/constants/URLS';
|
|
import type {
|
|
BatchBoardSchedule,
|
|
BatchBoardScheduleDetail,
|
|
BookableSchedule,
|
|
AssignBookingsPayload,
|
|
CompositionRemovalEntry,
|
|
CompositionUnassignedBooking,
|
|
UnassignedBookingsResponse,
|
|
CreateTrainSchedulePayload,
|
|
EligibleContainerBookingsResponse,
|
|
FreightType,
|
|
LocomotiveRecord,
|
|
PinWagonsPayload,
|
|
RecordCheckpointPayload,
|
|
TrainScheduleDetail,
|
|
TrainScheduleFilters,
|
|
TrainScheduleListItem,
|
|
TrainSchedulePreviewPayload,
|
|
TrainSchedulePreviewResponse,
|
|
TrainSchedulingGlobalRules,
|
|
TrainTrackResponse,
|
|
WagonAllocationAttemptResult,
|
|
YardOption,
|
|
} from '@/types/trainScheduling';
|
|
|
|
interface BookingReferenceDataResponse {
|
|
yard?: Array<YardOption & { label?: string }>;
|
|
}
|
|
|
|
const pathsFor = (freightType?: FreightType) =>
|
|
freightType === "BULK"
|
|
? URL_CONSTANTS.TRAIN_SCHEDULING.BULK
|
|
: URL_CONSTANTS.TRAIN_SCHEDULING.CONTAINER;
|
|
|
|
export const trainSchedulingService = {
|
|
getEligibleBookings: async (
|
|
filters?: TrainScheduleFilters,
|
|
freightType?: FreightType,
|
|
): Promise<EligibleContainerBookingsResponse> => {
|
|
const useUnified = !freightType || freightType === "MIXED";
|
|
// The container/bulk endpoints already encode freight type in the path, and their
|
|
// query DTOs reject an extra `freightType` param — so only pass the station filters.
|
|
const response = await client.get<EligibleContainerBookingsResponse>(
|
|
useUnified
|
|
? URL_CONSTANTS.TRAIN_SCHEDULING.ELIGIBLE_BOOKINGS
|
|
: pathsFor(freightType).ELIGIBLE_BOOKINGS,
|
|
{ params: { ...filters } },
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
preview: async (
|
|
payload: TrainSchedulePreviewPayload,
|
|
freightType?: FreightType,
|
|
): Promise<TrainSchedulePreviewResponse> => {
|
|
const useUnified = !freightType || freightType === "MIXED";
|
|
const response = await client.post<TrainSchedulePreviewResponse>(
|
|
useUnified ? URL_CONSTANTS.TRAIN_SCHEDULING.PREVIEW : pathsFor(freightType).PREVIEW,
|
|
payload,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
createSchedule: async (
|
|
payload: CreateTrainSchedulePayload,
|
|
freightType?: FreightType,
|
|
): Promise<TrainScheduleDetail> => {
|
|
const response = await client.post<TrainScheduleDetail>(
|
|
pathsFor(freightType).SCHEDULES,
|
|
payload,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
listSchedules: async (
|
|
freightType: FreightType = "CONTAINER",
|
|
): Promise<TrainScheduleListItem[]> => {
|
|
const response = await client.get<TrainScheduleListItem[]>(
|
|
pathsFor(freightType === "MIXED" ? undefined : freightType).SCHEDULES,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
getBatchBoard: async (): Promise<BatchBoardSchedule[]> => {
|
|
const response = await client.get<BatchBoardSchedule[]>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.BATCH_BOARD,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
getBatchBoardDetail: async (scheduleId: string): Promise<BatchBoardScheduleDetail> => {
|
|
const response = await client.get<BatchBoardScheduleDetail>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.BATCH_BOARD_DETAIL(scheduleId),
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
getBookableSchedules: async (
|
|
originYardId?: string,
|
|
destinationYardId?: string,
|
|
): Promise<BookableSchedule[]> => {
|
|
const response = await client.get<BookableSchedule[]>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.BOOKABLE_SCHEDULES,
|
|
{ params: { originYardId, destinationYardId } },
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
/**
|
|
* Day-level pool: the days that have an OPEN departure on the route. Staff pick
|
|
* a day; the batch engine assigns the train. No capacity is returned.
|
|
*/
|
|
getAvailableDays: async (
|
|
originYardId?: string,
|
|
destinationYardId?: string,
|
|
): Promise<string[]> => {
|
|
const response = await client.get<{ days: string[] }>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.AVAILABLE_DAYS,
|
|
{ params: { originYardId, destinationYardId } },
|
|
);
|
|
return unwrap(response.data).days;
|
|
},
|
|
|
|
runBatch: async (scheduleId: string): Promise<BatchBoardScheduleDetail> => {
|
|
const response = await client.post<BatchBoardScheduleDetail>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.RUN_BATCH(scheduleId),
|
|
{},
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
runAllocation: async (scheduleId: string): Promise<WagonAllocationAttemptResult> => {
|
|
const response = await client.post<WagonAllocationAttemptResult>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.RUN_ALLOCATION(scheduleId),
|
|
{},
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
setBookingWindow: async (
|
|
scheduleId: string,
|
|
status: "OPEN" | "CLOSED",
|
|
): Promise<TrainScheduleDetail> => {
|
|
const response = await client.patch<TrainScheduleDetail>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.BOOKING_WINDOW(scheduleId),
|
|
{ status },
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
markBookingPaid: async (bookingId: string): Promise<void> => {
|
|
await client.post(URL_CONSTANTS.TRAIN_SCHEDULING.MARK_BOOKING_PAID(bookingId), {});
|
|
},
|
|
|
|
expireBooking: async (bookingId: string): Promise<void> => {
|
|
await client.post(URL_CONSTANTS.TRAIN_SCHEDULING.EXPIRE_BOOKING(bookingId), {});
|
|
},
|
|
|
|
moveBookingSchedule: async (
|
|
bookingId: string,
|
|
trainScheduleId: string,
|
|
): Promise<void> => {
|
|
await client.post(URL_CONSTANTS.TRAIN_SCHEDULING.MOVE_BOOKING_SCHEDULE(bookingId), {
|
|
trainScheduleId,
|
|
});
|
|
},
|
|
|
|
getScheduleById: async (
|
|
id: string,
|
|
freightType?: FreightType,
|
|
): Promise<TrainScheduleDetail> => {
|
|
const response = await client.get<TrainScheduleDetail>(
|
|
pathsFor(freightType === "MIXED" ? undefined : freightType).SCHEDULE_BY_ID(id),
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
assignUnassignedBooking: async (
|
|
scheduleId: string,
|
|
bookingId: string,
|
|
): Promise<TrainScheduleDetail> => {
|
|
const response = await client.post<TrainScheduleDetail>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.ASSIGN_UNASSIGNED_BOOKING(scheduleId),
|
|
{ bookingId },
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
assignBookings: async (
|
|
scheduleId: string,
|
|
payload: AssignBookingsPayload,
|
|
freightType?: FreightType,
|
|
): Promise<TrainScheduleDetail> => {
|
|
const useUnified = !freightType || freightType === "MIXED";
|
|
const response = await client.post<TrainScheduleDetail>(
|
|
useUnified
|
|
? URL_CONSTANTS.TRAIN_SCHEDULING.ASSIGN_BOOKINGS(scheduleId)
|
|
: pathsFor(freightType).ASSIGN_BOOKINGS(scheduleId),
|
|
payload,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
unassignBooking: async (
|
|
scheduleId: string,
|
|
bookingId: string,
|
|
): Promise<TrainScheduleDetail> => {
|
|
const response = await client.delete<TrainScheduleDetail>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.UNASSIGN_BOOKING(scheduleId, bookingId),
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
pinWagons: async (
|
|
scheduleId: string,
|
|
payload: PinWagonsPayload,
|
|
): Promise<TrainScheduleDetail> => {
|
|
const response = await client.post<TrainScheduleDetail>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.PIN_WAGONS(scheduleId),
|
|
payload,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
finalizeSchedule: async (scheduleId: string): Promise<TrainScheduleDetail> => {
|
|
const response = await client.post<TrainScheduleDetail>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.FINALIZE(scheduleId),
|
|
{},
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
dispatchSchedule: async (scheduleId: string): Promise<TrainScheduleDetail> => {
|
|
const response = await client.post<TrainScheduleDetail>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.DISPATCH(scheduleId),
|
|
{},
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
getTrack: async (scheduleId: string): Promise<TrainTrackResponse> => {
|
|
const response = await client.get<TrainTrackResponse>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.CHECKPOINTS(scheduleId),
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
recordCheckpoint: async (
|
|
scheduleId: string,
|
|
payload: RecordCheckpointPayload,
|
|
): Promise<TrainTrackResponse> => {
|
|
const response = await client.post<TrainTrackResponse>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.CHECKPOINTS(scheduleId),
|
|
payload,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
arriveSchedule: async (scheduleId: string): Promise<TrainScheduleDetail> => {
|
|
const response = await client.post<TrainScheduleDetail>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.ARRIVE(scheduleId),
|
|
{},
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
cancelSchedule: async (
|
|
id: string,
|
|
freightType: FreightType = "CONTAINER",
|
|
): Promise<TrainScheduleDetail> => {
|
|
const response = await client.post<TrainScheduleDetail>(
|
|
pathsFor(freightType === "MIXED" ? undefined : freightType).CANCEL_SCHEDULE(id),
|
|
{},
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
getAvailableLocomotives: async (routeId?: string): Promise<LocomotiveRecord[]> => {
|
|
if (routeId) {
|
|
const response = await client.get<LocomotiveRecord[]>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.AVAILABLE_LOCOMOTIVES,
|
|
{ params: { routeId } },
|
|
);
|
|
return unwrap(response.data);
|
|
}
|
|
const response = await client.get<LocomotiveRecord[]>(URL_CONSTANTS.LOCOMOTIVES.BASE, {
|
|
params: { status: 'AVAILABLE' },
|
|
});
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
previewReschedule: async (
|
|
scheduleId: string,
|
|
payload: {
|
|
incomingBookingIds: string[];
|
|
trigger: string;
|
|
reason?: string;
|
|
newDepartureDate?: string;
|
|
},
|
|
) => {
|
|
const response = await client.post(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.RESCHEDULE_PREVIEW(scheduleId),
|
|
payload,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
executeReschedule: async (
|
|
scheduleId: string,
|
|
payload: {
|
|
incomingBookingIds: string[];
|
|
trigger: string;
|
|
reason?: string;
|
|
newDepartureDate?: string;
|
|
finalBookingIds: string[];
|
|
displacedBookingIds: string[];
|
|
},
|
|
) => {
|
|
const response = await client.post(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.RESCHEDULE_EXECUTE(scheduleId),
|
|
payload,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
maintenanceReschedule: async (
|
|
scheduleId: string,
|
|
payload: {
|
|
incomingBookingIds: string[];
|
|
newDepartureDate: string;
|
|
reason?: string;
|
|
},
|
|
) => {
|
|
const response = await client.post(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.MAINTENANCE(scheduleId),
|
|
{ ...payload, trigger: "TRAIN_MAINTENANCE" },
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
getGlobalRules: async (): Promise<TrainSchedulingGlobalRules> => {
|
|
const response = await client.get<TrainSchedulingGlobalRules>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.GLOBAL_RULES,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
updateGlobalRules: async (
|
|
payload: Partial<
|
|
Pick<
|
|
TrainSchedulingGlobalRules,
|
|
| "maxTrainLengthMeters"
|
|
| "maxTrainWeightTons"
|
|
| "maxWagonsPerTrain"
|
|
| "max20ftContainerWeightTons"
|
|
| "max20ftPairWeightDiffTons"
|
|
>
|
|
>,
|
|
): Promise<TrainSchedulingGlobalRules> => {
|
|
const response = await client.patch<TrainSchedulingGlobalRules>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.GLOBAL_RULES,
|
|
payload,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
getStations: async (): Promise<YardOption[]> => {
|
|
const response = await client.get<BookingReferenceDataResponse>(
|
|
URL_CONSTANTS.BOOKINGS.REFERENCE_DATA,
|
|
);
|
|
const data = unwrap(response.data);
|
|
return (data.yard ?? []).map((yard) => ({
|
|
id: yard.id,
|
|
name: yard.name ?? yard.label ?? yard.code,
|
|
code: yard.code,
|
|
country: yard.country,
|
|
}));
|
|
},
|
|
|
|
removeWagonSlot: async (scheduleId: string, wagonId: string): Promise<TrainScheduleDetail> => {
|
|
const response = await client.delete<TrainScheduleDetail>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.REMOVE_WAGON_SLOT(scheduleId, wagonId),
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
updateContainerItem: async (
|
|
scheduleId: string,
|
|
itemId: string,
|
|
payload: { containerNumber: string | null },
|
|
): Promise<{ id: string; containerNumber: string | null }> => {
|
|
const response = await client.patch<{ id: string; containerNumber: string | null }>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.UPDATE_CONTAINER_ITEM(scheduleId, itemId),
|
|
payload,
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
getUnassignedBookings: async (
|
|
scheduleId: string,
|
|
): Promise<UnassignedBookingsResponse> => {
|
|
const response = await client.get<UnassignedBookingsResponse>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.UNASSIGNED_BOOKINGS(scheduleId),
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
|
|
getCompositionRemovals: async (
|
|
scheduleId: string,
|
|
): Promise<CompositionRemovalEntry[]> => {
|
|
const response = await client.get<CompositionRemovalEntry[]>(
|
|
URL_CONSTANTS.TRAIN_SCHEDULING.COMPOSITION_REMOVALS(scheduleId),
|
|
);
|
|
return unwrap(response.data);
|
|
},
|
|
};
|