booking operations and trains scheduling also allocations

This commit is contained in:
marshal
2026-06-10 00:48:32 +03:00
parent 675975bc08
commit 5774d7db9d
180 changed files with 13423 additions and 3877 deletions

View File

@@ -0,0 +1,91 @@
import { compareSchedulingPriority } from "./compareSchedulingPriority";
export interface ThreeHourSchedulable {
id: string;
priorityScore?: number;
scheduledDate?: string;
preferredDepartureDate?: string;
isGovernment?: boolean;
}
export interface ThreeHourBookingBucket<T extends ThreeHourSchedulable = ThreeHourSchedulable> {
key: string;
label: string;
start: Date;
end: Date;
bookings: T[];
}
function bucketStart(date: Date): Date {
const start = new Date(date);
start.setUTCMinutes(0, 0, 0);
const hour = start.getUTCHours();
start.setUTCHours(Math.floor(hour / 3) * 3);
return start;
}
function formatBucketLabel(start: Date, end: Date): string {
const dateFmt = new Intl.DateTimeFormat("en-GB", {
day: "2-digit",
month: "short",
year: "numeric",
timeZone: "UTC",
});
const timeFmt = new Intl.DateTimeFormat("en-GB", {
hour: "2-digit",
minute: "2-digit",
hour12: false,
timeZone: "UTC",
});
return `${dateFmt.format(start)} · ${timeFmt.format(start)} ${timeFmt.format(end)} UTC`;
}
function getScheduledDate(booking: ThreeHourSchedulable): Date {
const raw = booking.scheduledDate ?? booking.preferredDepartureDate;
if (!raw) return new Date(0);
return new Date(raw);
}
function toPriorityRow(booking: ThreeHourSchedulable) {
return {
isGovernment: booking.isGovernment,
priorityScore: booking.priorityScore,
scheduledDate: booking.scheduledDate ?? booking.preferredDepartureDate ?? "",
};
}
export function groupBookingsByThreeHourWindow<T extends ThreeHourSchedulable>(
bookings: T[],
): ThreeHourBookingBucket<T>[] {
const map = new Map<string, ThreeHourBookingBucket<T>>();
for (const booking of bookings) {
const scheduled = getScheduledDate(booking);
const start = bucketStart(scheduled);
const end = new Date(start);
end.setUTCHours(end.getUTCHours() + 3);
const key = start.toISOString();
const existing = map.get(key);
if (existing) {
existing.bookings.push(booking);
} else {
map.set(key, {
key,
label: formatBucketLabel(start, end),
start,
end,
bookings: [booking],
});
}
}
return [...map.values()]
.map((bucket) => ({
...bucket,
bookings: [...bucket.bookings].sort((a, b) =>
compareSchedulingPriority(toPriorityRow(a), toPriorityRow(b)),
),
}))
.sort((a, b) => a.start.getTime() - b.start.getTime());
}