Skip to content

Commit 8c8550e

Browse files
fix: switching between months leads to incorrect autoselect first available date (#10366)
Co-authored-by: Hariom Balhara <[email protected]>
1 parent c6246c9 commit 8c8550e

File tree

6 files changed

+69
-23
lines changed

6 files changed

+69
-23
lines changed

packages/features/bookings/Booker/components/DatePicker.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,8 @@ export const DatePicker = () => {
2424
return (
2525
<DatePickerComponent
2626
isLoading={schedule.isLoading}
27-
onChange={(date: Dayjs) => {
28-
setSelectedDate(date.format("YYYY-MM-DD"));
29-
}}
30-
onMonthChange={(date: Dayjs) => {
31-
setMonth(date.format("YYYY-MM"));
32-
setSelectedDate(date.format("YYYY-MM-DD"));
33-
}}
27+
onChange={(date) => setSelectedDate(date ? date.format("YYYY-MM-DD") : date)}
28+
onMonthChange={(date) => setMonth(date.format("YYYY-MM"))}
3429
includedDates={nonEmptyScheduleDays}
3530
locale={i18n.language}
3631
browsingDate={month ? dayjs(month) : undefined}

packages/features/bookings/Booker/store.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ export const useBookerStore = create<BookerStore>((set, get) => ({
160160
updateQueryParam("date", selectedDate ?? "");
161161

162162
// Setting month make sure small calendar in fullscreen layouts also updates.
163-
if (newSelection.month() !== currentSelection.month()) {
163+
// If selectedDate is null, prevents setting month to Invalid-Date
164+
if (selectedDate && newSelection.month() !== currentSelection.month() ) {
164165
set({ month: newSelection.format("YYYY-MM") });
165166
updateQueryParam("month", newSelection.format("YYYY-MM"));
166167
}
@@ -193,7 +194,6 @@ export const useBookerStore = create<BookerStore>((set, get) => ({
193194
setMonth: (month: string | null) => {
194195
set({ month, selectedTimeslot: null });
195196
updateQueryParam("month", month ?? "");
196-
get().setSelectedDate(null);
197197
},
198198
isTeamEvent: false,
199199
seatedEventData: {

packages/features/calendars/DatePicker.tsx

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export type DatePickerProps = {
1616
/** which day of the week to render the calendar. Usually Sunday (=0) or Monday (=1) - default: Sunday */
1717
weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
1818
/** Fires whenever a selected date is changed. */
19-
onChange: (date: Dayjs) => void;
19+
onChange: (date: Dayjs | null) => void;
2020
/** Fires when the month is changed. */
2121
onMonthChange?: (date: Dayjs) => void;
2222
/** which date or dates are currently selected (not tracked from here) */
@@ -30,7 +30,7 @@ export type DatePickerProps = {
3030
/** Defaults to [], which dates are not bookable. Array of valid dates like: ["2022-04-23", "2022-04-24"] */
3131
excludedDates?: string[];
3232
/** defaults to all, which dates are bookable (inverse of excludedDates) */
33-
includedDates?: string[];
33+
includedDates?: string[] | null;
3434
/** allows adding classes to the container */
3535
className?: string;
3636
/** Shows a small loading spinner next to the month name */
@@ -121,7 +121,7 @@ const Days = ({
121121
// Create placeholder elements for empty days in first week
122122
const weekdayOfFirst = browsingDate.date(1).day();
123123
const currentDate = minDate.utcOffset(browsingDate.utcOffset());
124-
const availableDates = (includedDates: string[] | undefined) => {
124+
const availableDates = (includedDates: string[] | undefined | null) => {
125125
const dates = [];
126126
const lastDateOfMonth = browsingDate.date(daysInMonth(browsingDate));
127127
for (
@@ -148,6 +148,21 @@ const Days = ({
148148
days.push(date);
149149
}
150150

151+
const daysToRenderForTheMonth = days.map((day) => {
152+
if (!day) return { day: null, disabled: true };
153+
return {
154+
day: day,
155+
disabled:
156+
(includedDates && !includedDates.includes(yyyymmdd(day))) || excludedDates.includes(yyyymmdd(day)),
157+
};
158+
});
159+
160+
useHandleInitialDateSelection({
161+
daysToRenderForTheMonth,
162+
selected,
163+
onChange: props.onChange,
164+
});
165+
151166
const [selectedDatesAndTimes] = useBookerStore((state) => [state.selectedDatesAndTimes], shallow);
152167

153168
const isActive = (day: dayjs.Dayjs) => {
@@ -177,7 +192,7 @@ const Days = ({
177192

178193
return (
179194
<>
180-
{days.map((day, idx) => (
195+
{daysToRenderForTheMonth.map(({ day, disabled }, idx) => (
181196
<div key={day === null ? `e-${idx}` : `day-${day.format()}`} className="relative w-full pt-[100%]">
182197
{day === null ? (
183198
<div key={`e-${idx}`} />
@@ -194,10 +209,7 @@ const Days = ({
194209
onClick={() => {
195210
props.onChange(day);
196211
}}
197-
disabled={
198-
(includedDates && !includedDates.includes(yyyymmdd(day))) ||
199-
excludedDates.includes(yyyymmdd(day))
200-
}
212+
disabled={disabled}
201213
active={isActive(day)}
202214
/>
203215
)}
@@ -293,4 +305,41 @@ const DatePicker = ({
293305
);
294306
};
295307

308+
/**
309+
* Takes care of selecting a valid date in the month if the selected date is not available in the month
310+
*/
311+
const useHandleInitialDateSelection = ({
312+
daysToRenderForTheMonth,
313+
selected,
314+
onChange,
315+
}: {
316+
daysToRenderForTheMonth: { day: Dayjs | null; disabled: boolean }[];
317+
selected: Dayjs | Dayjs[] | null | undefined;
318+
onChange: (date: Dayjs | null) => void;
319+
}) => {
320+
// Let's not do something for now in case of multiple selected dates as behaviour is unclear and it's not needed at the moment
321+
if (selected instanceof Array) {
322+
return;
323+
}
324+
const firstAvailableDateOfTheMonth = daysToRenderForTheMonth.find((day) => !day.disabled)?.day;
325+
326+
const isSelectedDateAvailable = selected
327+
? daysToRenderForTheMonth.some(({ day, disabled }) => {
328+
if (day && yyyymmdd(day) === yyyymmdd(selected) && !disabled) return true;
329+
})
330+
: false;
331+
332+
if (firstAvailableDateOfTheMonth) {
333+
// If selected date not available in the month, select the first available date of the month
334+
if (!isSelectedDateAvailable) {
335+
onChange(firstAvailableDateOfTheMonth);
336+
}
337+
} else {
338+
// No date is available and if we were asked to select something inform that it couldn't be selected. This would actually help in not showing the timeslots section(with No Time Available) when no date in the month is available
339+
if (selected) {
340+
onChange(null);
341+
}
342+
}
343+
};
344+
296345
export default DatePicker;

packages/features/embed/Embed.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,8 @@ const EmailEmbed = ({ eventType, username }: { eventType?: EventType; username:
229229
<div className="text-default text-sm">{t("select_date")}</div>
230230
<DatePicker
231231
isLoading={schedule.isLoading}
232-
onChange={(date: Dayjs) => {
233-
setSelectedDate(date.format("YYYY-MM-DD"));
234-
}}
235-
onMonthChange={(date: Dayjs) => {
232+
onChange={(date) => setSelectedDate(date ? date.format("YYYY-MM-DD") : date)}
233+
onMonthChange={(date) => {
236234
setMonth(date.format("YYYY-MM"));
237235
setSelectedDate(date.format("YYYY-MM-DD"));
238236
}}

packages/features/schedules/components/DateOverrideInputDialog.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ const DateOverrideForm = ({
5151

5252
const [selectedDates, setSelectedDates] = useState<Dayjs[]>(value ? [dayjs.utc(value[0].start)] : []);
5353

54-
const onDateChange = (newDate: Dayjs) => {
54+
const onDateChange = (newDate: Dayjs | null) => {
55+
// If no date is selected, do nothing
56+
if (!newDate) {
57+
return;
58+
}
5559
// If clicking on a selected date unselect it
5660
if (selectedDates.some((date) => yyyymmdd(date) === yyyymmdd(newDate))) {
5761
setSelectedDates(selectedDates.filter((date) => yyyymmdd(date) !== yyyymmdd(newDate)));

packages/features/schedules/lib/use-schedule/useNonEmptyScheduleDays.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useMemo } from "react";
33
import type { Slots } from "../use-schedule";
44

55
export const getNonEmptyScheduleDays = (slots?: Slots) => {
6-
if (typeof slots === "undefined") return [];
6+
if (typeof slots === "undefined") return null;
77
return Object.keys(slots).filter((day) => slots[day].length > 0);
88
};
99

0 commit comments

Comments
 (0)