mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 13:10:56 +00:00
92 lines
2.4 KiB
TypeScript
92 lines
2.4 KiB
TypeScript
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());
|
||
}
|