refactor: booker component

This commit is contained in:
Morgan Vernay 2024-01-11 16:33:05 +02:00
parent 138dbd52d3
commit 556d6ea25b
7 changed files with 64 additions and 88 deletions

View File

@ -15,7 +15,7 @@ import { BookerLayouts } from "@calcom/prisma/zod-utils";
import { VerifyCodeDialog } from "../components/VerifyCodeDialog";
import { AvailableTimeSlots } from "./components/AvailableTimeSlots";
import { BookEventContainer, BookEventForm } from "./components/BookEventForm";
import { BookEventForm } from "./components/BookEventForm";
import { BookFormAsModal } from "./components/BookEventForm/BookFormAsModal";
import { EventMeta } from "./components/EventMeta";
import { Header } from "./components/Header";
@ -76,7 +76,7 @@ const BookerComponent = ({
duration,
});
const event = useEvent();
const { selectedTimeslot, setSelectedTimeslot, handleRemoveSlot, handleReserveSlot } = useSlots(event);
const { selectedTimeslot, setSelectedTimeslot } = useSlots(event);
const {
shouldShowFormInDialog,
hasDarkBackground,
@ -208,43 +208,38 @@ const BookerComponent = ({
return null;
}
const BookEventWizard = (
<BookEventContainer
onRemoveSelectedSlot={handleRemoveSlot}
onReserveSlot={handleReserveSlot}
event={event.data}>
<BookEventForm
key={key}
onCancel={() => {
setSelectedTimeslot(null);
if (seatedEventData.bookingUid) {
setSeatedEventData({ ...seatedEventData, bookingUid: undefined, attendees: undefined });
}
}}
onSubmit={renderConfirmNotVerifyEmailButtonCond ? handleBookEvent : handleVerifyEmail}
errorRef={bookerFormErrorRef}
errors={errors}
loadingStates={loadingStates}
renderConfirmNotVerifyEmailButtonCond={renderConfirmNotVerifyEmailButtonCond}
bookingForm={bookingForm as unknown as UseFormReturn<FieldValues, any>}
eventQuery={event}
rescheduleUid={rescheduleUid}>
<>
<VerifyCodeDialog
isOpenDialog={isEmailVerificationModalVisible}
setIsOpenDialog={setEmailVerificationModalVisible}
email={formEmail}
onSuccess={() => {
setVerifiedEmail(formEmail);
setEmailVerificationModalVisible(false);
handleBookEvent();
}}
isUserSessionRequiredToVerify={false}
/>
<RedirectToInstantMeetingModal hasInstantMeetingTokenExpired={hasInstantMeetingTokenExpired} />
</>
</BookEventForm>
</BookEventContainer>
const EventBooker = (
<BookEventForm
key={key}
onCancel={() => {
setSelectedTimeslot(null);
if (seatedEventData.bookingUid) {
setSeatedEventData({ ...seatedEventData, bookingUid: undefined, attendees: undefined });
}
}}
onSubmit={renderConfirmNotVerifyEmailButtonCond ? handleBookEvent : handleVerifyEmail}
errorRef={bookerFormErrorRef}
errors={errors}
loadingStates={loadingStates}
renderConfirmNotVerifyEmailButtonCond={renderConfirmNotVerifyEmailButtonCond}
bookingForm={bookingForm as unknown as UseFormReturn<FieldValues, any>}
eventQuery={event}
rescheduleUid={rescheduleUid}>
<>
<VerifyCodeDialog
isOpenDialog={isEmailVerificationModalVisible}
setIsOpenDialog={setEmailVerificationModalVisible}
email={formEmail}
onSuccess={() => {
setVerifiedEmail(formEmail);
setEmailVerificationModalVisible(false);
handleBookEvent();
}}
isUserSessionRequiredToVerify={false}
/>
<RedirectToInstantMeetingModal hasInstantMeetingTokenExpired={hasInstantMeetingTokenExpired} />
</>
</BookEventForm>
);
return (
@ -307,7 +302,7 @@ const BookerComponent = ({
"bg-default dark:bg-muted sticky top-0 z-10"
)}>
<Header
username={username}
isMyLink={Boolean(username === event?.data?.owner?.username)}
eventSlug={eventSlug}
enabledLayouts={bookerLayouts.enabledLayouts}
extraDays={layout === BookerLayouts.COLUMN_VIEW ? columnViewExtraDays.current : extraDays}
@ -342,7 +337,7 @@ const BookerComponent = ({
className="border-subtle sticky top-0 ml-[-1px] h-full p-6 md:w-[var(--booker-main-width)] md:border-l"
{...fadeInLeft}
visible={bookerState === "booking" && !shouldShowFormInDialog}>
{BookEventWizard}
{EventBooker}
</BookerSection>
<BookerSection
@ -406,7 +401,7 @@ const BookerComponent = ({
<BookFormAsModal
onCancel={() => setSelectedTimeslot(null)}
visible={bookerState === "booking" && shouldShowFormInDialog}>
{BookEventWizard}
{EventBooker}
</BookFormAsModal>
</>
);

View File

@ -3,11 +3,10 @@ import type {
IUseBookingFormLoadingStates,
} from "bookings/Booker/components/hooks/useBookingForm";
import type { TFunction } from "next-i18next";
import { useEffect, useState } from "react";
import { useState } from "react";
import type { FieldError } from "react-hook-form";
import type { UseFormReturn, FieldValues } from "react-hook-form";
import { MINUTES_TO_BOOK } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
import { Alert, Button, EmptyScreen, Form } from "@calcom/ui";
@ -29,38 +28,6 @@ type BookEventFormProps = {
renderConfirmNotVerifyEmailButtonCond: boolean;
};
type BookEventContainerProps = {
onReserveSlot: () => void;
onRemoveSelectedSlot: () => void;
event: useEventReturnType["data"];
children?: React.ReactNode;
};
export const BookEventContainer = ({
onReserveSlot,
onRemoveSelectedSlot,
event,
children,
}: BookEventContainerProps) => {
const timeslot = useBookerStore((state) => state.selectedTimeslot);
useEffect(() => {
onReserveSlot();
const interval = setInterval(() => {
onReserveSlot();
}, parseInt(MINUTES_TO_BOOK) * 60 * 1000 - 2000);
return () => {
onRemoveSelectedSlot();
clearInterval(interval);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [event?.id, timeslot]);
return <div className="flex h-full flex-col">{children}</div>;
};
export const BookEventForm = ({
onCancel,
eventQuery,

View File

@ -1 +1 @@
export { BookEventContainer, BookEventForm } from "./BookEventForm";
export { BookEventForm } from "./BookEventForm";

View File

@ -1,5 +1,4 @@
import { ChevronLeft, ChevronRight } from "lucide-react";
import { useSession } from "next-auth/react";
import { useCallback, useMemo } from "react";
import { shallow } from "zustand/shallow";
@ -20,20 +19,17 @@ export function Header({
isMobile,
enabledLayouts,
nextSlots,
username,
eventSlug,
ownerName,
isMyLink,
}: {
extraDays: number;
isMobile: boolean;
enabledLayouts: BookerLayouts[];
nextSlots: number;
username: string;
eventSlug: string;
ownerName?: string;
isMyLink: boolean;
}) {
const { t, i18n } = useLocale();
const session = useSession();
const [layout, setLayout] = useBookerStore((state) => [state.layout, state.setLayout], shallow);
const selectedDateString = useBookerStore((state) => state.selectedDate);
@ -64,7 +60,6 @@ export function Header({
<LayoutToggle onLayoutToggle={onLayoutToggle} layout={layout} enabledLayouts={enabledLayouts} />
);
};
const isMyLink = username === session?.data?.user.username; // TODO: check for if the user is the owner of the link
// In month view we only show the layout toggle.
if (isMonthView) {

View File

@ -119,7 +119,7 @@ export function OverlayCalendarContainer() {
shallow
);
const { data: session, status: sessionStatus } = useSession();
const { data: session } = useSession();
const setOverlayBusyDates = useOverlayCalendarStore((state) => state.setOverlayBusyDates);
const switchEnabled =
searchParams?.get("overlayCalendar") === "true" ||

View File

@ -66,7 +66,6 @@ export const useBookingForm = ({ event, hashedLink }: IUseBookingForm) => {
const username = useBookerStore((state) => state.username);
const routerQuery = useRouterQuery();
const searchParams = useSearchParams();
const verifiedEmail = useBookerStore((state) => state.verifiedEmail);
const bookingFormSchema = z
.object({
@ -307,9 +306,11 @@ export const useBookingForm = ({ event, hashedLink }: IUseBookingForm) => {
createInstantBookingMutation.error,
};
// A redirect is triggered on mutation success, so keep the loading state while it is happening.
const loadingStates = {
creatingBooking: createBookingMutation.isLoading,
creatingRecurringBooking: createRecurringBookingMutation.isLoading,
creatingBooking: createBookingMutation.isLoading || createBookingMutation.isSuccess,
creatingRecurringBooking:
createRecurringBookingMutation.isLoading || createRecurringBookingMutation.isSuccess,
creatingInstantBooking: createInstantBookingMutation.isLoading,
};

View File

@ -1,9 +1,11 @@
import { useEffect } from "react";
import { shallow } from "zustand/shallow";
import dayjs from "@calcom/dayjs";
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
import { useSlotReservationId } from "@calcom/features/bookings/Booker/useSlotReservationId";
import type { useEventReturnType } from "@calcom/features/bookings/Booker/utils/event";
import { MINUTES_TO_BOOK } from "@calcom/lib/constants";
import { trpc } from "@calcom/trpc";
export const useSlots = (event: useEventReturnType) => {
@ -45,6 +47,22 @@ export const useSlots = (event: useEventReturnType) => {
}
};
const timeslot = useBookerStore((state) => state.selectedTimeslot);
useEffect(() => {
handleReserveSlot();
const interval = setInterval(() => {
handleReserveSlot();
}, parseInt(MINUTES_TO_BOOK) * 60 * 1000 - 2000);
return () => {
handleRemoveSlot();
clearInterval(interval);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [event?.data?.id, timeslot]);
return {
selectedTimeslot,
setSelectedTimeslot,