diff --git a/app/explore/experts/[consultantId]/components/WeeklyAvailability.tsx b/app/explore/experts/[consultantId]/components/WeeklyAvailability.tsx index 8e431a1..089a178 100644 --- a/app/explore/experts/[consultantId]/components/WeeklyAvailability.tsx +++ b/app/explore/experts/[consultantId]/components/WeeklyAvailability.tsx @@ -20,11 +20,44 @@ function convertUTCToLocal(date: Date): Date { return new Date(date.getTime() - (offset * 60 * 1000)); } +function getDayBefore(day: DayOfWeek): DayOfWeek { + const days = [ + DayOfWeek.SUNDAY, + DayOfWeek.MONDAY, + DayOfWeek.TUESDAY, + DayOfWeek.WEDNESDAY, + DayOfWeek.THURSDAY, + DayOfWeek.FRIDAY, + DayOfWeek.SATURDAY + ]; + const index = days.indexOf(day); + return days[(index - 1 + 7) % 7]; +} + +function getDayAfter(day: DayOfWeek): DayOfWeek { + const days = [ + DayOfWeek.SUNDAY, + DayOfWeek.MONDAY, + DayOfWeek.TUESDAY, + DayOfWeek.WEDNESDAY, + DayOfWeek.THURSDAY, + DayOfWeek.FRIDAY, + DayOfWeek.SATURDAY + ]; + const index = days.indexOf(day); + return days[(index + 1) % 7]; +} + const formatTime = (isoString: string): string => { try { - // Convert UTC time to local time + // Create a base date for today + const baseDate = new Date(); + // Parse the time from the ISO string const utcDate = new Date(isoString); - const localDate = convertUTCToLocal(utcDate); + // Set the hours and minutes on the base date + baseDate.setHours(utcDate.getUTCHours(), utcDate.getUTCMinutes(), 0, 0); + // Convert to local time + const localDate = convertUTCToLocal(baseDate); return localDate.toLocaleTimeString([], { hour: "2-digit", @@ -43,27 +76,100 @@ export const WeeklyAvailability: React.FC = ({ selectedSlotId, }) => { const daysOfWeek = [ - "MONDAY", - "TUESDAY", - "WEDNESDAY", - "THURSDAY", - "FRIDAY", - "SATURDAY", - "SUNDAY", + DayOfWeek.MONDAY, + DayOfWeek.TUESDAY, + DayOfWeek.WEDNESDAY, + DayOfWeek.THURSDAY, + DayOfWeek.FRIDAY, + DayOfWeek.SATURDAY, + DayOfWeek.SUNDAY, ]; const dayLabels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; // Group slots by day and sort by time - const slotsByDay = daysOfWeek.map((day) => ({ - day, - slots: slots - .filter((slot) => slot.dayOfWeekforStartTimeInUTC === day) - .sort((a, b) => { - const timeA = new Date(a.slotStartTimeInUTC).getTime(); - const timeB = new Date(b.slotStartTimeInUTC).getTime(); - return timeA - timeB; - }), - })); + const slotsByDay = daysOfWeek.map((day) => { + // Get slots that: + // 1. Start on this day + // 2. End on this day (started previous day) + // 3. Start on this day and end next day + const daySlots = slots.filter((slot) => { + const previousDay = getDayBefore(day); + const nextDay = getDayAfter(day); + + // Include slots that: + // 1. Start on this day + // 2. End on this day (started previous day) + // 3. Start on this day and end next day + // 4. Start on previous day and end on next day (crosses entire day) + return ( + slot.dayOfWeekforStartTimeInUTC === day || + slot.dayOfWeekforEndTimeInUTC === day || + (slot.dayOfWeekforStartTimeInUTC === previousDay && + slot.dayOfWeekforEndTimeInUTC === nextDay) + ); + }); + + // For each slot, determine if it needs to be split at midnight + const processedSlots = daySlots.flatMap((slot) => { + // Create a base date for today + const baseDate = new Date(); + // Parse the times from the ISO strings + const startTime = new Date(slot.slotStartTimeInUTC); + const endTime = new Date(slot.slotEndTimeInUTC); + + // Set the hours and minutes on the base date + const localStartTime = new Date(baseDate); + localStartTime.setHours(startTime.getUTCHours(), startTime.getUTCMinutes(), 0, 0); + + const localEndTime = new Date(baseDate); + localEndTime.setHours(endTime.getUTCHours(), endTime.getUTCMinutes(), 0, 0); + + // Convert to local time + const convertedStartTime = convertUTCToLocal(localStartTime); + const convertedEndTime = convertUTCToLocal(localEndTime); + + // If the slot crosses midnight + if (convertedEndTime < convertedStartTime) { + // Create two slots: one ending at midnight, one starting at midnight + const midnightEnd = new Date(convertedStartTime); + midnightEnd.setHours(23, 59, 59, 999); + + const midnightStart = new Date(convertedEndTime); + midnightStart.setHours(0, 0, 0, 0); + + return [ + { + ...slot, + slotStartTimeInUTC: convertedStartTime.toISOString(), + slotEndTimeInUTC: midnightEnd.toISOString(), + }, + { + ...slot, + slotStartTimeInUTC: midnightStart.toISOString(), + slotEndTimeInUTC: convertedEndTime.toISOString(), + }, + ]; + } + + return [{ + ...slot, + slotStartTimeInUTC: convertedStartTime.toISOString(), + slotEndTimeInUTC: convertedEndTime.toISOString(), + }]; + }); + + // Sort slots by start time + const sortedSlots = processedSlots.sort((a, b) => { + const timeA = new Date(a.slotStartTimeInUTC).getTime(); + const timeB = new Date(b.slotStartTimeInUTC).getTime(); + return timeA - timeB; + }); + + return { + day, + slots: sortedSlots, + }; + }); return (
@@ -89,7 +195,7 @@ export const WeeklyAvailability: React.FC = ({ return (
{ if (selectedDate && consultantDetails) { if (consultantDetails.scheduleType === "WEEKLY") { - // For weekly schedule, use the slots directly from consultantDetails - const weeklySlots = consultantDetails.slotsOfAvailabilityWeekly.map( - (slot) => { - // Get the selected date's day - const selectedDay = dayMap[selectedDate.getDay()]; - - // Only map slots for the selected day - if (slot.dayOfWeekforStartTimeInUTC !== selectedDay) { - return null; - } + const selectedDay = dayMap[selectedDate.getDay()]; + const previousDay = getDayBefore(selectedDay); + const nextDay = getDayAfter(selectedDay); + + // Get all slots that could be relevant for this day + const relevantSlots = consultantDetails.slotsOfAvailabilityWeekly.filter(slot => { + // Include slots that: + // 1. Start on this day + // 2. End on this day (started previous day) + // 3. Start on this day and end next day + return ( + slot.dayOfWeekforStartTimeInUTC === selectedDay || + slot.dayOfWeekforEndTimeInUTC === selectedDay || + (slot.dayOfWeekforStartTimeInUTC === previousDay && + slot.dayOfWeekforEndTimeInUTC === nextDay) + ); + }); - // Create a new date object for the selected date - const slotDate = new Date(selectedDate); - - // Parse hours and minutes from the UTC time - const startTime = new Date(slot.slotStartTimeInUTC); - const endTime = new Date(slot.slotEndTimeInUTC); - - // Convert UTC times to local times - const localStartTime = convertUTCToLocal(startTime); - const localEndTime = convertUTCToLocal(endTime); - - // Set the hours and minutes on the selected date - const startDateTime = new Date(slotDate); - startDateTime.setHours(localStartTime.getHours(), localStartTime.getMinutes(), 0, 0); - - const endDateTime = new Date(slotDate); - endDateTime.setHours(localEndTime.getHours(), localEndTime.getMinutes(), 0, 0); + const weeklySlots = relevantSlots.flatMap(slot => { + // Create a new date object for the selected date + const slotDate = new Date(selectedDate); + + // Parse hours and minutes from the UTC time + const startTime = new Date(slot.slotStartTimeInUTC); + const endTime = new Date(slot.slotEndTimeInUTC); + + // Convert UTC times to local times + const localStartTime = convertUTCToLocal(startTime); + const localEndTime = convertUTCToLocal(endTime); + + // Set the hours and minutes on the selected date + const startDateTime = new Date(slotDate); + startDateTime.setHours(localStartTime.getHours(), localStartTime.getMinutes(), 0, 0); + + const endDateTime = new Date(slotDate); + endDateTime.setHours(localEndTime.getHours(), localEndTime.getMinutes(), 0, 0); + + // Handle slots that cross midnight + if (endDateTime < startDateTime) { + // Split the slot into two parts + const midnightEnd = new Date(startDateTime); + midnightEnd.setHours(23, 59, 59, 999); + + const midnightStart = new Date(endDateTime); + midnightStart.setHours(0, 0, 0, 0); + + // Return both parts of the slot + return [ + createWeeklySlot(slot, selectedDate, startDateTime, midnightEnd), + createWeeklySlot(slot, selectedDate, midnightStart, endDateTime) + ]; + } - // If the slot crosses midnight, adjust the end date - if (endDateTime < startDateTime) { - endDateTime.setDate(endDateTime.getDate() + 1); - } + // For regular slots that don't cross midnight + return [createWeeklySlot(slot, selectedDate, startDateTime, endDateTime)]; + }); - return { - slotId: slot.id, - dateInISO: selectedDate.toISOString(), - dayOfWeek: slot.dayOfWeekforStartTimeInUTC, - slotStartTimeInUTC: startDateTime.toISOString(), - slotEndTimeInUTC: endDateTime.toISOString(), - slotOfAvailabilityId: slot.id, - slotOfAppointmentId: "", - localStartTime: startDateTime.toLocaleTimeString(), - localEndTime: endDateTime.toLocaleTimeString(), - }; - }, - ).filter((slot): slot is TSlotTiming => slot !== null); + // Sort slots by start time + const sortedSlots = weeklySlots.sort((a, b) => { + return new Date(a.slotStartTimeInUTC).getTime() - new Date(b.slotStartTimeInUTC).getTime(); + }); - setSlotTimings(weeklySlots); + setSlotTimings(sortedSlots); } else if (consultantDetails.scheduleType === "CUSTOM") { - // For custom schedule, use the custom slots from consultantDetails const customSlots = consultantDetails.slotsOfAvailabilityCustom .filter((slot) => { const slotDate = new Date( @@ -179,20 +258,20 @@ export default function ExpertProfile( const startDateTime = convertUTCToLocal(new Date(slot.slotStartTimeInUTC)); const endDateTime = convertUTCToLocal(new Date(slot.slotEndTimeInUTC)); - return { - slotId: slot.id, - dateInISO: selectedDate.toISOString(), - dayOfWeek: dayMap[startDateTime.getDay()], - slotStartTimeInUTC: startDateTime.toISOString(), - slotEndTimeInUTC: endDateTime.toISOString(), - slotOfAvailabilityId: slot.id, - slotOfAppointmentId: "", - localStartTime: startDateTime.toLocaleTimeString(), - localEndTime: endDateTime.toLocaleTimeString(), - }; + // If end time is before start time, it means it ends next day + const adjustedEndDateTime = endDateTime < startDateTime + ? new Date(endDateTime.setDate(endDateTime.getDate() + 1)) + : endDateTime; + + return createCustomSlot(slot, selectedDate, startDateTime, adjustedEndDateTime); }); - setSlotTimings(customSlots); + // Sort slots by start time + const sortedSlots = customSlots.sort((a, b) => { + return new Date(a.slotStartTimeInUTC).getTime() - new Date(b.slotStartTimeInUTC).getTime(); + }); + + setSlotTimings(sortedSlots); } } }, [selectedDate, consultantDetails]); diff --git a/types/slots.ts b/types/slots.ts index 22dc5b9..12cda20 100644 --- a/types/slots.ts +++ b/types/slots.ts @@ -11,3 +11,17 @@ export type TSlotTiming = { localStartTime: string; localEndTime: string; }; + +export type TWeeklySlot = { + id: string; + dayOfWeekforStartTimeInUTC: DayOfWeek; + slotStartTimeInUTC: string | Date; + dayOfWeekforEndTimeInUTC: DayOfWeek; + slotEndTimeInUTC: string | Date; +}; + +export type TCustomSlot = { + id: string; + slotStartTimeInUTC: string | Date; + slotEndTimeInUTC: string | Date; +};