From 827b5d9b3ddd5d0f8a9da7fe0b94bfb90e1e3808 Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Wed, 15 Oct 2025 21:25:26 +0100 Subject: [PATCH] break calendar --- frontend/src/routes/book/+page.svelte | 83 ++++++++++++++++++++------- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/frontend/src/routes/book/+page.svelte b/frontend/src/routes/book/+page.svelte index 3f425c9..cfc9019 100644 --- a/frontend/src/routes/book/+page.svelte +++ b/frontend/src/routes/book/+page.svelte @@ -94,17 +94,24 @@ // Initialize date boundaries const today = new Date(); + const tomorrow = new Date(today); + tomorrow.setDate(today.getDate() + 1); const maxDate = new Date(); maxDate.setMonth(today.getMonth() + 6); // Create CalendarDate objects directly without toCalendar conversion const minDate = new CalendarDate(today.getFullYear(), today.getMonth() + 1, today.getDate()); + const tomorrowDate = new CalendarDate( + tomorrow.getFullYear(), + tomorrow.getMonth() + 1, + tomorrow.getDate() + ); const maxCalendarDate = new CalendarDate( maxDate.getFullYear(), maxDate.getMonth() + 1, maxDate.getDate() ); - let placeholder = $state(minDate); + let placeholder = $state(tomorrowDate); $effect(() => { const monthKey = `${placeholder.year}-${String(placeholder.month).padStart(2, '0')}`; if (!workingHoursCache.has(monthKey)) { @@ -116,7 +123,10 @@ async function fetchWorkingHoursForMonth(date: CalendarDate) { const monthKey = `${date.year}-${String(date.month).padStart(2, '0')}`; if (workingHoursCache.has(monthKey)) { - workingHours = workingHoursCache.get(monthKey)!; + // Use a timeout to ensure state updates properly + setTimeout(() => { + workingHours = workingHoursCache.get(monthKey)!; + }, 0); return; } @@ -176,15 +186,16 @@ } } - // Set default selected date to next available working day + // Set default selected date to next available working day (starting from tomorrow) function setDefaultSelectedDate( hoursMap: Record ) { const currentDate = new Date(); let nextDate = new Date(currentDate); - // Check next 30 days for available working day - for (let i = 0; i < 30; i++) { + // Check next 30 days for available working day (starting from tomorrow, i=1) + for (let i = 1; i < 30; i++) { + nextDate = new Date(currentDate); nextDate.setDate(currentDate.getDate() + i); const dateStr = nextDate.toISOString().split('T')[0]; @@ -198,9 +209,15 @@ } } - // Fallback to today if no working day found + // Fallback to tomorrow if no working day found if (!selectedDate) { - selectedDate = minDate; + const tomorrow = new Date(); + tomorrow.setDate(tomorrow.getDate() + 1); + selectedDate = new CalendarDate( + tomorrow.getFullYear(), + tomorrow.getMonth() + 1, + tomorrow.getDate() + ); } } @@ -248,7 +265,7 @@ } // Initialize with current month - fetchWorkingHoursForMonth(minDate); + fetchWorkingHoursForMonth(tomorrowDate); // Handle calendar month change function handleMonthChange(newMonth: CalendarDate) { @@ -279,37 +296,63 @@ // If no data for this date, assume unavailable if (!dayHours) return true; - return !dayHours.isOpen; + // If closed, mark as unavailable + if (!dayHours.isOpen) return true; + + // Check if there are any available time slots for the selected services + if (selectedServices.length > 0) { + const duration = getTotalDuration(); + const slots = generateTimeSlots(duration, date); + // If no slots available for the required duration, mark as unavailable + if (slots.length === 0) return true; + } + + return false; } - function generateTimeSlots(duration: number, date: CalendarDate | undefined) { - if (!date || !workingHours) return []; + function generateTimeSlots(duration: number, date: CalendarDate | undefined): string[] { + if (!date || !workingHours) { + return []; + } const dateStr = date.toString(); const dayHours = workingHours[dateStr]; - // If no working hours data or closed, return empty - if (!dayHours || !dayHours.isOpen) return []; + if (!dayHours || !dayHours.isOpen) { + return []; + } + + const slots: string[] = []; - const slots = []; const [startHour, startMinute] = dayHours.startTime.split(':').map(Number); const [endHour, endMinute] = dayHours.endTime.split(':').map(Number); - // Convert to minutes for easier calculation - const startTotalMinutes = startHour * 60 + startMinute; + let startTotalMinutes = startHour * 60 + startMinute; const endTotalMinutes = endHour * 60 + endMinute; - // Generate 15-minute intervals + const now = new Date(); + + const today = new CalendarDate(now.getFullYear(), now.getMonth() + 1, now.getDate()); + const isToday = date.compare(today) === 0; + + if (isToday) { + const currentMinutes = now.getHours() * 60 + now.getMinutes(); + const minimumStartMinutes = currentMinutes + 120; // Current time + 2 hours + + startTotalMinutes = Math.max(startTotalMinutes, minimumStartMinutes); + } + for (let minutes = startTotalMinutes; minutes < endTotalMinutes; minutes += 15) { const slotEndMinutes = minutes + duration; - // Skip if slot would end after closing time - if (slotEndMinutes > endTotalMinutes) continue; + if (slotEndMinutes > endTotalMinutes) { + continue; + } const hour = Math.floor(minutes / 60); const minute = minutes % 60; - const timeStr = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`; + const timeStr = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`; slots.push(timeStr); }