From 85afc8c8401f11bb2382a191c9180d7161435926 Mon Sep 17 00:00:00 2001 From: iamkiddy Date: Tue, 25 Nov 2025 20:38:37 +0000 Subject: [PATCH] Enhance Booking component for admin availability management. Implement time range selection for each day, load saved time ranges from localStorage, and validate time inputs. Update API integration to ensure available_days is an array of numbers and improve error handling for availability updates. Refactor UI for better user experience in managing weekly availability. --- app/(admin)/admin/booking/page.tsx | 414 ++++++++++++++++++++--------- lib/actions/appointments.ts | 41 ++- 2 files changed, 325 insertions(+), 130 deletions(-) diff --git a/app/(admin)/admin/booking/page.tsx b/app/(admin)/admin/booking/page.tsx index 7a85db0..86c8c98 100644 --- a/app/(admin)/admin/booking/page.tsx +++ b/app/(admin)/admin/booking/page.tsx @@ -50,11 +50,10 @@ export default function Booking() { const isDark = theme === "dark"; // Availability management - const { adminAvailability, isLoadingAdminAvailability, updateAdminAvailability, isUpdatingAvailability } = useAppointments(); + const { adminAvailability, isLoadingAdminAvailability, updateAvailability, isUpdatingAvailability, refetchAdminAvailability } = useAppointments(); const [selectedDays, setSelectedDays] = useState([]); const [availabilityDialogOpen, setAvailabilityDialogOpen] = useState(false); - const [startTime, setStartTime] = useState("09:00"); - const [endTime, setEndTime] = useState("17:00"); + const [dayTimeRanges, setDayTimeRanges] = useState>({}); const daysOfWeek = [ { value: 0, label: "Monday" }, @@ -66,10 +65,48 @@ export default function Booking() { { value: 6, label: "Sunday" }, ]; - // Initialize selected days when availability is loaded + // Load time ranges from localStorage on mount + useEffect(() => { + const savedTimeRanges = localStorage.getItem("adminAvailabilityTimeRanges"); + if (savedTimeRanges) { + try { + const parsed = JSON.parse(savedTimeRanges); + setDayTimeRanges(parsed); + } catch (error) { + console.error("Failed to parse saved time ranges:", error); + } + } + }, []); + + // Initialize selected days and time ranges when availability is loaded useEffect(() => { if (adminAvailability?.available_days) { setSelectedDays(adminAvailability.available_days); + // Load saved time ranges or use defaults + const savedTimeRanges = localStorage.getItem("adminAvailabilityTimeRanges"); + let initialRanges: Record = {}; + + if (savedTimeRanges) { + try { + const parsed = JSON.parse(savedTimeRanges); + // Only use saved ranges for days that are currently available + adminAvailability.available_days.forEach((day) => { + initialRanges[day] = parsed[day] || { startTime: "09:00", endTime: "17:00" }; + }); + } catch (error) { + // If parsing fails, use defaults + adminAvailability.available_days.forEach((day) => { + initialRanges[day] = { startTime: "09:00", endTime: "17:00" }; + }); + } + } else { + // No saved ranges, use defaults + adminAvailability.available_days.forEach((day) => { + initialRanges[day] = { startTime: "09:00", endTime: "17:00" }; + }); + } + + setDayTimeRanges(initialRanges); } }, [adminAvailability]); @@ -88,9 +125,40 @@ export default function Booking() { const timeSlotsForPicker = generateTimeSlots(); const handleDayToggle = (day: number) => { - setSelectedDays((prev) => - prev.includes(day) ? prev.filter((d) => d !== day) : [...prev, day].sort() - ); + setSelectedDays((prev) => { + const newDays = prev.includes(day) + ? prev.filter((d) => d !== day) + : [...prev, day].sort(); + + // Initialize time range for newly added day + if (!prev.includes(day) && !dayTimeRanges[day]) { + setDayTimeRanges((prevRanges) => ({ + ...prevRanges, + [day]: { startTime: "09:00", endTime: "17:00" }, + })); + } + + // Remove time range for removed day + if (prev.includes(day)) { + setDayTimeRanges((prevRanges) => { + const newRanges = { ...prevRanges }; + delete newRanges[day]; + return newRanges; + }); + } + + return newDays; + }); + }; + + const handleTimeRangeChange = (day: number, field: "startTime" | "endTime", value: string) => { + setDayTimeRanges((prev) => ({ + ...prev, + [day]: { + ...prev[day], + [field]: value, + }, + })); }; const handleSaveAvailability = async () => { @@ -99,14 +167,32 @@ export default function Booking() { return; } - if (startTime >= endTime) { - toast.error("End time must be after start time"); - return; + // Validate all time ranges + for (const day of selectedDays) { + const timeRange = dayTimeRanges[day]; + if (!timeRange || !timeRange.startTime || !timeRange.endTime) { + toast.error(`Please set time range for ${daysOfWeek.find(d => d.value === day)?.label}`); + return; + } + if (timeRange.startTime >= timeRange.endTime) { + toast.error(`End time must be after start time for ${daysOfWeek.find(d => d.value === day)?.label}`); + return; + } } try { - await updateAdminAvailability({ available_days: selectedDays }); + // Ensure selectedDays is an array of numbers + const daysToSave = selectedDays.map(day => Number(day)).sort(); + await updateAvailability({ available_days: daysToSave }); + + // Save time ranges to localStorage + localStorage.setItem("adminAvailabilityTimeRanges", JSON.stringify(dayTimeRanges)); + toast.success("Availability updated successfully!"); + // Refresh availability data + if (refetchAdminAvailability) { + await refetchAdminAvailability(); + } setAvailabilityDialogOpen(false); } catch (error) { console.error("Failed to update availability:", error); @@ -118,6 +204,12 @@ export default function Booking() { const handleOpenAvailabilityDialog = () => { if (adminAvailability?.available_days) { setSelectedDays(adminAvailability.available_days); + // Initialize time ranges for each day + const initialRanges: Record = {}; + adminAvailability.available_days.forEach((day) => { + initialRanges[day] = dayTimeRanges[day] || { startTime: "09:00", endTime: "17:00" }; + }); + setDayTimeRanges(initialRanges); } setAvailabilityDialogOpen(true); }; @@ -334,6 +426,60 @@ export default function Booking() { + {/* Available Days Display Card */} + {adminAvailability && ( +
+
+
+ +
+
+

+ Weekly Availability +

+ {adminAvailability.available_days_display && adminAvailability.available_days_display.length > 0 ? ( +
+ {adminAvailability.available_days.map((dayNum, index) => { + const dayName = daysOfWeek.find(d => d.value === dayNum)?.label || adminAvailability.available_days_display[index]; + const timeRange = dayTimeRanges[dayNum] || { startTime: "09:00", endTime: "17:00" }; + return ( +
+ + {dayName} + + ({new Date(`2000-01-01T${timeRange.startTime}`).toLocaleTimeString("en-US", { + hour: "numeric", + minute: "2-digit", + hour12: true, + })}{" "} + -{" "} + {new Date(`2000-01-01T${timeRange.endTime}`).toLocaleTimeString("en-US", { + hour: "numeric", + minute: "2-digit", + hour12: true, + })}) + +
+ ); + })} +
+ ) : ( +

+ No availability set. Click "Manage Availability" to set your available days. +

+ )} +
+
+
+ )} + {loading ? (
@@ -682,131 +828,145 @@ export default function Booking() {
) : ( <> - {/* Current Availability Display */} - {adminAvailability?.available_days_display && adminAvailability.available_days_display.length > 0 && ( -
-

- Current availability: {adminAvailability.available_days_display.join(", ")} -

-
- )} - - {/* Days Selection */} -
- -

- Select the days of the week when you accept appointment requests -

-
- {daysOfWeek.map((day) => ( - - ))} -
-
- - {/* Time Selection */} + {/* Days Selection with Time Ranges */}

- Set the time range when appointments can be scheduled + Select days and set time ranges for each day

- -
- {/* Start Time */} -
- - -
- {/* End Time */} -
- - -
-
+
+ {isSelected && } +
+ + {day.label} + + +
- {/* Time Range Display */} -
-

- Available hours:{" "} - {new Date(`2000-01-01T${startTime}`).toLocaleTimeString("en-US", { - hour: "numeric", - minute: "2-digit", - hour12: true, - })}{" "} - -{" "} - {new Date(`2000-01-01T${endTime}`).toLocaleTimeString("en-US", { - hour: "numeric", - minute: "2-digit", - hour12: true, - })} -

+ {isSelected && ( +
+
+ {/* Start Time */} +
+ + +
+ + {/* End Time */} +
+ + +
+
+ + {/* Time Range Preview */} +
+ {new Date(`2000-01-01T${timeRange.startTime}`).toLocaleTimeString("en-US", { + hour: "numeric", + minute: "2-digit", + hour12: true, + })}{" "} + -{" "} + {new Date(`2000-01-01T${timeRange.endTime}`).toLocaleTimeString("en-US", { + hour: "numeric", + minute: "2-digit", + hour12: true, + })} +
+
+ )} +
+ ); + })} @@ -829,7 +989,7 @@ export default function Booking() {