Skip to content

fix: skip confirm step followup #19076

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
06dfd34
Fix: Resolved double-click issue on timeSlot in Booker Atom
SomayChauhan Feb 2, 2025
da0b6d7
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 3, 2025
1cecfa1
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 3, 2025
98d9af7
fix: show toast on booking error
SomayChauhan Feb 4, 2025
51e5c89
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 4, 2025
1355f03
fix: type error
SomayChauhan Feb 4, 2025
0319c5b
Prevent the automatic change of bookerState when all fields are filled.
SomayChauhan Feb 6, 2025
fa6659e
fix: when a user clicks on a new timeslot, any previously displayed "…
SomayChauhan Feb 6, 2025
0f78af6
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 6, 2025
b42f0b0
Merge branch 'followup/skip-confirm-step' of https://github.com/calco…
SomayChauhan Feb 6, 2025
55d00c4
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 6, 2025
e7b820c
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 7, 2025
1bd09a6
Update useSkipConfirmStep.ts
SomayChauhan Feb 10, 2025
97d3940
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 14, 2025
31e4020
fix: remove 2 states for slots
SomayChauhan Feb 14, 2025
40425d6
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 14, 2025
8d57607
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 14, 2025
08a2d4e
Update useSlotsForDate.ts
SomayChauhan Feb 14, 2025
be6f0f6
fix types
SomayChauhan Feb 14, 2025
0307425
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 14, 2025
b711be5
use common types
SomayChauhan Feb 14, 2025
039d554
Merge branch 'main' into followup/skip-confirm-step
Udit-takkar Feb 16, 2025
cfd28b3
Merge branch 'main' into followup/skip-confirm-step
Udit-takkar Feb 20, 2025
6b78a54
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 21, 2025
903b37f
fix: show VerifyCodeDialog and RedirectToInstantMeetingModal when ski…
SomayChauhan Feb 21, 2025
a866efe
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 21, 2025
ca8dcec
fix for instant bookings
SomayChauhan Feb 21, 2025
b3f99dd
Merge branch 'main' into followup/skip-confirm-step
SomayChauhan Feb 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 27 additions & 27 deletions packages/features/bookings/Booker/Booker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,12 @@ const BookerComponent = ({
}
};

const skipConfirmStep = useSkipConfirmStep(bookingForm, event?.data?.bookingFields);
const skipConfirmStep = useSkipConfirmStep(
bookingForm,
bookerState,
isInstantMeeting,
event?.data?.bookingFields
);

