From 6371c89e06dc687df76def8133f2667d40625ac3 Mon Sep 17 00:00:00 2001 From: gilteixeira Date: Sun, 17 Dec 2023 12:03:29 +0000 Subject: [PATCH 1/9] Multiple selection of slots added. --- src/TimeSlotPicker.tsx | 61 ++++++++++---------------- src/components/LocalContext.tsx | 18 ++++---- src/components/ScheduleDateElement.tsx | 20 ++++----- src/components/ScheduleDatePicker.tsx | 39 ++++++++-------- src/components/TimeSlot.tsx | 33 +++++--------- src/components/TimeSlots.tsx | 31 +++++++++---- 6 files changed, 94 insertions(+), 108 deletions(-) diff --git a/src/TimeSlotPicker.tsx b/src/TimeSlotPicker.tsx index c14b3c4..cbc8681 100644 --- a/src/TimeSlotPicker.tsx +++ b/src/TimeSlotPicker.tsx @@ -13,9 +13,9 @@ import { import { LocalContext } from './components/LocalContext'; interface Props { - setDateOfAppointment: (data: IAppointment | null) => void; + setScheduledAppointments: (data: IAppointment[] | null) => void; availableDates?: IAvailableDates[]; - scheduledAppointment?: IAppointment | undefined; + scheduledAppointments?: IAppointment[]; marginTop?: number; datePickerBackgroundColor?: string; timeSlotsBackgroundColor?: string; @@ -28,8 +28,8 @@ interface Props { const TimeSlotPicker = ({ availableDates = fixedAvailableDates, - setDateOfAppointment, - scheduledAppointment, + scheduledAppointments, + setScheduledAppointments, marginTop = 0, datePickerBackgroundColor, timeSlotsBackgroundColor, @@ -39,36 +39,21 @@ const TimeSlotPicker = ({ dayNamesOverride = defaultDayNames, monthNamesOverride = defaultMonthNames, }: Props) => { - const [selectedTime, setSelectedTime] = useState(''); - const [selectedDate, setSelectedDate] = useState( - availableDates[0] + const [selectedDay, setSelectedDay] = useState( + scheduledAppointments && + scheduledAppointments.length > 0 ? + scheduledAppointments.sort((a, b) => { + return new Date(a.appointmentDate).getTime() - new Date(b.appointmentDate).getTime(); + })[0].appointmentDate : + availableDates.sort((a, b) => { + return new Date(a.date).getTime() - new Date(b.date).getTime(); + })[0].date ); - useEffect(() => { - // Get first day with available appointments - const firstAvailableDay = - availableDates.findIndex((date) => date.slotTimes.length > 0) || 0; - setSelectedDate(availableDates?.[firstAvailableDay]); - setSelectedTime(availableDates?.[firstAvailableDay]?.slotTimes?.[0] || ''); - }, [availableDates]); - - // If any changes on date and time selected update data of appointment - useEffect(() => { - if (selectedDate && selectedTime) { - setDateOfAppointment({ - appointmentDate: selectedDate.date, - appointmentTime: selectedTime, - }); - } else { - setDateOfAppointment(null); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedDate, selectedTime]); - return ( - {selectedDate && ( + {selectedDay && ( data.date === selectedDay)?.slotTimes || []} + scheduledAppointments={scheduledAppointments} + setScheduledAppointments={setScheduledAppointments} backgroundColor={timeSlotsBackgroundColor} /> )} diff --git a/src/components/LocalContext.tsx b/src/components/LocalContext.tsx index f5f8a44..af91e19 100644 --- a/src/components/LocalContext.tsx +++ b/src/components/LocalContext.tsx @@ -15,9 +15,9 @@ interface IOverrideData { monthNamesOverride: string[]; } -export const SelectedDateContext = createContext(''); -export const ScheduledAppointmentContext = createContext< - IAppointment | undefined +export const selectedDayContext = createContext(''); +export const scheduledAppointmentsContext = createContext< + IAppointment[] | undefined >(undefined); export const OverrideDataContext = createContext({ @@ -30,23 +30,23 @@ export const OverrideDataContext = createContext({ interface Props { children: React.ReactNode; slotDate: string; - scheduledAppointment: IAppointment | undefined; + scheduledAppointments: IAppointment[] | undefined; overrideData: IOverrideData; } export const LocalContext = ({ children, slotDate, - scheduledAppointment, + scheduledAppointments, overrideData, }: Props) => { return ( - - + + {children} - - + + ); }; diff --git a/src/components/ScheduleDateElement.tsx b/src/components/ScheduleDateElement.tsx index 1c8ca98..e2dcfd3 100644 --- a/src/components/ScheduleDateElement.tsx +++ b/src/components/ScheduleDateElement.tsx @@ -4,28 +4,28 @@ import CalendarDay from './CalendarDay'; interface ScheduleDateElementProps { slotDate: IAvailableDates; - selectedDate?: IAvailableDates; - onPress: () => void; + selectedDay: string; currentDay: number; - appointmentDay: number; + onPress: () => void; + appointmentDays: number[]; } const ScheduleDateElement = ({ slotDate, - onPress, - selectedDate, + selectedDay, currentDay, - appointmentDay, + onPress, + appointmentDays, }: ScheduleDateElementProps) => { const day = useMemo(() => new Date(slotDate.date).getDate(), [slotDate.date]); const isAppointmentToday = useMemo( - () => appointmentDay === day, - [appointmentDay, day] + () => day in appointmentDays, + [appointmentDays, day] ); const isSelected = useCallback(() => { - return slotDate.date === selectedDate?.date; - }, [slotDate.date, selectedDate?.date]); + return slotDate.date === selectedDay; + }, [slotDate.date, selectedDay]); return ( void; - setSelectedTime: (time: string) => void; - scheduledAppointment: IAppointment | undefined; + selectedDay: string; + setSelectedDay: (date: string) => void; + scheduledAppointments: IAppointment[] | undefined; backgroundColor?: string; } @@ -19,10 +18,9 @@ const currentDay = today.getDate(); const ScheduleDatePicker = ({ availableDates, - selectedDate, - setSelectedDate, - setSelectedTime, - scheduledAppointment, + selectedDay, + setSelectedDay, + scheduledAppointments, backgroundColor = theme.colors.white, }: Props) => { const scrollRef = useRef(); @@ -32,35 +30,36 @@ const ScheduleDatePicker = ({ [monthNamesOverride] ); - const getScheduledAppointmentDate = useCallback( - (date) => new Date(date).getDate(), + const getScheduledAppointmentsDate = useCallback( + (date: string) => new Date(date).getDate(), [] ); const onDatePress = (date: IAvailableDates) => { - setSelectedDate(date); - setSelectedTime(date?.slotTimes?.[0] || ''); + setSelectedDay(date.date); }; - const getAppointmentDay: () => number = useCallback(() => { - if (scheduledAppointment?.appointmentDate) { - return getScheduledAppointmentDate(scheduledAppointment?.appointmentDate); - } + const getAppointmentDays: () => number[] = useCallback(() => { + const schecduledDays = scheduledAppointments?.map((data) => + getScheduledAppointmentsDate(data.appointmentDate) + ); + if (schecduledDays?.length) + return schecduledDays; - return -1; - }, [scheduledAppointment?.appointmentDate, getScheduledAppointmentDate]); + return []; + }, [scheduledAppointments?.map((data) => data.appointmentDate), getScheduledAppointmentsDate]); const dateContainer = (date: IAvailableDates, index: number) => { return ( { onDatePress(date); }} currentDay={currentDay} - appointmentDay={getAppointmentDay()} + appointmentDays={getAppointmentDays()} /> ); diff --git a/src/components/TimeSlot.tsx b/src/components/TimeSlot.tsx index 816a94e..bd5fb53 100644 --- a/src/components/TimeSlot.tsx +++ b/src/components/TimeSlot.tsx @@ -3,29 +3,23 @@ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { theme } from '../utils/theme'; import { OverrideDataContext, - ScheduledAppointmentContext, - SelectedDateContext, + scheduledAppointmentsContext, + selectedDayContext, } from './LocalContext'; interface Props { value: string; onPress: () => void; - selectedTime: string; } -const TimeSlot = ({ value, onPress, selectedTime }: Props) => { - const isSelected = selectedTime === value; - const scheduledAppointment = useContext(ScheduledAppointmentContext); - const selectedDate = useContext(SelectedDateContext); +const TimeSlot = ({ value, onPress }: Props) => { + const scheduledAppointments = useContext(scheduledAppointmentsContext); + const selectedDay = useContext(selectedDayContext); const { mainColor, timeSlotWidth } = useContext(OverrideDataContext); - const appointmentDateToCompare = useMemo( - () => scheduledAppointment?.appointmentDate?.split('T')[0], - [scheduledAppointment?.appointmentDate] - ); - const selectedDateToCompare = useMemo( - () => selectedDate.split('T')[0], - [selectedDate] + const isSelected = scheduledAppointments?.find( + (appointment) => appointment.appointmentDate === selectedDay && + appointment.appointmentTime === value ); const appointmentDot = useMemo(() => { @@ -41,21 +35,14 @@ const TimeSlot = ({ value, onPress, selectedTime }: Props) => { // Check if there is an appointment to mark time slot appropriately const getAppointmentDot: () => React.JSX.Element | null = useCallback(() => { - if (scheduledAppointment?.appointmentDate) { - if ( - selectedDateToCompare === appointmentDateToCompare && - scheduledAppointment.appointmentTime === value - ) { + if (isSelected) { return appointmentDot; } - } return null; }, [ appointmentDot, - scheduledAppointment, - appointmentDateToCompare, - selectedDateToCompare, + scheduledAppointments, value, ]); diff --git a/src/components/TimeSlots.tsx b/src/components/TimeSlots.tsx index eaa2b30..48fe0fd 100644 --- a/src/components/TimeSlots.tsx +++ b/src/components/TimeSlots.tsx @@ -2,27 +2,43 @@ import React, { useCallback } from 'react'; import TimeSlot from './TimeSlot'; import { StyleSheet, Text, View } from 'react-native'; import { theme } from '../utils/theme'; +import { IAppointment } from '../interfaces/app.interface'; interface Props { + selectedDay: string; slotTimes: string[]; - selectedTime: string; - setSelectedTime: (value: string) => void; + scheduledAppointments: IAppointment[] | undefined; + setScheduledAppointments: (value: IAppointment[] | null) => void; title?: string; backgroundColor?: string; mainColor?: string; } const TimeSlots = ({ + selectedDay, slotTimes, - selectedTime, - setSelectedTime, + scheduledAppointments, + setScheduledAppointments, title = 'Select time', backgroundColor = theme.colors.white, }: Props) => { const onPress = (value: string) => { - setSelectedTime(value); + if (!scheduledAppointments) setScheduledAppointments([{ + appointmentDate: selectedDay, + appointmentTime: value, + }]); + else { + const isAlreadyScheduled = scheduledAppointments.find((data) => data.appointmentTime === value); + if (isAlreadyScheduled) { + setScheduledAppointments(scheduledAppointments.filter((data) => data.appointmentTime !== value)); + } else { + setScheduledAppointments([...scheduledAppointments, { + appointmentDate: selectedDay, + appointmentTime: value, + }]); + } + } }; - const getTimeSlots = useCallback(() => { return slotTimes.map((time) => { return ( @@ -30,13 +46,12 @@ const TimeSlots = ({ onPress(time)} value={time} - selectedTime={selectedTime} /> ); }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [slotTimes, selectedTime]); + }, [slotTimes, scheduledAppointments]); return ( From 190a5c10ebafb8c7e0ede9fa037d7c8fed8fb733 Mon Sep 17 00:00:00 2001 From: gilteixeira Date: Sun, 17 Dec 2023 12:13:50 +0000 Subject: [PATCH 2/9] fixed days dot not appearing --- src/components/ScheduleDateElement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ScheduleDateElement.tsx b/src/components/ScheduleDateElement.tsx index e2dcfd3..00c7d56 100644 --- a/src/components/ScheduleDateElement.tsx +++ b/src/components/ScheduleDateElement.tsx @@ -19,7 +19,7 @@ const ScheduleDateElement = ({ }: ScheduleDateElementProps) => { const day = useMemo(() => new Date(slotDate.date).getDate(), [slotDate.date]); const isAppointmentToday = useMemo( - () => day in appointmentDays, + () => appointmentDays.find((data) => data === day) && day, [appointmentDays, day] ); From 5d96bbf32dd64a98d3b80e4248aa0ad0bb819d22 Mon Sep 17 00:00:00 2001 From: gilteixeira Date: Sun, 17 Dec 2023 13:48:57 +0000 Subject: [PATCH 3/9] added options to allow for multipleSelections and the strategy used --- src/TimeSlotPicker.tsx | 8 +- src/components/ScheduleDatePicker.tsx | 2 +- src/components/TimeSlots.tsx | 108 ++++++++++++++++++++++---- 3 files changed, 101 insertions(+), 17 deletions(-) diff --git a/src/TimeSlotPicker.tsx b/src/TimeSlotPicker.tsx index cbc8681..740e6b5 100644 --- a/src/TimeSlotPicker.tsx +++ b/src/TimeSlotPicker.tsx @@ -13,9 +13,11 @@ import { import { LocalContext } from './components/LocalContext'; interface Props { - setScheduledAppointments: (data: IAppointment[] | null) => void; availableDates?: IAvailableDates[]; scheduledAppointments?: IAppointment[]; + setScheduledAppointments: (data: IAppointment[] | null) => void; + multipleSelection?: boolean; + multipleSelectionStrategy?: 'consecutive' | 'non-consecutive'; marginTop?: number; datePickerBackgroundColor?: string; timeSlotsBackgroundColor?: string; @@ -30,6 +32,8 @@ const TimeSlotPicker = ({ availableDates = fixedAvailableDates, scheduledAppointments, setScheduledAppointments, + multipleSelection = false, + multipleSelectionStrategy = 'non-consecutive', marginTop = 0, datePickerBackgroundColor, timeSlotsBackgroundColor, @@ -78,6 +82,8 @@ const TimeSlotPicker = ({ slotTimes={availableDates.find((data) => data.date === selectedDay)?.slotTimes || []} scheduledAppointments={scheduledAppointments} setScheduledAppointments={setScheduledAppointments} + multipleSelection={multipleSelection} + multipleSelectionStrategy={multipleSelectionStrategy} backgroundColor={timeSlotsBackgroundColor} /> )} diff --git a/src/components/ScheduleDatePicker.tsx b/src/components/ScheduleDatePicker.tsx index fc49516..4cf3e54 100644 --- a/src/components/ScheduleDatePicker.tsx +++ b/src/components/ScheduleDatePicker.tsx @@ -47,7 +47,7 @@ const ScheduleDatePicker = ({ return schecduledDays; return []; - }, [scheduledAppointments?.map((data) => data.appointmentDate), getScheduledAppointmentsDate]); + }, [scheduledAppointments, getScheduledAppointmentsDate]); const dateContainer = (date: IAvailableDates, index: number) => { return ( diff --git a/src/components/TimeSlots.tsx b/src/components/TimeSlots.tsx index 48fe0fd..5007b24 100644 --- a/src/components/TimeSlots.tsx +++ b/src/components/TimeSlots.tsx @@ -9,6 +9,8 @@ interface Props { slotTimes: string[]; scheduledAppointments: IAppointment[] | undefined; setScheduledAppointments: (value: IAppointment[] | null) => void; + multipleSelection: boolean; + multipleSelectionStrategy: 'consecutive' | 'non-consecutive'; title?: string; backgroundColor?: string; mainColor?: string; @@ -19,23 +21,102 @@ const TimeSlots = ({ slotTimes, scheduledAppointments, setScheduledAppointments, + multipleSelection, + multipleSelectionStrategy, title = 'Select time', backgroundColor = theme.colors.white, }: Props) => { const onPress = (value: string) => { - if (!scheduledAppointments) setScheduledAppointments([{ - appointmentDate: selectedDay, - appointmentTime: value, - }]); - else { - const isAlreadyScheduled = scheduledAppointments.find((data) => data.appointmentTime === value); - if (isAlreadyScheduled) { - setScheduledAppointments(scheduledAppointments.filter((data) => data.appointmentTime !== value)); - } else { - setScheduledAppointments([...scheduledAppointments, { + if (!scheduledAppointments || scheduledAppointments.length === 0) + setScheduledAppointments([ + { appointmentDate: selectedDay, appointmentTime: value, - }]); + }, + ]); + else { + if (!multipleSelection) { + if (scheduledAppointments[0].appointmentTime === value) + setScheduledAppointments(null); + } else { + const isAlreadyScheduled = scheduledAppointments.find( + (data) => data.appointmentTime === value + ); + if (isAlreadyScheduled) { + if (multipleSelectionStrategy === 'non-consecutive') + setScheduledAppointments( + scheduledAppointments.filter( + (data) => data.appointmentTime !== value + ) + ); + else { + // only remove if it is not consecutive + const valueSplit = value.split('-'); + const valueStartTime = valueSplit[0]; + const valueEndTime = valueSplit[1]; + const isConsecutive = + scheduledAppointments.find((data) => { + const dataSplit = data.appointmentTime.split('-'); + const dataStartTime = dataSplit[0]; + const dataEndTime = dataSplit[1]; + if (data.appointmentDate === selectedDay) { + if (dataStartTime === valueEndTime) return true; + } + return false; + }) && + scheduledAppointments.find((data) => { + const dataSplit = data.appointmentTime.split('-'); + const dataStartTime = dataSplit[0]; + const dataEndTime = dataSplit[1]; + if (data.appointmentDate === selectedDay) { + if (dataEndTime === valueStartTime) return true; + } + return false; + }); + if (!isConsecutive) + setScheduledAppointments( + scheduledAppointments.filter( + (data) => data.appointmentTime !== value + ) + ); + } + } else { + if (multipleSelectionStrategy === 'non-consecutive') + setScheduledAppointments([ + ...scheduledAppointments, + { + appointmentDate: selectedDay, + appointmentTime: value, + }, + ]); + else { + const valueSplit = value.split('-'); + const valueStartTime = valueSplit[0]; + const valueEndTime = valueSplit[1]; + const isConsecutive = scheduledAppointments.find((data) => { + const dataSplit = data.appointmentTime.split('-'); + const dataStartTime = dataSplit[0]; + const dataEndTime = dataSplit[1]; + if (data.appointmentDate === selectedDay) { + if ( + dataStartTime === valueEndTime || + dataEndTime === valueStartTime + ) + return true; + } + return false; + }); + if (isConsecutive) { + setScheduledAppointments([ + ...scheduledAppointments, + { + appointmentDate: selectedDay, + appointmentTime: value, + }, + ]); + } + } + } } } }; @@ -43,10 +124,7 @@ const TimeSlots = ({ return slotTimes.map((time) => { return ( - onPress(time)} - value={time} - /> + onPress(time)} value={time} /> ); }); From a11c7cb9157fd226e63823bf66da86be7ea06f6b Mon Sep 17 00:00:00 2001 From: gilteixeira Date: Sun, 17 Dec 2023 21:11:59 +0000 Subject: [PATCH 4/9] docs: added multipleSelection to the example App and ran tsc and eslint --- example/src/App.tsx | 12 +++++---- example/src/SelectedTimeSlot.tsx | 16 +++++++++--- src/TimeSlotPicker.tsx | 36 +++++++++++++++++--------- src/components/ScheduleDateElement.tsx | 2 +- src/components/ScheduleDatePicker.tsx | 3 +-- src/components/TimeSlot.tsx | 17 +++++------- src/components/TimeSlots.tsx | 23 +++++++++++----- 7 files changed, 68 insertions(+), 41 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index aa38793..62f3dcc 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -10,17 +10,19 @@ import { SelectedTimeSlot } from './SelectedTimeSlot'; import { bookedData, dummyAvailableDates } from './data'; export default function App() { - const [dateOfAppointment, setDateOfAppointment] = - useState(null); + const [scheduledAppointments, setScheduledAppointments] = useState< + IAppointment[] + >([bookedData]); return ( - + ); diff --git a/example/src/SelectedTimeSlot.tsx b/example/src/SelectedTimeSlot.tsx index a3c64c1..f71718f 100644 --- a/example/src/SelectedTimeSlot.tsx +++ b/example/src/SelectedTimeSlot.tsx @@ -3,10 +3,10 @@ import { StyleSheet, Text, View } from 'react-native'; import { IAppointment } from '@dgreasi/react-native-time-slot-picker'; interface Props { - dateOfAppointment: IAppointment | null; + scheduledAppointments: IAppointment[] | null; } -export const SelectedTimeSlot = ({ dateOfAppointment }: Props) => { +export const SelectedTimeSlot = ({ scheduledAppointments }: Props) => { return ( Selected time slot @@ -20,13 +20,21 @@ export const SelectedTimeSlot = ({ dateOfAppointment }: Props) => { appointmentDate: - {dateOfAppointment?.appointmentDate} + + {scheduledAppointments && scheduledAppointments[0] + ? scheduledAppointments[0].appointmentDate + : ''} + appointmentTime: - {dateOfAppointment?.appointmentTime} + + {scheduledAppointments && scheduledAppointments[0] + ? scheduledAppointments[0].appointmentTime + : ''} + diff --git a/src/TimeSlotPicker.tsx b/src/TimeSlotPicker.tsx index 740e6b5..1dc56f0 100644 --- a/src/TimeSlotPicker.tsx +++ b/src/TimeSlotPicker.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { IAppointment, IAvailableDates } from './interfaces/app.interface'; import { View } from 'react-native'; import ScheduleDatePicker from './components/ScheduleDatePicker'; @@ -15,9 +15,12 @@ import { LocalContext } from './components/LocalContext'; interface Props { availableDates?: IAvailableDates[]; scheduledAppointments?: IAppointment[]; - setScheduledAppointments: (data: IAppointment[] | null) => void; + setScheduledAppointments: (data: IAppointment[]) => void; multipleSelection?: boolean; - multipleSelectionStrategy?: 'consecutive' | 'non-consecutive'; + multipleSelectionStrategy?: + | 'consecutive' + | 'same-day-consecutive' + | 'non-consecutive'; marginTop?: number; datePickerBackgroundColor?: string; timeSlotsBackgroundColor?: string; @@ -43,15 +46,21 @@ const TimeSlotPicker = ({ dayNamesOverride = defaultDayNames, monthNamesOverride = defaultMonthNames, }: Props) => { + const sortedAppointments = scheduledAppointments?.sort((a, b) => { + return ( + new Date(a.appointmentDate).getTime() - + new Date(b.appointmentDate).getTime() + ); + }); + const sortedAvailableDates = availableDates.sort((a, b) => { + return new Date(a.date).getTime() - new Date(b.date).getTime(); + }); const [selectedDay, setSelectedDay] = useState( - scheduledAppointments && - scheduledAppointments.length > 0 ? - scheduledAppointments.sort((a, b) => { - return new Date(a.appointmentDate).getTime() - new Date(b.appointmentDate).getTime(); - })[0].appointmentDate : - availableDates.sort((a, b) => { - return new Date(a.date).getTime() - new Date(b.date).getTime(); - })[0].date + sortedAppointments && sortedAppointments[0] + ? sortedAppointments[0].appointmentDate + : sortedAvailableDates && sortedAvailableDates[0] + ? sortedAvailableDates[0].date + : '' ); return ( @@ -79,7 +88,10 @@ const TimeSlotPicker = ({ data.date === selectedDay)?.slotTimes || []} + slotTimes={ + availableDates.find((data) => data.date === selectedDay) + ?.slotTimes || [] + } scheduledAppointments={scheduledAppointments} setScheduledAppointments={setScheduledAppointments} multipleSelection={multipleSelection} diff --git a/src/components/ScheduleDateElement.tsx b/src/components/ScheduleDateElement.tsx index 00c7d56..e7b9dc8 100644 --- a/src/components/ScheduleDateElement.tsx +++ b/src/components/ScheduleDateElement.tsx @@ -19,7 +19,7 @@ const ScheduleDateElement = ({ }: ScheduleDateElementProps) => { const day = useMemo(() => new Date(slotDate.date).getDate(), [slotDate.date]); const isAppointmentToday = useMemo( - () => appointmentDays.find((data) => data === day) && day, + () => !!appointmentDays.find((data) => data === day), [appointmentDays, day] ); diff --git a/src/components/ScheduleDatePicker.tsx b/src/components/ScheduleDatePicker.tsx index 4cf3e54..5f8a4dd 100644 --- a/src/components/ScheduleDatePicker.tsx +++ b/src/components/ScheduleDatePicker.tsx @@ -43,8 +43,7 @@ const ScheduleDatePicker = ({ const schecduledDays = scheduledAppointments?.map((data) => getScheduledAppointmentsDate(data.appointmentDate) ); - if (schecduledDays?.length) - return schecduledDays; + if (schecduledDays?.length) return schecduledDays; return []; }, [scheduledAppointments, getScheduledAppointmentsDate]); diff --git a/src/components/TimeSlot.tsx b/src/components/TimeSlot.tsx index bd5fb53..43598c1 100644 --- a/src/components/TimeSlot.tsx +++ b/src/components/TimeSlot.tsx @@ -18,8 +18,9 @@ const TimeSlot = ({ value, onPress }: Props) => { const { mainColor, timeSlotWidth } = useContext(OverrideDataContext); const isSelected = scheduledAppointments?.find( - (appointment) => appointment.appointmentDate === selectedDay && - appointment.appointmentTime === value + (appointment) => + appointment.appointmentDate === selectedDay && + appointment.appointmentTime === value ); const appointmentDot = useMemo(() => { @@ -35,16 +36,12 @@ const TimeSlot = ({ value, onPress }: Props) => { // Check if there is an appointment to mark time slot appropriately const getAppointmentDot: () => React.JSX.Element | null = useCallback(() => { - if (isSelected) { - return appointmentDot; - } + if (isSelected) { + return appointmentDot; + } return null; - }, [ - appointmentDot, - scheduledAppointments, - value, - ]); + }, [appointmentDot, isSelected]); return ( diff --git a/src/components/TimeSlots.tsx b/src/components/TimeSlots.tsx index 5007b24..c6bdaba 100644 --- a/src/components/TimeSlots.tsx +++ b/src/components/TimeSlots.tsx @@ -8,9 +8,12 @@ interface Props { selectedDay: string; slotTimes: string[]; scheduledAppointments: IAppointment[] | undefined; - setScheduledAppointments: (value: IAppointment[] | null) => void; + setScheduledAppointments: (value: IAppointment[]) => void; multipleSelection: boolean; - multipleSelectionStrategy: 'consecutive' | 'non-consecutive'; + multipleSelectionStrategy: + | 'consecutive' + | 'same-day-consecutive' + | 'non-consecutive'; title?: string; backgroundColor?: string; mainColor?: string; @@ -36,8 +39,12 @@ const TimeSlots = ({ ]); else { if (!multipleSelection) { - if (scheduledAppointments[0].appointmentTime === value) - setScheduledAppointments(null); + if ( + scheduledAppointments && + scheduledAppointments[0] && + scheduledAppointments[0].appointmentTime === value + ) + setScheduledAppointments([]); } else { const isAlreadyScheduled = scheduledAppointments.find( (data) => data.appointmentTime === value @@ -58,7 +65,6 @@ const TimeSlots = ({ scheduledAppointments.find((data) => { const dataSplit = data.appointmentTime.split('-'); const dataStartTime = dataSplit[0]; - const dataEndTime = dataSplit[1]; if (data.appointmentDate === selectedDay) { if (dataStartTime === valueEndTime) return true; } @@ -66,7 +72,6 @@ const TimeSlots = ({ }) && scheduledAppointments.find((data) => { const dataSplit = data.appointmentTime.split('-'); - const dataStartTime = dataSplit[0]; const dataEndTime = dataSplit[1]; if (data.appointmentDate === selectedDay) { if (dataEndTime === valueStartTime) return true; @@ -97,7 +102,11 @@ const TimeSlots = ({ const dataSplit = data.appointmentTime.split('-'); const dataStartTime = dataSplit[0]; const dataEndTime = dataSplit[1]; - if (data.appointmentDate === selectedDay) { + if ( + (multipleSelectionStrategy === 'same-day-consecutive' && + data.appointmentDate === selectedDay) || + multipleSelectionStrategy === 'consecutive' + ) { if ( dataStartTime === valueEndTime || dataEndTime === valueStartTime From 6bbfa341f9d4479534e426a0caa50bea72fda791 Mon Sep 17 00:00:00 2001 From: gilteixeira Date: Sun, 17 Dec 2023 23:23:31 +0000 Subject: [PATCH 5/9] docs: updated the example and props names, descriptions and types --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8c9e3e2..b6fd2df 100644 --- a/README.md +++ b/README.md @@ -78,8 +78,8 @@ const availableDates: IAvailableDates[] = [ ]; export default function App() { - const [dateOfAppointment, setDateOfAppointment] = - useState(null); + const [scheduledAppointments, setScheduledAppointments] = + useState([]); useEffect(() => { // Contains the selected date, time slot in the following format @@ -91,8 +91,9 @@ export default function App() { ); @@ -105,9 +106,11 @@ You can find a detailed example [here](example/src/App.tsx). | Prop name | Description | Type | Default | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| `setDateOfAppointment` | A component to use on top of header image. It can also be used without header image to render a custom component as header. | `(data: IAppointment \| null) => void` | **REQUIRED** | | `availableDates` | The array of the available slot times per date. | `IAvailableDates[]` | [fixedAvailableDates](src/utils/dateHelpers.ts) | -| `scheduledAppointment` | An already existed appointment, which is going to mark the specific date as `with appointment`. | `IAppointment` | `undefined` | +| `scheduledAppointments` | An already existing array of appointments, which is going to mark the specific dates as `with appointment`. | `IAppointment[]` | `[]` +| `setScheduledAppointments` | Callback called when the user selects or deselects a time slot. It contains all currently selected time slots. | `(data: IAppointment[]) => void` | **REQUIRED** | +| `multipleSelection` | Enables the selection of multiple slots. | `boolean` | `false` | | +| `multipleSelectionStrategy` | Strategy used to restrict selection and deselection. | `"consecutive" \| "same-day-consecutive" \| "non-consecutive"` | `non-consecutive` | | | `marginTop` | Margin top for the whole component. | `number` | `0` | | `datePickerBackgroundColor` | Background color of the section with the horizontal scroll, which contains the days. | `hex string` | `'#FFFFFF'` | | `timeSlotsBackgroundColor` | Background color of the section that contains the time slots. | `hex string` | `'#FFFFFF'` | From 288b57e5762f5b899b17f54d427f20e13cf979ad Mon Sep 17 00:00:00 2001 From: gilteixeira Date: Sat, 30 Dec 2023 16:28:43 +0000 Subject: [PATCH 6/9] fix: consecutive in two distinct days;feat:utility --- example/src/App.tsx | 55 +++++++++++++-------- src/TimeSlotPicker.tsx | 26 ++++++---- src/components/TimeSlots.tsx | 95 +++++++++++++++++++++++++++++------- src/utils/availableSlots.ts | 51 +++++++++++++++++++ 4 files changed, 178 insertions(+), 49 deletions(-) create mode 100644 src/utils/availableSlots.ts diff --git a/example/src/App.tsx b/example/src/App.tsx index 62f3dcc..a62091d 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,40 +1,53 @@ import * as React from 'react'; import { useState } from 'react'; -import { StatusBar } from 'react-native'; +import { ScrollView, StatusBar, StyleSheet } from 'react-native'; import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; import { IAppointment, TimeSlotPicker, } from '@dgreasi/react-native-time-slot-picker'; import { SelectedTimeSlot } from './SelectedTimeSlot'; -import { bookedData, dummyAvailableDates } from './data'; +import { generateAvailableDates } from '../../src/utils/availableSlots'; export default function App() { const [scheduledAppointments, setScheduledAppointments] = useState< - IAppointment[] - >([bookedData]); - + IAppointment[] | undefined + >(); + const dummyAvailableDates = React.useMemo(() => { + return generateAvailableDates(3, 30); + }, []); return ( - - + + + + ); } + +const styles = StyleSheet.create({ + scrollViewContainer: { + minHeight: '100%', + }, +}); diff --git a/src/TimeSlotPicker.tsx b/src/TimeSlotPicker.tsx index 1dc56f0..239e24a 100644 --- a/src/TimeSlotPicker.tsx +++ b/src/TimeSlotPicker.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { IAppointment, IAvailableDates } from './interfaces/app.interface'; import { View } from 'react-native'; import ScheduleDatePicker from './components/ScheduleDatePicker'; @@ -46,15 +46,20 @@ const TimeSlotPicker = ({ dayNamesOverride = defaultDayNames, monthNamesOverride = defaultMonthNames, }: Props) => { - const sortedAppointments = scheduledAppointments?.sort((a, b) => { - return ( - new Date(a.appointmentDate).getTime() - - new Date(b.appointmentDate).getTime() - ); - }); - const sortedAvailableDates = availableDates.sort((a, b) => { - return new Date(a.date).getTime() - new Date(b.date).getTime(); - }); + const sortedAppointments = useMemo(() => { + return scheduledAppointments?.sort((a, b) => { + return ( + new Date(a.appointmentDate).getTime() - + new Date(b.appointmentDate).getTime() + ); + }); + }, [scheduledAppointments]); + const sortedAvailableDates = useMemo(() => { + return availableDates.sort((a, b) => { + return new Date(a.date).getTime() - new Date(b.date).getTime(); + }); + }, [availableDates]); + const [selectedDay, setSelectedDay] = useState( sortedAppointments && sortedAppointments[0] ? sortedAppointments[0].appointmentDate @@ -92,6 +97,7 @@ const TimeSlotPicker = ({ availableDates.find((data) => data.date === selectedDay) ?.slotTimes || [] } + availableDates={availableDates} scheduledAppointments={scheduledAppointments} setScheduledAppointments={setScheduledAppointments} multipleSelection={multipleSelection} diff --git a/src/components/TimeSlots.tsx b/src/components/TimeSlots.tsx index c6bdaba..c652733 100644 --- a/src/components/TimeSlots.tsx +++ b/src/components/TimeSlots.tsx @@ -2,12 +2,13 @@ import React, { useCallback } from 'react'; import TimeSlot from './TimeSlot'; import { StyleSheet, Text, View } from 'react-native'; import { theme } from '../utils/theme'; -import { IAppointment } from '../interfaces/app.interface'; +import { IAppointment, IAvailableDates } from '../interfaces/app.interface'; interface Props { selectedDay: string; slotTimes: string[]; scheduledAppointments: IAppointment[] | undefined; + availableDates: IAvailableDates[]; setScheduledAppointments: (value: IAppointment[]) => void; multipleSelection: boolean; multipleSelectionStrategy: @@ -23,6 +24,7 @@ const TimeSlots = ({ selectedDay, slotTimes, scheduledAppointments, + availableDates, setScheduledAppointments, multipleSelection, multipleSelectionStrategy, @@ -61,24 +63,81 @@ const TimeSlots = ({ const valueSplit = value.split('-'); const valueStartTime = valueSplit[0]; const valueEndTime = valueSplit[1]; - const isConsecutive = - scheduledAppointments.find((data) => { - const dataSplit = data.appointmentTime.split('-'); - const dataStartTime = dataSplit[0]; - if (data.appointmentDate === selectedDay) { - if (dataStartTime === valueEndTime) return true; - } - return false; - }) && - scheduledAppointments.find((data) => { - const dataSplit = data.appointmentTime.split('-'); - const dataEndTime = dataSplit[1]; - if (data.appointmentDate === selectedDay) { - if (dataEndTime === valueStartTime) return true; - } - return false; + // find the prior and next consecutive time slots, they may be in the prior or next day, and check if they are scheculed + const priorDay = new Date(selectedDay); + priorDay.setDate(priorDay.getDate() - 1); + const nextDay = new Date(selectedDay); + nextDay.setDate(nextDay.getDate() + 1); + let priorAvailableDate = availableDates.find( + (data) => + data.date === selectedDay && + data.slotTimes.find((slotTime) => { + const valueStartTimeSplit = valueStartTime?.split(':'); + const slotTimeSplit = slotTime.split('-')[0]?.split(':'); + if ( + valueStartTimeSplit && + slotTimeSplit && + valueStartTimeSplit[0] && + valueStartTimeSplit[1] && + slotTimeSplit[0] && + slotTimeSplit[1] + ) { + const isSlotBeforeSelectedTime = + parseInt(valueStartTimeSplit[0], 10) * 60 + + parseInt(valueStartTimeSplit[1], 10) > + parseInt(slotTimeSplit[0], 10) * 60 + + parseInt(slotTimeSplit[1], 10); + return ( + slotTime.split('-')[1] === valueStartTime && + isSlotBeforeSelectedTime + ); + } + return false; + }) + ); + if ( + !priorAvailableDate && + multipleSelectionStrategy === 'consecutive' + ) { + priorAvailableDate = availableDates.find((data) => { + return ( + data.date === priorDay.toISOString() && + data.slotTimes.find( + (slotTime) => slotTime.split('-')[1] === valueStartTime + ) + ); }); - if (!isConsecutive) + } + let nextAvailableDate = availableDates.find( + (data) => + data.date === selectedDay && + data.slotTimes.find( + (slotTime) => slotTime.split('-')[0] === valueEndTime + ) + ); + if ( + !nextAvailableDate && + multipleSelectionStrategy === 'consecutive' + ) { + nextAvailableDate = availableDates.find( + (data) => + data.date === nextDay.toISOString() && + data.slotTimes.find( + (slotTime) => slotTime.split('-')[0] === valueEndTime + ) + ); + } + const isPriorScheduled = scheduledAppointments.find( + (data) => + data.appointmentDate === priorAvailableDate?.date && + data.appointmentTime.split('-')[1] === valueStartTime + ); + const isNextScheduled = scheduledAppointments.find( + (data) => + data.appointmentDate === nextAvailableDate?.date && + data.appointmentTime.split('-')[0] === valueEndTime + ); + if (!(isPriorScheduled && isNextScheduled)) setScheduledAppointments( scheduledAppointments.filter( (data) => data.appointmentTime !== value diff --git a/src/utils/availableSlots.ts b/src/utils/availableSlots.ts new file mode 100644 index 0000000..5bc0e8a --- /dev/null +++ b/src/utils/availableSlots.ts @@ -0,0 +1,51 @@ +import { IAvailableDates } from '@dgreasi/react-native-time-slot-picker'; + +function generateTimeSlots(startDate: Date, slotLength: number): string[] { + let times: string[] = []; // time array + let tt = + Math.ceil(startDate.getMinutes() / slotLength) * slotLength + + startDate.getHours() * 60; // start time + + // loop to increment the time and push results in array + for (let i = 0; tt < 24 * 60; i++) { + let hhStart = Math.floor(tt / 60) % 24; // getting hours of day in 0-24 format + let mmStart = tt % 60; // getting minutes of the hour in 0-59 format + let hhEnd = Math.floor((tt + slotLength) / 60) % 24; + let mmEnd = (tt + slotLength) % 60; + + times[i] = + ('0' + hhStart).slice(-2) + + ':' + + ('0' + mmStart).slice(-2) + + '-' + + ('0' + hhEnd).slice(-2) + + ':' + + ('0' + mmEnd).slice(-2); + tt = tt + slotLength; + } + + return times; +} + +export function generateAvailableDates( + numDays: number, + slotLength: number +): IAvailableDates[] { + let availableDates: IAvailableDates[] = []; + let currentDate = new Date(); + // Garantee that the time is 0:00:00 + currentDate.setHours(0, 0, 0, 0); + + // Generate the list of time slots starting from the current time + for (let i = 0; i < numDays; i++) { + let slotTimes = generateTimeSlots(currentDate, slotLength); + let dateStr = currentDate.toISOString(); + availableDates.push({ date: dateStr, slotTimes: slotTimes }); + + // Move to the next day + currentDate.setDate(currentDate.getDate() + 1); + currentDate.setHours(0, 0, 0, 0); // Reset time to the start of the next day + } + + return availableDates; +} From e1fff47075b3995aecbece4181586954eadfe067 Mon Sep 17 00:00:00 2001 From: gilteixeira Date: Sat, 30 Dec 2023 16:41:06 +0000 Subject: [PATCH 7/9] fix: consecutive nextSlot was not being preperly calculated. Similar to the last commit --- src/components/TimeSlots.tsx | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/components/TimeSlots.tsx b/src/components/TimeSlots.tsx index c652733..80a146d 100644 --- a/src/components/TimeSlots.tsx +++ b/src/components/TimeSlots.tsx @@ -111,9 +111,29 @@ const TimeSlots = ({ let nextAvailableDate = availableDates.find( (data) => data.date === selectedDay && - data.slotTimes.find( - (slotTime) => slotTime.split('-')[0] === valueEndTime - ) + data.slotTimes.find((slotTime) => { + const valueStartTimeSplit = valueStartTime?.split(':'); + const slotTimeSplit = slotTime.split('-')[0]?.split(':'); + if ( + valueStartTimeSplit && + slotTimeSplit && + valueStartTimeSplit[0] && + valueStartTimeSplit[1] && + slotTimeSplit[0] && + slotTimeSplit[1] + ) { + const isSlotAfterSelectedTime = + parseInt(valueStartTimeSplit[0], 10) * 60 + + parseInt(valueStartTimeSplit[1], 10) < + parseInt(slotTimeSplit[0], 10) * 60 + + parseInt(slotTimeSplit[1], 10); + return ( + slotTime.split('-')[0] === valueEndTime && + isSlotAfterSelectedTime + ); + } + return false; + }) ); if ( !nextAvailableDate && From 135e88dad4cc2c6284cc233a4f2629607319c7f4 Mon Sep 17 00:00:00 2001 From: gilteixeira Date: Sat, 30 Dec 2023 18:24:22 +0000 Subject: [PATCH 8/9] fix: fixed the util slot time generation function and exported it --- example/src/App.tsx | 4 ++-- src/components/TimeSlots.tsx | 4 ++++ src/index.tsx | 1 + src/utils/availableSlots.ts | 4 +--- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index a62091d..f5ddd25 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -5,16 +5,16 @@ import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; import { IAppointment, TimeSlotPicker, + generateAvailableDates, } from '@dgreasi/react-native-time-slot-picker'; import { SelectedTimeSlot } from './SelectedTimeSlot'; -import { generateAvailableDates } from '../../src/utils/availableSlots'; export default function App() { const [scheduledAppointments, setScheduledAppointments] = useState< IAppointment[] | undefined >(); const dummyAvailableDates = React.useMemo(() => { - return generateAvailableDates(3, 30); + return generateAvailableDates(2, 60); }, []); return ( diff --git a/src/components/TimeSlots.tsx b/src/components/TimeSlots.tsx index 80a146d..36ebff2 100644 --- a/src/components/TimeSlots.tsx +++ b/src/components/TimeSlots.tsx @@ -100,6 +100,8 @@ const TimeSlots = ({ multipleSelectionStrategy === 'consecutive' ) { priorAvailableDate = availableDates.find((data) => { + console.log('data.date', data.date); + console.log('priorDay.toISOString()', priorDay.toISOString()); return ( data.date === priorDay.toISOString() && data.slotTimes.find( @@ -157,6 +159,8 @@ const TimeSlots = ({ data.appointmentDate === nextAvailableDate?.date && data.appointmentTime.split('-')[0] === valueEndTime ); + console.log('isPriorScheduled', isPriorScheduled); + console.log('isNextScheduled', isNextScheduled); if (!(isPriorScheduled && isNextScheduled)) setScheduledAppointments( scheduledAppointments.filter( diff --git a/src/index.tsx b/src/index.tsx index 3dbfa82..8ac085d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,2 +1,3 @@ export { default as TimeSlotPicker } from './TimeSlotPicker'; export * from './interfaces/app.interface'; +export * from './utils/availableSlots'; diff --git a/src/utils/availableSlots.ts b/src/utils/availableSlots.ts index 5bc0e8a..4681a33 100644 --- a/src/utils/availableSlots.ts +++ b/src/utils/availableSlots.ts @@ -1,5 +1,4 @@ import { IAvailableDates } from '@dgreasi/react-native-time-slot-picker'; - function generateTimeSlots(startDate: Date, slotLength: number): string[] { let times: string[] = []; // time array let tt = @@ -33,12 +32,11 @@ export function generateAvailableDates( ): IAvailableDates[] { let availableDates: IAvailableDates[] = []; let currentDate = new Date(); - // Garantee that the time is 0:00:00 - currentDate.setHours(0, 0, 0, 0); // Generate the list of time slots starting from the current time for (let i = 0; i < numDays; i++) { let slotTimes = generateTimeSlots(currentDate, slotLength); + currentDate.setHours(0, 0, 0, 0); // Reset time to the start of the day let dateStr = currentDate.toISOString(); availableDates.push({ date: dateStr, slotTimes: slotTimes }); From 541dd3ab1747393500040ce33aa3e38df5a1d0f2 Mon Sep 17 00:00:00 2001 From: gilteixeira Date: Tue, 6 Feb 2024 13:51:16 +0000 Subject: [PATCH 9/9] docs: added comments to explain the logic --- src/components/TimeSlots.tsx | 42 ++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/components/TimeSlots.tsx b/src/components/TimeSlots.tsx index 36ebff2..bf4d675 100644 --- a/src/components/TimeSlots.tsx +++ b/src/components/TimeSlots.tsx @@ -32,6 +32,7 @@ const TimeSlots = ({ backgroundColor = theme.colors.white, }: Props) => { const onPress = (value: string) => { + // If no appointments are scheduled, add the selected time slot if (!scheduledAppointments || scheduledAppointments.length === 0) setScheduledAppointments([ { @@ -40,7 +41,10 @@ const TimeSlots = ({ }, ]); else { + // An appointment is already scheduled if (!multipleSelection) { + // Single time slot selection + // If the selected time slot is already scheduled, remove it if ( scheduledAppointments && scheduledAppointments[0] && @@ -48,10 +52,12 @@ const TimeSlots = ({ ) setScheduledAppointments([]); } else { + // Multiple time slot selection const isAlreadyScheduled = scheduledAppointments.find( (data) => data.appointmentTime === value ); if (isAlreadyScheduled) { + // non-consecutive strategy, just remove the selected time slot if (multipleSelectionStrategy === 'non-consecutive') setScheduledAppointments( scheduledAppointments.filter( @@ -59,11 +65,11 @@ const TimeSlots = ({ ) ); else { - // only remove if it is not consecutive + // Check if removing the time slot will break the consecutive rule const valueSplit = value.split('-'); const valueStartTime = valueSplit[0]; const valueEndTime = valueSplit[1]; - // find the prior and next consecutive time slots, they may be in the prior or next day, and check if they are scheculed + // find the prior and next consecutive time slots, they may be in different days const priorDay = new Date(selectedDay); priorDay.setDate(priorDay.getDate() - 1); const nextDay = new Date(selectedDay); @@ -74,6 +80,7 @@ const TimeSlots = ({ data.slotTimes.find((slotTime) => { const valueStartTimeSplit = valueStartTime?.split(':'); const slotTimeSplit = slotTime.split('-')[0]?.split(':'); + // Type safety check if ( valueStartTimeSplit && slotTimeSplit && @@ -82,11 +89,13 @@ const TimeSlots = ({ slotTimeSplit[0] && slotTimeSplit[1] ) { - const isSlotBeforeSelectedTime = + const valueMinutes = parseInt(valueStartTimeSplit[0], 10) * 60 + - parseInt(valueStartTimeSplit[1], 10) > + parseInt(valueStartTimeSplit[1], 10); + const slotMinutes = parseInt(slotTimeSplit[0], 10) * 60 + - parseInt(slotTimeSplit[1], 10); + parseInt(slotTimeSplit[1], 10); + const isSlotBeforeSelectedTime = slotMinutes < valueMinutes; return ( slotTime.split('-')[1] === valueStartTime && isSlotBeforeSelectedTime @@ -97,11 +106,10 @@ const TimeSlots = ({ ); if ( !priorAvailableDate && - multipleSelectionStrategy === 'consecutive' + multipleSelectionStrategy !== 'same-day-consecutive' ) { + // Check the prior day priorAvailableDate = availableDates.find((data) => { - console.log('data.date', data.date); - console.log('priorDay.toISOString()', priorDay.toISOString()); return ( data.date === priorDay.toISOString() && data.slotTimes.find( @@ -110,6 +118,7 @@ const TimeSlots = ({ ); }); } + // Check for the next available time slot in the same day let nextAvailableDate = availableDates.find( (data) => data.date === selectedDay && @@ -124,11 +133,14 @@ const TimeSlots = ({ slotTimeSplit[0] && slotTimeSplit[1] ) { - const isSlotAfterSelectedTime = + const valueMinutes = parseInt(valueStartTimeSplit[0], 10) * 60 + - parseInt(valueStartTimeSplit[1], 10) < + parseInt(valueStartTimeSplit[1], 10); + const timeSlotMinutes = parseInt(slotTimeSplit[0], 10) * 60 + - parseInt(slotTimeSplit[1], 10); + parseInt(slotTimeSplit[1], 10); + const isSlotAfterSelectedTime = + valueMinutes < timeSlotMinutes; return ( slotTime.split('-')[0] === valueEndTime && isSlotAfterSelectedTime @@ -139,8 +151,9 @@ const TimeSlots = ({ ); if ( !nextAvailableDate && - multipleSelectionStrategy === 'consecutive' + multipleSelectionStrategy !== 'same-day-consecutive' ) { + // Check the next day nextAvailableDate = availableDates.find( (data) => data.date === nextDay.toISOString() && @@ -149,6 +162,7 @@ const TimeSlots = ({ ) ); } + // If the slot isn't consecutive (either the first or last slot) and remove it const isPriorScheduled = scheduledAppointments.find( (data) => data.appointmentDate === priorAvailableDate?.date && @@ -159,8 +173,6 @@ const TimeSlots = ({ data.appointmentDate === nextAvailableDate?.date && data.appointmentTime.split('-')[0] === valueEndTime ); - console.log('isPriorScheduled', isPriorScheduled); - console.log('isNextScheduled', isNextScheduled); if (!(isPriorScheduled && isNextScheduled)) setScheduledAppointments( scheduledAppointments.filter( @@ -169,6 +181,7 @@ const TimeSlots = ({ ); } } else { + // New time slot if (multipleSelectionStrategy === 'non-consecutive') setScheduledAppointments([ ...scheduledAppointments, @@ -178,6 +191,7 @@ const TimeSlots = ({ }, ]); else { + // Check if it's consecutive const valueSplit = value.split('-'); const valueStartTime = valueSplit[0]; const valueEndTime = valueSplit[1];