Start time grid #62

Merged
stne3960 merged 30 commits from start_time_grid into main 2026-01-16 14:17:09 +01:00
3 changed files with 42 additions and 21 deletions
Showing only changes of commit 0fc2f7c0d8 - Show all commits

View File

@ -12,7 +12,8 @@
"openapi-fetch": "^0.13.5", "openapi-fetch": "^0.13.5",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-router": "^7.4.1" "react-router": "^7.4.1",
"temporal-polyfill": "^0.3.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.21.0", "@eslint/js": "^9.21.0",
@ -3726,6 +3727,21 @@
"url": "https://opencollective.com/webpack" "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": { "node_modules/tinyglobby": {
"version": "0.2.14", "version": "0.2.14",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",

View File

@ -16,7 +16,8 @@
"openapi-fetch": "^0.13.5", "openapi-fetch": "^0.13.5",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-router": "^7.4.1" "react-router": "^7.4.1",
"temporal-polyfill": "^0.3.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.21.0", "@eslint/js": "^9.21.0",

View File

@ -1,4 +1,5 @@
import { useState, useMemo } from "react"; import { useState, useMemo } from "react";
import { Temporal } from "temporal-polyfill";
/** A booking with ISO 8601 datetime strings */ /** A booking with ISO 8601 datetime strings */
export interface Booking { export interface Booking {
@ -41,30 +42,30 @@ export interface TimeSlot {
/** Parse ISO 8601 duration (e.g. "PT4H") to hours */ /** Parse ISO 8601 duration (e.g. "PT4H") to hours */
function parseDurationToHours(duration: string): number { function parseDurationToHours(duration: string): number {
const match = duration.match(/PT(\d+)H/); return Temporal.Duration.from(duration).hours || 4;
return match ? parseInt(match[1], 10) : 4;
} }
function timeToMinutes(time: string): number { function timeToMinutes(time: string): number {
const [hours, minutes] = time.split(":").map(Number); const t = Temporal.PlainTime.from(time);
return hours * 60 + minutes; return t.hour * 60 + t.minute;
} }
function minutesToTime(minutes: number): string { function minutesToTime(minutes: number): string {
const h = Math.floor(minutes / 60); return Temporal.PlainTime.from({
const m = minutes % 60; hour: Math.floor(minutes / 60),
return `${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")}`; minute: minutes % 60,
}).toString().slice(0, 5);
} }
/** Check if a date string is today */ /** Check if a date string is today */
function isToday(date: string): boolean { 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 */ /** Get current time in minutes since midnight */
function getCurrentTimeMinutes(): number { function getCurrentTimeMinutes(): number {
const now = new Date(); const now = Temporal.Now.plainTimeISO();
return now.getHours() * 60 + now.getMinutes(); return now.hour * 60 + now.minute;
} }
/** /**
@ -104,9 +105,10 @@ function calculateTimeSlots(
let availableMinutes = Math.min(latestMinutes - mins, maxHours * 60); let availableMinutes = Math.min(latestMinutes - mins, maxHours * 60);
for (const booking of room.bookings) { for (const booking of room.bookings) {
if (!booking.start.startsWith(date)) continue; const bookingDateTime = Temporal.PlainDateTime.from(booking.start);
const bookingStart = timeToMinutes(booking.start.split("T")[1]); if (bookingDateTime.toPlainDate().toString() !== date) continue;
const bookingEnd = timeToMinutes(booking.end.split("T")[1]); const bookingStart = timeToMinutes(bookingDateTime.toPlainTime().toString());
const bookingEnd = timeToMinutes(Temporal.PlainDateTime.from(booking.end).toPlainTime().toString());
if (mins >= bookingStart && mins < bookingEnd) { if (mins >= bookingStart && mins < bookingEnd) {
availableMinutes = 0; availableMinutes = 0;
@ -156,9 +158,10 @@ function findBestRoom(
let roomAvailable = true; let roomAvailable = true;
for (const booking of room.bookings) { for (const booking of room.bookings) {
if (!booking.start.startsWith(date)) continue; const bookingDateTime = Temporal.PlainDateTime.from(booking.start);
const bookingStart = timeToMinutes(booking.start.split("T")[1]); if (bookingDateTime.toPlainDate().toString() !== date) continue;
const bookingEnd = timeToMinutes(booking.end.split("T")[1]); const bookingStart = timeToMinutes(bookingDateTime.toPlainTime().toString());
const bookingEnd = timeToMinutes(Temporal.PlainDateTime.from(booking.end).toPlainTime().toString());
if (startMinutes >= bookingStart && startMinutes < bookingEnd) { if (startMinutes >= bookingStart && startMinutes < bookingEnd) {
roomAvailable = false; roomAvailable = false;
@ -221,9 +224,10 @@ function calculateEndTimeOptions(
let maxEndMinutes = Math.min(startMinutes + maxHours * 60, latestMinutes); let maxEndMinutes = Math.min(startMinutes + maxHours * 60, latestMinutes);
for (const booking of room.bookings) { for (const booking of room.bookings) {
if (!booking.start.startsWith(date)) continue; const bookingDateTime = Temporal.PlainDateTime.from(booking.start);
const bookingStart = timeToMinutes(booking.start.split("T")[1]); if (bookingDateTime.toPlainDate().toString() !== date) continue;
const bookingEnd = timeToMinutes(booking.end.split("T")[1]); 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 start time is during a booking, no options available
if (startMinutes >= bookingStart && startMinutes < bookingEnd) { if (startMinutes >= bookingStart && startMinutes < bookingEnd) {