// Cloudflare Turnstile Captcha
const shouldRenderCaptcha = !!(
Expand All @@ -175,7 +180,8 @@ const BookerComponent = ({
useEffect(() => {
if (event.isPending) return setBookerState("loading");
if (!selectedDate) return setBookerState("selecting_date");
if (!selectedTimeslot || skipConfirmStep) return setBookerState("selecting_time");
if (!selectedTimeslot) return setBookerState("selecting_time");
if (selectedTimeslot && skipConfirmStep && !isInstantMeeting) return setBookerState("selecting_time");
return setBookerState("booking");
}, [event, selectedDate, selectedTimeslot, setBookerState, skipConfirmStep]);

Expand Down Expand Up @@ -212,22 +218,6 @@ const BookerComponent = ({
isVerificationCodeSending={isVerificationCodeSending}
isPlatform={isPlatform}>
<>
{verifyCode && formEmail ? (
<VerifyCodeDialog
isOpenDialog={isEmailVerificationModalVisible}
setIsOpenDialog={setEmailVerificationModalVisible}
email={formEmail}
isUserSessionRequiredToVerify={false}
verifyCodeWithSessionNotRequired={verifyCode.verifyCodeWithSessionNotRequired}
verifyCodeWithSessionRequired={verifyCode.verifyCodeWithSessionRequired}
error={verifyCode.error}
resetErrors={verifyCode.resetErrors}
isPending={verifyCode.isPending}
setIsPending={verifyCode.setIsPending}
/>
) : (
<></>
)}
{!isPlatform && (
<RedirectToInstantMeetingModal
expiryTime={expiryTime}
Expand All @@ -253,26 +243,17 @@ const BookerComponent = ({
event,
expiryTime,
extraOptions,
formEmail,
formErrors,
handleBookEvent,
handleVerifyEmail,
isEmailVerificationModalVisible,
key,
loadingStates,
onGoBackInstantMeeting,
renderConfirmNotVerifyEmailButtonCond,
rescheduleUid,
seatedEventData,
setEmailVerificationModalVisible,
setSeatedEventData,
setSelectedTimeslot,
verifyCode?.error,
verifyCode?.isPending,
verifyCode?.resetErrors,
verifyCode?.setIsPending,
verifyCode?.verifyCodeWithSessionNotRequired,
verifyCode?.verifyCodeWithSessionRequired,
isPlatform,
shouldRenderCaptcha,
isVerificationCodeSending,
Expand Down Expand Up @@ -532,6 +513,25 @@ const BookerComponent = ({
)}
</div>

<>
{verifyCode && formEmail ? (
<VerifyCodeDialog
isOpenDialog={isEmailVerificationModalVisible}
setIsOpenDialog={setEmailVerificationModalVisible}
email={formEmail}
isUserSessionRequiredToVerify={false}
verifyCodeWithSessionNotRequired={verifyCode.verifyCodeWithSessionNotRequired}
verifyCodeWithSessionRequired={verifyCode.verifyCodeWithSessionRequired}
error={verifyCode.error}
resetErrors={verifyCode.resetErrors}
isPending={verifyCode.isPending}
setIsPending={verifyCode.setIsPending}
/>
) : (
<></>
)}
</>

<BookFormAsModal
onCancel={() => setSelectedTimeslot(null)}
visible={bookerState === "booking" && shouldShowFormInDialog}>
Expand Down
81 changes: 53 additions & 28 deletions packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { useRef } from "react";
import { useCallback, useMemo, useRef } from "react";

import dayjs from "@calcom/dayjs";
import { AvailableTimes, AvailableTimesSkeleton } from "@calcom/features/bookings";
import type { IUseBookingLoadingStates } from "@calcom/features/bookings/Booker/components/hooks/useBookings";
import type { BookerEvent } from "@calcom/features/bookings/types";
import { useNonEmptyScheduleDays } from "@calcom/features/schedules";
import type { Slot } from "@calcom/features/schedules";
import { useSlotsForAvailableDates } from "@calcom/features/schedules/lib/use-schedule/useSlotsForDate";
import { classNames } from "@calcom/lib";
import { BookerLayouts } from "@calcom/prisma/zod-utils";

import { AvailableTimesHeader } from "../../components/AvailableTimesHeader";
import { useBookerStore } from "../store";
import type { useScheduleForEventReturnType } from "../utils/event";
import { getQueryParam } from "../utils/query-param";

type AvailableTimeSlotsProps = {
extraDays?: number;
Expand Down Expand Up @@ -55,52 +57,73 @@ export const AvailableTimeSlots = ({
isLoading,
customClassNames,
skipConfirmStep,
seatsPerTimeSlot,
onSubmit,
...props
}: AvailableTimeSlotsProps) => {
const selectedDate = useBookerStore((state) => state.selectedDate);

const setSelectedTimeslot = useBookerStore((state) => state.setSelectedTimeslot);
const setSeatedEventData = useBookerStore((state) => state.setSeatedEventData);
const date = selectedDate || dayjs().format("YYYY-MM-DD");
const [layout] = useBookerStore((state) => [state.layout]);
const isColumnView = layout === BookerLayouts.COLUMN_VIEW;
const containerRef = useRef<HTMLDivElement | null>(null);

const onTimeSelect = (
time: string,
attendees: number,
seatsPerTimeSlot?: number | null,
bookingUid?: string
) => {
setSelectedTimeslot(time);
if (seatsPerTimeSlot) {
setSeatedEventData({
seatsPerTimeSlot,
attendees,
bookingUid,
showAvailableSeatsCount,
});
}
if (skipConfirmStep) {
onSubmit(time);
}
return;
};

const nonEmptyScheduleDays = useNonEmptyScheduleDays(schedule?.slots);
const nonEmptyScheduleDaysFromSelectedDate = nonEmptyScheduleDays.filter(
(slot) => dayjs(selectedDate).diff(slot, "day") <= 0
);

// Creates an array of dates to fetch slots for.
// If `extraDays` is passed in, we will extend the array with the next `extraDays` days.
const dates = !extraDays
? [date]
: nonEmptyScheduleDaysFromSelectedDate.length > 0
? nonEmptyScheduleDaysFromSelectedDate.slice(0, extraDays)
: [];
const dates = useMemo(() => {
if (!extraDays) return [date];
if (nonEmptyScheduleDaysFromSelectedDate.length > 0) {
return nonEmptyScheduleDaysFromSelectedDate.slice(0, extraDays);
}
return [];
}, [date, extraDays, nonEmptyScheduleDaysFromSelectedDate]);

const slotsPerDay = useSlotsForAvailableDates(dates, schedule?.slots);
const { slotsPerDay, toggleConfirmButton } = useSlotsForAvailableDates(dates, schedule?.slots);

const overlayCalendarToggled =
getQueryParam("overlayCalendar") === "true" || localStorage.getItem("overlayCalendarSwitchDefault");

const onTimeSelect = useCallback(
(time: string, attendees: number, seatsPerTimeSlot?: number | null, bookingUid?: string) => {
setSelectedTimeslot(time);
if (seatsPerTimeSlot) {
setSeatedEventData({
seatsPerTimeSlot,
attendees,
bookingUid,
showAvailableSeatsCount,
});
}
if (skipConfirmStep) {
onSubmit(time);
}
return;
},
[onSubmit, setSeatedEventData, setSelectedTimeslot, skipConfirmStep, showAvailableSeatsCount]
);

const handleSlotClick = useCallback(
(selectedSlot: Slot, isOverlapping: boolean) => {
if ((overlayCalendarToggled && isOverlapping) || skipConfirmStep) {
toggleConfirmButton(selectedSlot);
} else {
onTimeSelect(
selectedSlot.time,
selectedSlot?.attendees || 0,
seatsPerTimeSlot,
selectedSlot.bookingUid
);
}
},
[overlayCalendarToggled, onTimeSelect, seatsPerTimeSlot, skipConfirmStep, toggleConfirmButton]
);

return (
<>
Expand Down Expand Up @@ -150,6 +173,8 @@ export const AvailableTimeSlots = ({
slots={slots.slots}
showAvailableSeatsCount={showAvailableSeatsCount}
skipConfirmStep={skipConfirmStep}
seatsPerTimeSlot={seatsPerTimeSlot}
handleSlotClick={handleSlotClick}
{...props}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ export const BookEventForm = ({
/>
</div>
)}
{/* Cloudflare Turnstile Captcha */}
{!isPlatform && (
<div className="text-subtle my-3 w-full text-xs">
<Trans
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { useState, useEffect } from "react";
import type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/components/hooks/useBookingForm";
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
import { getBookingResponsesSchemaWithOptionalChecks } from "@calcom/features/bookings/lib/getBookingResponsesSchema";
import type { BookerEvent } from "@calcom/features/bookings/types";

import type { BookerEvent } from "../../../types";
import type { BookerState } from "../../types";

const useSkipConfirmStep = (
bookingForm: UseBookingFormReturnType["bookingForm"],
bookerState: BookerState,
isInstantMeeting: boolean,
bookingFields?: BookerEvent["bookingFields"]
) => {
const bookingFormValues = bookingForm.getValues();
Expand Down Expand Up @@ -34,8 +38,8 @@ const useSkipConfirmStep = (
}
};

checkSkipStep();
}, [bookingFormValues, bookingFields, rescheduleUid]);
bookerState === "selecting_time" && !isInstantMeeting && checkSkipStep();
}, [bookingFormValues, bookingFields, rescheduleUid, bookerState]);

return canSkip;
};
Expand Down
Loading
Loading