Start time grid #62
18
frontend/package-lock.json
generated
18
frontend/package-lock.json
generated
@ -12,7 +12,8 @@
|
||||
"openapi-fetch": "^0.13.5",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-router": "^7.4.1"
|
||||
"react-router": "^7.4.1",
|
||||
"temporal-polyfill": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
@ -3726,6 +3727,21 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/temporal-polyfill": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.3.0.tgz",
|
||||
"integrity": "sha512-qNsTkX9K8hi+FHDfHmf22e/OGuXmfBm9RqNismxBrnSmZVJKegQ+HYYXT+R7Ha8F/YSm2Y34vmzD4cxMu2u95g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"temporal-spec": "0.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/temporal-spec": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.3.0.tgz",
|
||||
"integrity": "sha512-n+noVpIqz4hYgFSMOSiINNOUOMFtV5cZQNCmmszA6GiVFVRt3G7AqVyhXjhCSmowvQn+NsGn+jMDMKJYHd3bSQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
||||
|
||||
@ -16,7 +16,8 @@
|
||||
"openapi-fetch": "^0.13.5",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-router": "^7.4.1"
|
||||
"react-router": "^7.4.1",
|
||||
"temporal-polyfill": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useState, useMemo } from "react";
|
||||
import { Temporal } from "temporal-polyfill";
|
||||
|
||||
/** A booking with ISO 8601 datetime strings */
|
||||
export interface Booking {
|
||||
@ -41,30 +42,30 @@ export interface TimeSlot {
|
||||
|
||||
/** Parse ISO 8601 duration (e.g. "PT4H") to hours */
|
||||
function parseDurationToHours(duration: string): number {
|
||||
const match = duration.match(/PT(\d+)H/);
|
||||
return match ? parseInt(match[1], 10) : 4;
|
||||
return Temporal.Duration.from(duration).hours || 4;
|
||||
}
|
||||
|
||||
function timeToMinutes(time: string): number {
|
||||
const [hours, minutes] = time.split(":").map(Number);
|
||||
return hours * 60 + minutes;
|
||||
const t = Temporal.PlainTime.from(time);
|
||||
return t.hour * 60 + t.minute;
|
||||
}
|
||||
|
||||
function minutesToTime(minutes: number): string {
|
||||
const h = Math.floor(minutes / 60);
|
||||
const m = minutes % 60;
|
||||
return `${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")}`;
|
||||
return Temporal.PlainTime.from({
|
||||
hour: Math.floor(minutes / 60),
|
||||
minute: minutes % 60,
|
||||
}).toString().slice(0, 5);
|
||||
}
|
||||
|
||||
/** Check if a date string is today */
|
||||
function isToday(date: string): boolean {
|
||||
return date === new Date().toISOString().split("T")[0];
|
||||
return Temporal.PlainDate.from(date).equals(Temporal.Now.plainDateISO());
|
||||
}
|
||||
|
||||
/** Get current time in minutes since midnight */
|
||||
function getCurrentTimeMinutes(): number {
|
||||
const now = new Date();
|
||||
return now.getHours() * 60 + now.getMinutes();
|
||||
const now = Temporal.Now.plainTimeISO();
|
||||
return now.hour * 60 + now.minute;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,9 +105,10 @@ function calculateTimeSlots(
|
||||
let availableMinutes = Math.min(latestMinutes - mins, maxHours * 60);
|
||||
|
||||
for (const booking of room.bookings) {
|
||||
if (!booking.start.startsWith(date)) continue;
|
||||
const bookingStart = timeToMinutes(booking.start.split("T")[1]);
|
||||
const bookingEnd = timeToMinutes(booking.end.split("T")[1]);
|
||||
const bookingDateTime = Temporal.PlainDateTime.from(booking.start);
|
||||
if (bookingDateTime.toPlainDate().toString() !== date) continue;
|
||||
const bookingStart = timeToMinutes(bookingDateTime.toPlainTime().toString());
|
||||
const bookingEnd = timeToMinutes(Temporal.PlainDateTime.from(booking.end).toPlainTime().toString());
|
||||
|
||||
if (mins >= bookingStart && mins < bookingEnd) {
|
||||
availableMinutes = 0;
|
||||
@ -156,9 +158,10 @@ function findBestRoom(
|
||||
let roomAvailable = true;
|
||||
|
||||
for (const booking of room.bookings) {
|
||||
if (!booking.start.startsWith(date)) continue;
|
||||
const bookingStart = timeToMinutes(booking.start.split("T")[1]);
|
||||
const bookingEnd = timeToMinutes(booking.end.split("T")[1]);
|
||||
const bookingDateTime = Temporal.PlainDateTime.from(booking.start);
|
||||
if (bookingDateTime.toPlainDate().toString() !== date) continue;
|
||||
const bookingStart = timeToMinutes(bookingDateTime.toPlainTime().toString());
|
||||
const bookingEnd = timeToMinutes(Temporal.PlainDateTime.from(booking.end).toPlainTime().toString());
|
||||
|
||||
if (startMinutes >= bookingStart && startMinutes < bookingEnd) {
|
||||
roomAvailable = false;
|
||||
@ -221,9 +224,10 @@ function calculateEndTimeOptions(
|
||||
let maxEndMinutes = Math.min(startMinutes + maxHours * 60, latestMinutes);
|
||||
|
||||
for (const booking of room.bookings) {
|
||||
if (!booking.start.startsWith(date)) continue;
|
||||
const bookingStart = timeToMinutes(booking.start.split("T")[1]);
|
||||
const bookingEnd = timeToMinutes(booking.end.split("T")[1]);
|
||||
const bookingDateTime = Temporal.PlainDateTime.from(booking.start);
|
||||
if (bookingDateTime.toPlainDate().toString() !== date) continue;
|
||||
const bookingStart = timeToMinutes(bookingDateTime.toPlainTime().toString());
|
||||
const bookingEnd = timeToMinutes(Temporal.PlainDateTime.from(booking.end).toPlainTime().toString());
|
||||
|
||||
// If start time is during a booking, no options available
|
||||
if (startMinutes >= bookingStart && startMinutes < bookingEnd) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user