From 9930789d714e8b18b48ca442f80a9259dba42fd2 Mon Sep 17 00:00:00 2001 From: iamkiddy Date: Wed, 3 Dec 2025 18:27:09 +0000 Subject: [PATCH] Refactor appointment detail and booking components to enhance the display of selected time slots. Implement logic to fetch and merge selected slots from the appointment list, improving data handling and user experience. Update UI to group slots by date for better clarity and organization. --- app/(admin)/admin/booking/[id]/page.tsx | 122 +++++++++++++-------- app/(admin)/admin/booking/page.tsx | 43 ++++++-- app/(user)/user/appointments/[id]/page.tsx | 122 +++++++++++++-------- 3 files changed, 190 insertions(+), 97 deletions(-) diff --git a/app/(admin)/admin/booking/[id]/page.tsx b/app/(admin)/admin/booking/[id]/page.tsx index 3ec4ccb..c101ee4 100644 --- a/app/(admin)/admin/booking/[id]/page.tsx +++ b/app/(admin)/admin/booking/[id]/page.tsx @@ -20,7 +20,7 @@ import { MapPin, } from "lucide-react"; import { useAppTheme } from "@/components/ThemeProvider"; -import { getAppointmentDetail, scheduleAppointment, rejectAppointment } from "@/lib/actions/appointments"; +import { getAppointmentDetail, scheduleAppointment, rejectAppointment, listAppointments } from "@/lib/actions/appointments"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -58,8 +58,23 @@ export default function AppointmentDetailPage() { setLoading(true); try { - const data = await getAppointmentDetail(appointmentId); - setAppointment(data); + // Fetch both detail and list to get selected_slots from list endpoint + const [detailData, listData] = await Promise.all([ + getAppointmentDetail(appointmentId), + listAppointments().catch(() => []) // Fallback to empty array if list fails + ]); + + // Find matching appointment in list to get selected_slots + const listAppointment = Array.isArray(listData) + ? listData.find((apt: Appointment) => apt.id === appointmentId) + : null; + + // Merge selected_slots from list into detail data + if (listAppointment && listAppointment.selected_slots && Array.isArray(listAppointment.selected_slots) && listAppointment.selected_slots.length > 0) { + detailData.selected_slots = listAppointment.selected_slots; + } + + setAppointment(detailData); } catch (error) { toast.error("Failed to load appointment details"); router.push("/admin/booking"); @@ -367,7 +382,7 @@ export default function AppointmentDetailPage() { )} - {/* Selected Slots (replacing Matching Slots) */} + {/* Selected Slots */} {appointment.selected_slots && Array.isArray(appointment.selected_slots) && appointment.selected_slots.length > 0 && (
@@ -382,46 +397,65 @@ export default function AppointmentDetailPage() {
-
- {appointment.selected_slots.map((slot: any, idx: number) => { - const dayNames: Record = { - 0: "Monday", - 1: "Tuesday", - 2: "Wednesday", - 3: "Thursday", - 4: "Friday", - 5: "Saturday", - 6: "Sunday", - }; - const timeSlotLabels: Record = { - morning: "Morning", - afternoon: "Lunchtime", - evening: "Evening", - }; - const dayName = dayNames[slot.day] || `Day ${slot.day}`; - const timeSlot = String(slot.time_slot).toLowerCase().trim(); - const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot; - - return ( -
-

- {dayName} -

-

- {timeLabel} -

- {slot.date && ( -

- {formatShortDate(slot.date)} -

- )} -
- ); - })} -
+ {(() => { + const dayNames: Record = { + 0: "Monday", + 1: "Tuesday", + 2: "Wednesday", + 3: "Thursday", + 4: "Friday", + 5: "Saturday", + 6: "Sunday", + }; + const timeSlotLabels: Record = { + morning: "Morning", + afternoon: "Lunchtime", + evening: "Evening", + }; + + // Group slots by date + const slotsByDate: Record = {}; + appointment.selected_slots.forEach((slot: any) => { + const date = slot.date || ""; + if (!slotsByDate[date]) { + slotsByDate[date] = []; + } + slotsByDate[date].push(slot); + }); + + return ( +
+ {Object.entries(slotsByDate).map(([date, slots]) => ( +
+
+

+ {formatShortDate(date)} +

+ {slots.length > 0 && slots[0]?.day !== undefined && ( +

+ {dayNames[slots[0].day] || `Day ${slots[0].day}`} +

+ )} +
+
+ {slots.map((slot: any, idx: number) => { + const timeSlot = String(slot.time_slot).toLowerCase().trim(); + const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot; + return ( + + {timeLabel} + + ); + })} +
+
+ ))} +
+ ); + })()}
)} diff --git a/app/(admin)/admin/booking/page.tsx b/app/(admin)/admin/booking/page.tsx index 532ba43..49abfc8 100644 --- a/app/(admin)/admin/booking/page.tsx +++ b/app/(admin)/admin/booking/page.tsx @@ -680,27 +680,52 @@ export default function Booking() { {(() => { - // Handle preferred_dates + const dayNames: Record = { + 0: "Monday", + 1: "Tuesday", + 2: "Wednesday", + 3: "Thursday", + 4: "Friday", + 5: "Saturday", + 6: "Sunday", + }; + const timeSlotLabels: Record = { + morning: "Morning", + afternoon: "Lunchtime", + evening: "Evening", + }; + + // Show selected_slots if available + if (appointment.selected_slots && Array.isArray(appointment.selected_slots) && appointment.selected_slots.length > 0) { + return ( +
+ {appointment.selected_slots.slice(0, 2).map((slot, idx) => ( + + {dayNames[slot.day] || `Day ${slot.day}`} - {timeSlotLabels[slot.time_slot] || slot.time_slot} + + ))} + {appointment.selected_slots.length > 2 && ( + + +{appointment.selected_slots.length - 2} more + + )} +
+ ); + } + + // Fallback to preferred_dates and preferred_time_slots if selected_slots not available const dates = Array.isArray(appointment.preferred_dates) ? appointment.preferred_dates : appointment.preferred_dates ? [appointment.preferred_dates] : []; - // Handle preferred_time_slots const timeSlots = Array.isArray(appointment.preferred_time_slots) ? appointment.preferred_time_slots : appointment.preferred_time_slots ? [appointment.preferred_time_slots] : []; - // Time slot labels - const timeSlotLabels: Record = { - morning: "Morning", - afternoon: "Lunchtime", - evening: "Evening", - }; - if (dates.length === 0 && timeSlots.length === 0) { return -; } diff --git a/app/(user)/user/appointments/[id]/page.tsx b/app/(user)/user/appointments/[id]/page.tsx index 1330fdd..a15cf6c 100644 --- a/app/(user)/user/appointments/[id]/page.tsx +++ b/app/(user)/user/appointments/[id]/page.tsx @@ -18,7 +18,7 @@ import { Copy, } from "lucide-react"; import { useAppTheme } from "@/components/ThemeProvider"; -import { getAppointmentDetail } from "@/lib/actions/appointments"; +import { getAppointmentDetail, listAppointments } from "@/lib/actions/appointments"; import { Button } from "@/components/ui/button"; import { Navbar } from "@/components/Navbar"; import { toast } from "sonner"; @@ -40,8 +40,23 @@ export default function UserAppointmentDetailPage() { setLoading(true); try { - const data = await getAppointmentDetail(appointmentId); - setAppointment(data); + // Fetch both detail and list to get selected_slots from list endpoint + const [detailData, listData] = await Promise.all([ + getAppointmentDetail(appointmentId), + listAppointments().catch(() => []) // Fallback to empty array if list fails + ]); + + // Find matching appointment in list to get selected_slots + const listAppointment = Array.isArray(listData) + ? listData.find((apt: Appointment) => apt.id === appointmentId) + : null; + + // Merge selected_slots from list into detail data + if (listAppointment && listAppointment.selected_slots && Array.isArray(listAppointment.selected_slots) && listAppointment.selected_slots.length > 0) { + detailData.selected_slots = listAppointment.selected_slots; + } + + setAppointment(detailData); } catch (error) { toast.error("Failed to load appointment details"); router.push("/user/dashboard"); @@ -306,7 +321,7 @@ export default function UserAppointmentDetailPage() { )} - {/* Selected Slots (replacing Matching Slots) */} + {/* Selected Slots */} {appointment.selected_slots && Array.isArray(appointment.selected_slots) && appointment.selected_slots.length > 0 && (
@@ -321,46 +336,65 @@ export default function UserAppointmentDetailPage() {
-
- {appointment.selected_slots.map((slot: any, idx: number) => { - const dayNames: Record = { - 0: "Monday", - 1: "Tuesday", - 2: "Wednesday", - 3: "Thursday", - 4: "Friday", - 5: "Saturday", - 6: "Sunday", - }; - const timeSlotLabels: Record = { - morning: "Morning", - afternoon: "Lunchtime", - evening: "Evening", - }; - const dayName = dayNames[slot.day] || `Day ${slot.day}`; - const timeSlot = String(slot.time_slot).toLowerCase().trim(); - const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot; - - return ( -
-

- {dayName} -

-

- {timeLabel} -

- {slot.date && ( -

- {formatShortDate(slot.date)} -

- )} -
- ); - })} -
+ {(() => { + const dayNames: Record = { + 0: "Monday", + 1: "Tuesday", + 2: "Wednesday", + 3: "Thursday", + 4: "Friday", + 5: "Saturday", + 6: "Sunday", + }; + const timeSlotLabels: Record = { + morning: "Morning", + afternoon: "Lunchtime", + evening: "Evening", + }; + + // Group slots by date + const slotsByDate: Record = {}; + appointment.selected_slots.forEach((slot: any) => { + const date = slot.date || ""; + if (!slotsByDate[date]) { + slotsByDate[date] = []; + } + slotsByDate[date].push(slot); + }); + + return ( +
+ {Object.entries(slotsByDate).map(([date, slots]) => ( +
+
+

+ {formatShortDate(date)} +

+ {slots.length > 0 && slots[0]?.day !== undefined && ( +

+ {dayNames[slots[0].day] || `Day ${slots[0].day}`} +

+ )} +
+
+ {slots.map((slot: any, idx: number) => { + const timeSlot = String(slot.time_slot).toLowerCase().trim(); + const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot; + return ( + + {timeLabel} + + ); + })} +
+
+ ))} +
+ ); + })()}
)}