refactor: move Booker hooks from packages/features to apps/web/modules (#27343)

* refactor: move Booker hooks from packages/features to apps/web/modules

Migrate the following Booker hooks:
- useOverlayCalendar
- useSkipConfirmStep
- useInitializeWeekStart
- useIsQuickAvailabilityCheckFeatureEnabled
- useDecoyBooking

These hooks are only imported by apps/web files, making them safe to move
without creating circular dependencies.

Part of the tRPC-driven UI migration from packages/features to apps/web/modules.

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* fix: revert useIsQuickAvailabilityCheckFeatureEnabled move and fix import paths

- Reverted useIsQuickAvailabilityCheckFeatureEnabled.ts back to packages/features since it's imported by useSlots.ts in packages/features (would create circular dependency)
- Fixed import paths in useOverlayCalendar.ts to use @calcom/features paths
- Fixed import paths in useSkipConfirmStep.ts to use @calcom/features paths
- Updated Booker.tsx to import useIsQuickAvailabilityCheckFeatureEnabled from original location

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* fix: use @calcom/web alias for hook imports to fix vitest resolution

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* refactor

* migrate more hooks

* fix

* mv types

* fix

* fix

* fix

* fix

* revert: remove formatting-only changes to make PR easier to review

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* fix

* fix

* fix

* revert: restore original import ordering in moved hooks

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* fix imports

* fix

* fix

* wip

* wip

* wip

* wip

* wip

* wip

* fix: add isBrowser guard to useSlotsViewOnSmallScreen hook

Address Cubic AI review feedback (confidence 9/10) by adding the isBrowser
guard to the useSlotsViewOnSmallScreen hook to follow the file's client-only
contract and avoid executing during SSR/prerendering.

Co-Authored-By: unknown <>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Benny Joo
2026-01-31 07:33:33 -03:00
committed by GitHub
co-authored by unknown Devin AI
parent 5476c38f3b
commit 73f51920c5
46 changed files with 214 additions and 214 deletions
@@ -4,7 +4,7 @@ import { useParams } from "next/navigation";
import dayjs from "@calcom/dayjs";
import { DecoyBookingSuccessCard } from "~/bookings/components/DecoyBookingSuccessCard";
import { useDecoyBooking } from "@calcom/features/bookings/Booker/components/hooks/useDecoyBooking";
import { useDecoyBooking } from "~/bookings/hooks/useDecoyBooking";
export default function BookingSuccessful() {
const params = useParams();
@@ -6,7 +6,7 @@ import {
AvailableTimesSkeleton,
} from "@calcom/web/modules/bookings/components/AvailableTimes";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import type { IUseBookingLoadingStates } from "@calcom/features/bookings/Booker/components/hooks/useBookings";
import type { IUseBookingLoadingStates } from "../hooks/useBookings";
import type { BookerEvent } from "@calcom/features/bookings/types";
import type { Slot } from "@calcom/features/schedules/lib/use-schedule/types";
import { useNonEmptyScheduleDays } from "@calcom/features/schedules/lib/use-schedule/useNonEmptyScheduleDays";
@@ -9,7 +9,7 @@ import dayjs from "@calcom/dayjs";
import type { IOutOfOfficeData } from "@calcom/features/availability/lib/getUserAvailability";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { OutOfOfficeInSlots } from "./OutOfOfficeInSlots";
import type { IUseBookingLoadingStates } from "@calcom/features/bookings/Booker/components/hooks/useBookings";
import type { IUseBookingLoadingStates } from "../hooks/useBookings";
import type { BookerEvent } from "@calcom/features/bookings/types";
import type { Slot } from "@calcom/features/schedules/lib/use-schedule/types";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -19,7 +19,7 @@ import { Button } from "@calcom/ui/components/button";
import { Icon } from "@calcom/ui/components/icon";
import { SkeletonText } from "@calcom/ui/components/skeleton";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import { useBookerTime } from "@calcom/features/bookings/Booker/hooks/useBookerTime";
import { getQueryParam } from "@calcom/features/bookings/Booker/utils/query-param";
import { useCheckOverlapWithOverlay } from "@calcom/features/bookings/lib/useCheckOverlapWithOverlay";
import type { Slots } from "@calcom/features/bookings/types";
@@ -18,9 +18,9 @@ import { EmptyScreen } from "@calcom/ui/components/empty-screen";
import { Form } from "@calcom/ui/components/form";
import { formatEventFromTime } from "@calcom/features/bookings/Booker/utils/dates";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/components/hooks/useBookingForm";
import type { IUseBookingErrors, IUseBookingLoadingStates } from "@calcom/features/bookings/Booker/components/hooks/useBookings";
import { useBookerTime } from "@calcom/features/bookings/Booker/hooks/useBookerTime";
import type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/hooks/useBookingForm";
import type { IUseBookingErrors, IUseBookingLoadingStates } from "../../hooks/useBookings";
import { BookingFields } from "./BookingFields";
import { FormSkeleton } from "./Skeleton";
@@ -12,7 +12,7 @@ import { DialogContent } from "@calcom/ui/components/dialog";
import { getDurationFormatted } from "../event-meta/Duration";
import { FromTime } from "@calcom/features/bookings/Booker/utils/dates";
import { useEvent } from "@calcom/features/bookings/Booker/utils/event";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import { useBookerTime } from "@calcom/features/bookings/Booker/hooks/useBookerTime";
const BookEventFormWrapper = ({ children, onCancel }: { onCancel: () => void; children: ReactNode }) => {
const { data } = useEvent();
@@ -35,7 +35,8 @@ import "@calcom/dayjs/__mocks__";
import "@calcom/web/modules/auth/components/Turnstile";
import { render, screen } from "@calcom/features/bookings/Booker/__tests__/test-utils";
import type { BookerProps, WrappedBookerProps } from "@calcom/features/bookings/Booker/types";
import type { BookerProps } from "@calcom/features/bookings/Booker/types";
import type { WrappedBookerProps } from "../types";
import { Booker } from "./Booker";
vi.mock("framer-motion", async (importOriginal) => {
@@ -6,15 +6,16 @@ import { useEmbedUiConfig } from "@calcom/embed-core/embed-iframe";
import { updateEmbedBookerState } from "@calcom/embed-core/src/embed-iframe";
import TurnstileCaptcha from "@calcom/web/modules/auth/components/Turnstile";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useIsQuickAvailabilityCheckFeatureEnabled } from "@calcom/features/bookings/Booker/components/hooks/useIsQuickAvailabilityCheckFeatureEnabled";
import useSkipConfirmStep from "@calcom/features/bookings/Booker/components/hooks/useSkipConfirmStep";
import { useIsQuickAvailabilityCheckFeatureEnabled } from "../hooks/useIsQuickAvailabilityCheckFeatureEnabled";
import { useSkipConfirmStep } from "@calcom/web/modules/bookings/hooks/useSkipConfirmStep";
import {
fadeInLeft,
getBookerSizeClassNames,
useBookerResizeAnimation,
} from "@calcom/features/bookings/Booker/config";
import framerFeatures from "@calcom/features/bookings/Booker/framer-features";
import type { BookerProps, WrappedBookerProps } from "@calcom/features/bookings/Booker/types";
import type { BookerProps } from "@calcom/features/bookings/Booker/types";
import type { WrappedBookerProps } from "../types";
import { isBookingDryRun } from "@calcom/features/bookings/Booker/utils/isBookingDryRun";
import { isTimeSlotAvailable } from "@calcom/features/bookings/Booker/utils/isTimeslotAvailable";
import { getQueryParam } from "@calcom/features/bookings/Booker/utils/query-param";
@@ -8,13 +8,13 @@ import {
useBookerStoreContext,
useInitializeBookerStoreContext,
} from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useBookerLayout } from "@calcom/features/bookings/Booker/components/hooks/useBookerLayout";
import { useBookingForm } from "@calcom/features/bookings/Booker/components/hooks/useBookingForm";
import { useBookings } from "@calcom/features/bookings/Booker/components/hooks/useBookings";
import { useCalendars } from "@calcom/features/bookings/Booker/components/hooks/useCalendars";
import { useSlots } from "@calcom/features/bookings/Booker/components/hooks/useSlots";
import { useVerifyCode } from "@calcom/features/bookings/Booker/components/hooks/useVerifyCode";
import { useVerifyEmail } from "@calcom/features/bookings/Booker/components/hooks/useVerifyEmail";
import { useBookerLayout } from "@calcom/features/bookings/Booker/hooks/useBookerLayout";
import { useBookingForm } from "@calcom/features/bookings/Booker/hooks/useBookingForm";
import { useBookings } from "../hooks/useBookings";
import { useCalendars } from "../hooks/useCalendars";
import { useSlots } from "../hooks/useSlots";
import { useVerifyCode } from "../hooks/useVerifyCode";
import { useVerifyEmail } from "../hooks/useVerifyEmail";
import { useInitializeBookerStore } from "@calcom/features/bookings/Booker/store";
import { useEvent, useScheduleForEvent } from "@calcom/features/bookings/Booker/utils/event";
import { useBrandColors } from "@calcom/features/bookings/Booker/utils/use-brand-colors";
@@ -10,7 +10,7 @@ import { weekdayToWeekIndex } from "@calcom/lib/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { User } from "@calcom/prisma/client";
import type { PeriodData } from "@calcom/types/Event";
import { useSlotsViewOnSmallScreen } from "@calcom/features/bookings/Booker/components/hooks/useSlotsViewOnSmallScreen";
import { useSlotsViewOnSmallScreen } from "@calcom/embed-core/embed-iframe";
import type { Slots } from "@calcom/features/bookings/types";
@@ -1,6 +1,6 @@
import { Timezone as PlatformTimezoneSelect } from "@calcom/atoms/timezone";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import { useBookerTime } from "@calcom/features/bookings/Booker/hooks/useBookerTime";
import { fadeInUp } from "@calcom/features/bookings/Booker/config";
import type { Timezone } from "@calcom/features/bookings/Booker/types";
import { FromToTime } from "@calcom/features/bookings/Booker/utils/dates";
@@ -5,7 +5,7 @@ import { useIsPlatform } from "@calcom/atoms/hooks/useIsPlatform";
import dayjs from "@calcom/dayjs";
import { useIsEmbed } from "@calcom/embed-core/embed-iframe";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useInitializeWeekStart } from "@calcom/features/bookings/Booker/components/hooks/useInitializeWeekStart";
import { useInitializeWeekStart } from "@calcom/web/modules/bookings/hooks/useInitializeWeekStart";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { formatDateTime } from "@calcom/lib/dateTimeFormatter";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -2,8 +2,8 @@ import { useMemo, useEffect } from "react";
import dayjs from "@calcom/dayjs";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useAvailableTimeSlots } from "@calcom/features/bookings/Booker/components/hooks/useAvailableTimeSlots";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import { useAvailableTimeSlots } from "@calcom/features/bookings/Booker/hooks/useAvailableTimeSlots";
import { useBookerTime } from "@calcom/features/bookings/Booker/hooks/useBookerTime";
import type { BookerEvent } from "@calcom/features/bookings/types";
import { Calendar } from "@calcom/web/modules/calendars/weeklyview/components/Calendar";
import type { CalendarEvent } from "@calcom/features/calendars/weeklyview/types/events";
@@ -1,6 +1,6 @@
import { useIsPlatform } from "@calcom/atoms/hooks/useIsPlatform";
import { useOverlayCalendar } from "@calcom/features/bookings/Booker/components/hooks/useOverlayCalendar";
import type { WrappedBookerPropsMain } from "@calcom/features/bookings/Booker/types";
import { useOverlayCalendar } from "@calcom/web/modules/bookings/hooks/useOverlayCalendar";
import type { WrappedBookerPropsMain } from "../../types";
import { useEffect } from "react";
import { OverlayCalendarContinueModal } from "./OverlayCalendarContinueModal";
import { OverlayCalendarSettingsModal } from "./OverlayCalendarSettingsModal";
@@ -11,7 +11,7 @@ import { Badge } from "@calcom/ui/components/badge";
import { Input } from "@calcom/ui/components/form";
import { Tooltip } from "@calcom/ui/components/tooltip";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import { useBookerTime } from "@calcom/features/bookings/Booker/hooks/useBookerTime";
export const EventOccurences = ({ event }: { event: Pick<BookerEvent, "recurringEvent"> }) => {
const maxOccurences = event.recurringEvent?.count || null;
@@ -26,8 +26,8 @@ import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc/react";
import { showToast } from "@calcom/ui/components/toast";
import { useBookingSuccessRedirect } from "../../../lib/bookingSuccessRedirect";
import type { UseBookingFormReturnType } from "./useBookingForm";
import { useBookingSuccessRedirect } from "@calcom/features/bookings/lib/bookingSuccessRedirect";
import type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/hooks/useBookingForm";
export interface IUseBookings {
event: {
@@ -1,12 +1,13 @@
import { useTimePreferences } from "@calcom/features/bookings/lib";
import { localStorage } from "@calcom/lib/webstorage";
import { trpc } from "@calcom/trpc/react";
import { useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";
import { shallow } from "zustand/shallow";
import { useBookerStore } from "../../store";
import type { ToggledConnectedCalendars } from "../../types";
import { useOverlayCalendarStore } from "../OverlayCalendar/store";
import { useOverlayCalendarStore } from "@calcom/features/bookings/Booker/components/OverlayCalendar/store";
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
import type { ToggledConnectedCalendars } from "@calcom/features/bookings/Booker/types";
import { useTimePreferences } from "@calcom/features/bookings/lib";
import { localStorage } from "@calcom/lib/webstorage";
import { trpc } from "@calcom/trpc/react";
type UseCalendarsProps = {
hasSession: boolean;
@@ -1,19 +1,14 @@
import { useEffect } from "react";
import dayjs from "@calcom/dayjs";
import { useSlotsViewOnSmallScreen } from "@calcom/embed-core/embed-iframe";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useSlotsViewOnSmallScreen } from "@calcom/features/bookings/Booker/components/hooks/useSlotsViewOnSmallScreen";
export const useInitializeWeekStart = (
isPlatform: boolean,
isCalendarView: boolean
) => {
export const useInitializeWeekStart = (isPlatform: boolean, isCalendarView: boolean) => {
const slotsViewOnSmallScreen = useSlotsViewOnSmallScreen();
const today = dayjs();
const weekStart = today.startOf("week").format("YYYY-MM-DD");
const setSelectedDate = useBookerStoreContext(
(state) => state.setSelectedDate
);
const setSelectedDate = useBookerStoreContext((state) => state.setSelectedDate);
useEffect(() => {
// don't auto-select date if slots view on small screen is enabled
@@ -1,9 +1,8 @@
import { useEffect, useRef } from "react";
import { isVisitorWithinPercentage } from "@calcom/features/bookings/Booker/utils/isFeatureEnabledForVisitor";
import { PUBLIC_QUICK_AVAILABILITY_ROLLOUT } from "@calcom/lib/constants";
import { isVisitorWithinPercentage } from "../../utils/isFeatureEnabledForVisitor";
export const useIsQuickAvailabilityCheckFeatureEnabled = () => {
const isQuickAvailabilityCheckFeatureEnabledRef = useRef(
isVisitorWithinPercentage({ percentage: PUBLIC_QUICK_AVAILABILITY_ROLLOUT })
@@ -1,10 +1,13 @@
import dayjs from "@calcom/dayjs";
import { useTimePreferences } from "@calcom/features/bookings/lib";
import { useEffect, useState } from "react";
import { shallow } from "zustand/shallow";
import type { ToggledConnectedCalendars, WrappedBookerPropsMain } from "../../types";
import { useOverlayCalendarStore } from "../OverlayCalendar/store";
import { useLocalSet } from "./useLocalSet";
import dayjs from "@calcom/dayjs";
import { useOverlayCalendarStore } from "@calcom/features/bookings/Booker/components/OverlayCalendar/store";
import type { ToggledConnectedCalendars } from "@calcom/features/bookings/Booker/types";
import { useTimePreferences } from "@calcom/features/bookings/lib";
import type { WrappedBookerPropsMain } from "../types";
import { useLocalSet } from "@calcom/features/bookings/Booker/hooks/useLocalSet";
export const useOverlayCalendar = ({
connectedCalendars,
@@ -1,11 +1,13 @@
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 { useEffect, useState } from "react";
import type { BookerEvent } from "../../../types";
import type { BookerState } from "../../types";
const useSkipConfirmStep = (
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
import type { BookerState } from "@calcom/features/bookings/Booker/types";
import { getBookingResponsesSchemaWithOptionalChecks } from "@calcom/features/bookings/lib/getBookingResponsesSchema";
import type { BookerEvent } from "@calcom/features/bookings/types";
import type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/hooks/useBookingForm";
export const useSkipConfirmStep = (
bookingForm: UseBookingFormReturnType["bookingForm"],
bookerState: BookerState,
isInstantMeeting: boolean,
@@ -45,5 +47,3 @@ const useSkipConfirmStep = (
return canSkip;
};
export default useSkipConfirmStep;
@@ -12,12 +12,9 @@ import {
} from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { trpc } from "@calcom/trpc/react";
import type { TIsAvailableOutputSchema } from "@calcom/trpc/server/routers/viewer/slots/isAvailable.schema";
import type { QuickAvailabilityCheck } from "@calcom/features/bookings/Booker/types";
import { useIsQuickAvailabilityCheckFeatureEnabled } from "./useIsQuickAvailabilityCheckFeatureEnabled";
export type QuickAvailabilityCheck = TIsAvailableOutputSchema["slots"][number];
const useQuickAvailabilityChecks = ({
eventTypeId,
eventDuration,
@@ -51,7 +48,7 @@ const useQuickAvailabilityChecks = ({
{
slots: slotsToCheck,
// enabled flag can't be true if eventTypeId is nullish
eventTypeId: eventTypeId!,
},
{
+92
View File
@@ -1,7 +1,17 @@
import type { DataTableRow } from "@calcom/features/data-table/lib/separator";
import type { RouterOutputs } from "@calcom/trpc/react";
import type { BookerEventQuery } from "@calcom/features/bookings/types";
import type { useScheduleForEventReturnType } from "@calcom/features/bookings/Booker/utils/event";
import type { ToggledConnectedCalendars, CustomClassNames } from "@calcom/features/bookings/Booker/types";
import type { EventBusyDate } from "@calcom/types/Calendar";
import type { validStatuses } from "./lib/validStatuses";
import type { UseBookerLayoutType } from "@calcom/features/bookings/Booker/hooks/useBookerLayout";
import type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/hooks/useBookingForm";
import type { UseBookingsReturnType } from "./hooks/useBookings";
import type { UseSlotsReturnType } from "./hooks/useSlots";
import type { UseVerifyCodeReturnType } from "./hooks/useVerifyCode";
import type { UseVerifyEmailReturnType } from "./hooks/useVerifyEmail";
export type BookingsGetOutput = RouterOutputs["viewer"]["bookings"]["get"];
@@ -26,3 +36,85 @@ export type BookingRowData = {
export type RowData = DataTableRow<BookingRowData>;
export type BookingListingStatus = (typeof validStatuses)[number];
export type { UseBookerLayoutType } from "@calcom/features/bookings/Booker/hooks/useBookerLayout";
export type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/hooks/useBookingForm";
export type { UseBookingsReturnType } from "./hooks/useBookings";
export type { UseSlotsReturnType } from "./hooks/useSlots";
export type { UseVerifyCodeReturnType } from "./hooks/useVerifyCode";
export type { UseVerifyEmailReturnType } from "./hooks/useVerifyEmail";
export type WrappedBookerPropsMain = {
sessionUsername?: string | null;
rescheduleUid: string | null;
rescheduledBy: string | null;
bookingUid: string | null;
isRedirect: boolean;
fromUserNameRedirected: string;
hasSession: boolean;
onGoBackInstantMeeting: () => void;
onConnectNowInstantMeeting: () => void;
onOverlayClickNoCalendar: () => void;
onClickOverlayContinue: () => void;
onOverlaySwitchStateChange: (state: boolean) => void;
extraOptions: Record<string, string | string[]>;
bookings: UseBookingsReturnType;
slots: UseSlotsReturnType;
calendars: {
overlayBusyDates?: EventBusyDate[];
isOverlayCalendarEnabled: boolean;
connectedCalendars: {
calendars?: {
name?: string;
integrationTitle?: string;
externalId: string;
primary: boolean | null;
}[];
credentialId: number;
delegationCredentialId?: string | null;
cacheUpdatedAt: null;
error?: {
message: string | null;
};
primary?: {
email?: string;
};
integration: {
slug: string;
name?: string | undefined;
title?: string;
logo: string;
};
}[];
loadingConnectedCalendar: boolean;
onToggleCalendar: (calendarsToLoad: Set<ToggledConnectedCalendars>) => void;
};
bookerForm: UseBookingFormReturnType;
event: BookerEventQuery;
schedule: useScheduleForEventReturnType;
bookerLayout: UseBookerLayoutType;
verifyEmail: UseVerifyEmailReturnType;
customClassNames?: CustomClassNames;
isBookingDryRun?: boolean;
renderCaptcha?: boolean;
confirmButtonDisabled?: boolean;
};
export type WrappedBookerPropsForPlatform = WrappedBookerPropsMain & {
isPlatform: true;
verifyCode: undefined;
customClassNames?: CustomClassNames;
timeZones?: import("@calcom/features/bookings/Booker/types").Timezone[];
roundRobinHideOrgAndTeam?: boolean;
hideOrgTeamAvatar?: boolean;
};
export type WrappedBookerPropsForWeb = WrappedBookerPropsMain & {
isPlatform: false;
verifyCode: UseVerifyCodeReturnType;
timeZones?: import("@calcom/features/bookings/Booker/types").Timezone[];
roundRobinHideOrgAndTeam?: boolean;
hideOrgTeamAvatar?: boolean;
};
export type WrappedBookerProps = WrappedBookerPropsForPlatform | WrappedBookerPropsForWeb;
@@ -2,7 +2,7 @@ import { useEffect, useMemo } from "react";
import dayjs from "@calcom/dayjs";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import { useBookerTime } from "@calcom/features/bookings/Booker/hooks/useBookerTime";
import type { BookerEvent } from "@calcom/features/bookings/types";
import { Calendar } from "@calcom/web/modules/calendars/weeklyview/components/Calendar";
import type { CalendarAvailableTimeslots } from "@calcom/features/calendars/weeklyview/types/state";
+1 -1
View File
@@ -53,7 +53,7 @@ import { Icon } from "@calcom/ui/components/icon";
import { HorizontalTabs } from "@calcom/ui/components/navigation";
import { showToast } from "@calcom/ui/components/toast";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import { useBookerTime } from "@calcom/features/bookings/Booker/hooks/useBookerTime";
import { EmbedTabName, type tabs } from "./EmbedTabs";
import { buildCssVarsPerTheme } from "@calcom/features/embed/lib/buildCssVarsPerTheme";
import { EmbedTheme } from "@calcom/features/embed/lib/constants";
@@ -1,5 +1,5 @@
import dayjs from "@calcom/dayjs";
import { useAvailableTimeSlots } from "@calcom/features/bookings/Booker/components/hooks/useAvailableTimeSlots";
import { useAvailableTimeSlots } from "@calcom/features/bookings/Booker/hooks/useAvailableTimeSlots";
import { useTimePreferences } from "@calcom/features/bookings/lib/timePreferences";
import { useSchedule } from "@calcom/features/schedules/lib/use-schedule/useSchedule";
import { useTroubleshooterStore } from "@calcom/features/troubleshooter/store";
@@ -33,7 +33,7 @@ import type {
setNonStylesConfig,
} from "./types";
import { useCompatSearchParams } from "./useCompatSearchParams";
export { useBookerEmbedEvents } from "./embed-iframe/react-hooks";
export { useBookerEmbedEvents, useSlotsViewOnSmallScreen } from "./embed-iframe/react-hooks";
// We don't import it from Booker/types because the types from this module are published to npm and we can't import packages that aren't published
type BookerState = "loading" | "selecting_date" | "selecting_time" | "booking";
@@ -3,7 +3,9 @@
* All hooks defined in this file must be client side hooks and must not be executed in server side.
* So, they should start with isBrowser check.
*/
import useMediaQuery from "@calcom/lib/hooks/useMediaQuery";
import { sdkActionManager } from "../sdk-event";
import { useEmbedUiConfig, useIsEmbed } from "../embed-iframe";
import { embedStore, getEventHasFired, setEventHasFired, getReloadInitiated, setReloadInitiated } from "./lib/embedStore";
import { isBrowser, isPrerendering } from "./lib/utils";
@@ -114,3 +116,13 @@ export const useBookerEmbedEvents = ({
}
};
export const useSlotsViewOnSmallScreen = () => {
const isEmbed = useIsEmbed();
const isMobile = useMediaQuery("(max-width: 768px)");
const embedUiConfig = useEmbedUiConfig();
if (!isBrowser || !isEmbed || !isMobile) return false;
return embedUiConfig.useSlotsViewOnSmallScreen ?? false;
};
@@ -1,13 +0,0 @@
import { useIsEmbed, useEmbedUiConfig } from "@calcom/embed-core/embed-iframe";
import useMediaQuery from "@calcom/lib/hooks/useMediaQuery";
export const useSlotsViewOnSmallScreen = () => {
const isEmbed = useIsEmbed();
const isMobile = useMediaQuery("(max-width: 768px)");
const embedUiConfig = useEmbedUiConfig();
if (!isEmbed || !isMobile) return false;
return embedUiConfig.useSlotsViewOnSmallScreen ?? false;
};
@@ -27,4 +27,4 @@ export const useAvailableTimeSlots = ({ schedule, eventDuration }: UseAvailableT
return availableTimeslots;
}, [schedule, eventDuration]);
};
};
@@ -5,45 +5,32 @@ import {
useEmbedType,
useEmbedUiConfig,
useIsEmbed,
useSlotsViewOnSmallScreen,
} from "@calcom/embed-core/embed-iframe";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { extraDaysConfig } from "@calcom/features/bookings/Booker/config";
import type { BookerLayout } from "@calcom/features/bookings/Booker/types";
import { validateLayout } from "@calcom/features/bookings/Booker/utils/layout";
import { getQueryParam } from "@calcom/features/bookings/Booker/utils/query-param";
import type { BookerEvent } from "@calcom/features/bookings/types";
import useMediaQuery from "@calcom/lib/hooks/useMediaQuery";
import type { BookerLayouts } from "@calcom/prisma/zod-utils";
import { defaultBookerLayoutSettings } from "@calcom/prisma/zod-utils";
import { useSlotsViewOnSmallScreen } from "@calcom/features/bookings/Booker/components/hooks/useSlotsViewOnSmallScreen";
import { extraDaysConfig } from "../../config";
import type { BookerLayout } from "../../types";
import { validateLayout } from "../../utils/layout";
import { getQueryParam } from "../../utils/query-param";
export type UseBookerLayoutType = ReturnType<typeof useBookerLayout>;
export const useBookerLayout = (
profileBookerLayouts:
| BookerEvent["profile"]["bookerLayouts"]
| undefined
| null
profileBookerLayouts: BookerEvent["profile"]["bookerLayouts"] | undefined | null
) => {
const [_layout, setLayout] = useBookerStoreContext(
(state) => [state.layout, state.setLayout],
shallow
);
const [_layout, setLayout] = useBookerStoreContext((state) => [state.layout, state.setLayout], shallow);
const isEmbed = useIsEmbed();
const isMobile = useMediaQuery("(max-width: 768px)");
const isTablet = useMediaQuery("(max-width: 1024px)");
const embedUiConfig = useEmbedUiConfig();
// In Embed we give preference to embed configuration for the layout.If that's not set, we use the App configuration for the event layout
// But if it's mobile view, there is only one layout supported which is 'mobile'
const layout = isEmbed
? isMobile
? "mobile"
: validateLayout(embedUiConfig.layout) || _layout
: _layout;
const extraDays = isTablet
? extraDaysConfig[layout].tablet
: extraDaysConfig[layout].desktop;
const layout = isEmbed ? (isMobile ? "mobile" : validateLayout(embedUiConfig.layout) || _layout) : _layout;
const extraDays = isTablet ? extraDaysConfig[layout].tablet : extraDaysConfig[layout].desktop;
const embedType = useEmbedType();
// Floating Button and Element Click both are modal and thus have dark background
const hasDarkBackground = isEmbed && embedType !== "inline";
@@ -72,9 +59,7 @@ export const useBookerLayout = (
bookerLayouts?.enabledLayouts?.length &&
layout !== _layout
) {
const validLayout = bookerLayouts.enabledLayouts.find(
(userLayout) => userLayout === layout
);
const validLayout = bookerLayouts.enabledLayouts.find((userLayout) => userLayout === layout);
if (validLayout) setLayout(validLayout);
}
}, [bookerLayouts, setLayout, _layout, isEmbed, isMobile]);
@@ -99,8 +84,8 @@ export const useBookerLayout = (
const hideEventTypeDetails = isEmbed
? embedUiConfig.hideEventTypeDetails
: typeof hideEventTypeDetailsParam === "string"
? hideEventTypeDetailsParam === "true"
: false;
? hideEventTypeDetailsParam === "true"
: false;
const slotsViewOnSmallScreen = useSlotsViewOnSmallScreen();
@@ -118,4 +103,4 @@ export const useBookerLayout = (
slotsViewOnSmallScreen,
bookerLayouts,
};
};
};
@@ -1,9 +1,7 @@
import { shallow } from "zustand/shallow";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useTimePreferences } from "../../../lib/timePreferences";
import { getBookerTimezone } from "../../utils/getBookerTimezone";
import { getBookerTimezone } from "@calcom/features/bookings/Booker/utils/getBookerTimezone";
import { useTimePreferences } from "@calcom/features/bookings/lib/timePreferences";
import { shallow } from "zustand/shallow";
export const useBookerTime = () => {
const [timezoneFromBookerStore] = useBookerStoreContext((state) => [state.timezone], shallow);
@@ -19,4 +17,4 @@ export const useBookerTime = () => {
timezoneFromBookerStore,
timezoneFromTimePreferences,
};
};
};
@@ -127,4 +127,4 @@ export const useBookingForm = ({
formErrors: errors,
errors,
};
};
};
@@ -291,4 +291,4 @@ describe("useInitialFormValues - Autofill Disable Feature", () => {
expect(result.current.values.responses?.phone).toBeUndefined();
});
});
});
});
@@ -85,7 +85,7 @@ export function useInitialFormValues({
eventType?.owner?.profile?.organization?.organizationSettings?.disableAutofillOnBookingPage ??
false;
useEffect(() => {
(async function () {
(async () => {
if (Object.keys(formValues).length) {
setInitialValuesState({
values: formValues,
@@ -118,11 +118,11 @@ export function useInitialFormValues({
const defaultUserValues = (() => {
const rescheduledEmail =
rescheduleUid && bookingData && bookingData.attendees.length > 0
? bookingData.attendees[0].email ?? ""
? (bookingData.attendees[0].email ?? "")
: "";
const rescheduledName =
rescheduleUid && bookingData && bookingData.attendees.length > 0
? bookingData.attendees[0].name ?? ""
? (bookingData.attendees[0].name ?? "")
: "";
if (isAutofillDisabledByOrg) {
@@ -133,8 +133,8 @@ export function useInitialFormValues({
}
return {
email: rescheduledEmail || (parsedQuery["email"] ? parsedQuery["email"] : email ?? ""),
name: rescheduledName || (parsedQuery["name"] ? parsedQuery["name"] : name ?? username ?? ""),
email: rescheduledEmail || (parsedQuery["email"] ? parsedQuery["email"] : (email ?? "")),
name: rescheduledName || (parsedQuery["name"] ? parsedQuery["name"] : (name ?? username ?? "")),
};
})();
@@ -212,4 +212,4 @@ export function useInitialFormValues({
]);
return initialValuesState;
}
}
@@ -68,4 +68,4 @@ export function useLocalSet<T extends HasExternalId>(key: string, initialValue:
};
return { set, addValue, removeById, toggleValue, hasItem, clearSet };
}
}
+8 -79
View File
@@ -1,13 +1,8 @@
import type { UseBookerLayoutType } from "@calcom/features/bookings/Booker/components/hooks/useBookerLayout";
import type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/components/hooks/useBookingForm";
import type { UseBookingsReturnType } from "@calcom/features/bookings/Booker/components/hooks/useBookings";
import type { UseSlotsReturnType } from "@calcom/features/bookings/Booker/components/hooks/useSlots";
import type { UseVerifyCodeReturnType } from "@calcom/features/bookings/Booker/components/hooks/useVerifyCode";
import type { UseVerifyEmailReturnType } from "@calcom/features/bookings/Booker/components/hooks/useVerifyEmail";
import type { useScheduleForEventReturnType } from "@calcom/features/bookings/Booker/utils/event";
import type { BookerEventQuery } from "@calcom/features/bookings/types";
import type { IntlSupportedTimeZones } from "@calcom/lib/timeZones";
import type { EventBusyDate } from "@calcom/types/Calendar";
import type { TIsAvailableOutputSchema } from "@calcom/trpc/server/routers/viewer/slots/isAvailable.schema";
import type React from "react";
import type { GetBookingType } from "../lib/get-booking";
@@ -110,79 +105,6 @@ export type ToggledConnectedCalendars = {
externalId: string;
};
export type WrappedBookerPropsMain = {
sessionUsername?: string | null;
rescheduleUid: string | null;
rescheduledBy: string | null;
bookingUid: string | null;
isRedirect: boolean;
fromUserNameRedirected: string;
hasSession: boolean;
onGoBackInstantMeeting: () => void;
onConnectNowInstantMeeting: () => void;
onOverlayClickNoCalendar: () => void;
onClickOverlayContinue: () => void;
onOverlaySwitchStateChange: (state: boolean) => void;
extraOptions: Record<string, string | string[]>;
bookings: UseBookingsReturnType;
slots: UseSlotsReturnType;
calendars: {
overlayBusyDates?: EventBusyDate[];
isOverlayCalendarEnabled: boolean;
connectedCalendars: {
calendars?: {
name?: string;
integrationTitle?: string;
externalId: string;
primary: boolean | null;
}[];
credentialId: number;
delegationCredentialId?: string | null;
cacheUpdatedAt: null;
error?: {
message: string | null;
};
primary?: {
email?: string;
};
integration: {
slug: string;
name?: string | undefined;
title?: string;
logo: string;
};
}[];
loadingConnectedCalendar: boolean;
onToggleCalendar: (calendarsToLoad: Set<ToggledConnectedCalendars>) => void;
};
bookerForm: UseBookingFormReturnType;
event: BookerEventQuery;
schedule: useScheduleForEventReturnType;
bookerLayout: UseBookerLayoutType;
verifyEmail: UseVerifyEmailReturnType;
customClassNames?: CustomClassNames;
isBookingDryRun?: boolean;
renderCaptcha?: boolean;
confirmButtonDisabled?: boolean;
};
export type WrappedBookerPropsForPlatform = WrappedBookerPropsMain & {
isPlatform: true;
verifyCode: undefined;
customClassNames?: CustomClassNames;
timeZones?: Timezone[];
roundRobinHideOrgAndTeam?: boolean;
hideOrgTeamAvatar?: boolean;
};
export type WrappedBookerPropsForWeb = WrappedBookerPropsMain & {
isPlatform: false;
verifyCode: UseVerifyCodeReturnType;
timeZones?: Timezone[];
roundRobinHideOrgAndTeam?: boolean;
hideOrgTeamAvatar?: boolean;
};
export type WrappedBookerProps = WrappedBookerPropsForPlatform | WrappedBookerPropsForWeb;
export type VIEW_TYPE = "MONTH_VIEW" | "WEEK_VIEW" | "COLUMN_VIEW";
export type BookerState = "loading" | "selecting_date" | "selecting_time" | "booking";
@@ -221,3 +143,10 @@ export type DatePickerClassNames = {
datePickerDatesActive?: string;
datePickerToggle?: string;
};
export type QuickAvailabilityCheck = {
status: "available" | "reserved" | "minBookNoticeViolation" | "slotInPast";
utcStartIso: string;
utcEndIso: string;
realStatus?: "available" | "reserved" | "minBookNoticeViolation" | "slotInPast" | undefined;
};
@@ -5,7 +5,7 @@ import { useSchedule } from "@calcom/features/schedules/lib/use-schedule/useSche
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { trpc } from "@calcom/trpc/react";
import { useBookerTime } from "../components/hooks/useBookerTime";
import { useBookerTime } from "@calcom/features/bookings/Booker/hooks/useBookerTime";
export type useEventReturnType = ReturnType<typeof useEvent>;
export type useScheduleForEventReturnType = ReturnType<typeof useScheduleForEvent>;
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import type { QuickAvailabilityCheck } from "../components/hooks/useSlots";
import type { QuickAvailabilityCheck } from "../types";
import { isTimeSlotAvailable } from "./isTimeslotAvailable";
describe("isTimeSlotAvailable", () => {
@@ -1,6 +1,6 @@
import dayjs from "@calcom/dayjs";
import type { QuickAvailabilityCheck } from "../components/hooks/useSlots";
import type { QuickAvailabilityCheck } from "../types";
import { isSlotEquivalent, isValidISOFormat } from "./isSlotEquivalent";
type Maybe<T> = T | undefined;
@@ -17,7 +17,7 @@ import { SkeletonText } from "@calcom/ui/components/skeleton";
import { Tooltip } from "@calcom/ui/components/tooltip";
import NoAvailabilityDialog from "./NoAvailabilityDialog";
import { useSlotsViewOnSmallScreen } from "@calcom/features/bookings/Booker/components/hooks/useSlotsViewOnSmallScreen";
import { useSlotsViewOnSmallScreen } from "@calcom/embed-core/embed-iframe";
export type DatePickerProps = {
/** which day of the week to render the calendar. Usually Sunday (=0) or Monday (=1) - default: Sunday */
@@ -7,9 +7,9 @@ import {
useBookerStoreContext,
useInitializeBookerStoreContext,
} from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useBookerLayout } from "@calcom/features/bookings/Booker/components/hooks/useBookerLayout";
import { useBookingForm } from "@calcom/features/bookings/Booker/components/hooks/useBookingForm";
import { useLocalSet } from "@calcom/features/bookings/Booker/components/hooks/useLocalSet";
import { useBookerLayout } from "@calcom/features/bookings/Booker/hooks/useBookerLayout";
import { useBookingForm } from "@calcom/features/bookings/Booker/hooks/useBookingForm";
import { useLocalSet } from "@calcom/features/bookings/Booker/hooks/useLocalSet";
import { useInitializeBookerStore } from "@calcom/features/bookings/Booker/store";
import { useTimePreferences } from "@calcom/features/bookings/lib";
import type { ConnectedDestinationCalendars } from "@calcom/features/calendars/lib/getConnectedDestinationCalendars";
@@ -7,8 +7,8 @@ import {
} from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { Header } from "@calcom/web/modules/bookings/components/Header";
import { BookerSection } from "@calcom/web/modules/bookings/components/Section";
import { useAvailableTimeSlots } from "@calcom/features/bookings/Booker/components/hooks/useAvailableTimeSlots";
import { useBookerLayout } from "@calcom/features/bookings/Booker/components/hooks/useBookerLayout";
import { useAvailableTimeSlots } from "@calcom/features/bookings/Booker/hooks/useAvailableTimeSlots";
import { useBookerLayout } from "@calcom/features/bookings/Booker/hooks/useBookerLayout";
import { useTimePreferences } from "@calcom/features/bookings/lib";
import { LargeCalendar } from "@calcom/web/modules/calendar-view/components/LargeCalendar";
import { getUsernameList } from "@calcom/features/eventtypes/lib/defaultEvents";
@@ -2,8 +2,8 @@ import { useSearchParams } from "next/navigation";
import { useIsPlatform } from "@calcom/atoms/hooks/useIsPlatform";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/components/hooks/useBookingForm";
import { useBookerTime } from "@calcom/features/bookings/Booker/hooks/useBookerTime";
import type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/hooks/useBookingForm";
import { mapBookingToMutationInput, mapRecurringBookingToMutationInput } from "@calcom/features/bookings/lib";
import type { BookingCreateBody } from "@calcom/features/bookings/lib/bookingCreateBodySchema";
import type { BookerEvent } from "@calcom/features/bookings/types";