import { LazyMotion, domAnimation, m, AnimatePresence } from "framer-motion"; import dynamic from "next/dynamic"; import { useCallback, useEffect, useRef } from "react"; import StickyBox from "react-sticky-box"; import { shallow } from "zustand/shallow"; import classNames from "@calcom/lib/classNames"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import useMediaQuery from "@calcom/lib/hooks/useMediaQuery"; import { ToggleGroup } from "@calcom/ui"; import { Calendar, Columns, Grid } from "@calcom/ui/components/icon"; import { AvailableTimeSlots } from "./components/AvailableTimeSlots"; import { Away } from "./components/Away"; import { BookEventForm } from "./components/BookEventForm"; import { BookFormAsModal } from "./components/BookEventForm/BookFormAsModal"; import { EventMeta } from "./components/EventMeta"; import { LargeCalendar } from "./components/LargeCalendar"; import { LargeViewHeader } from "./components/LargeViewHeader"; import { BookerSection } from "./components/Section"; import { fadeInLeft, getBookerSizeClassNames, useBookerResizeAnimation } from "./config"; import { useBookerStore, useInitializeBookerStore } from "./store"; import type { BookerLayout, BookerProps } from "./types"; import { useEvent } from "./utils/event"; import { useBrandColors } from "./utils/use-brand-colors"; const PoweredBy = dynamic(() => import("@calcom/ee/components/PoweredBy")); const DatePicker = dynamic(() => import("./components/DatePicker").then((mod) => mod.DatePicker), { ssr: false, }); const BookerComponent = ({ username, eventSlug, month, rescheduleBooking, hideBranding = false, }: BookerProps) => { const { t } = useLocale(); const isMobile = useMediaQuery("(max-width: 768px)"); const isTablet = useMediaQuery("(max-width: 1024px)"); const timeslotsRef = useRef(null); const StickyOnDesktop = isMobile ? "div" : StickyBox; const rescheduleUid = typeof window !== "undefined" ? new URLSearchParams(window.location.search).get("rescheduleUid") : null; const event = useEvent(); const [layout, setLayout] = useBookerStore((state) => [state.layout, state.setLayout], shallow); const [bookerState, setBookerState] = useBookerStore((state) => [state.state, state.setState], shallow); const selectedDate = useBookerStore((state) => state.selectedDate); const [selectedTimeslot, setSelectedTimeslot] = useBookerStore( (state) => [state.selectedTimeslot, state.setSelectedTimeslot], shallow ); const extraDays = layout === "large_timeslots" ? (isTablet ? 2 : 4) : 0; const onLayoutToggle = useCallback((newLayout: BookerLayout) => setLayout(newLayout), [setLayout]); const animationScope = useBookerResizeAnimation(layout, bookerState); useBrandColors({ brandColor: event.data?.profile.brandColor, darkBrandColor: event.data?.profile.darkBrandColor, theme: event.data?.profile.theme, }); useInitializeBookerStore({ username, eventSlug, month, eventId: event?.data?.id, rescheduleUid, rescheduleBooking, }); useEffect(() => { if (isMobile && layout !== "mobile") { setLayout("mobile"); } else if (!isMobile && layout === "mobile") { setLayout("small_calendar"); } }, [isMobile, setLayout, layout]); useEffect(() => { if (event.isLoading) return setBookerState("loading"); if (!selectedDate) return setBookerState("selecting_date"); if (!selectedTimeslot) return setBookerState("selecting_time"); return setBookerState("booking"); }, [event, selectedDate, selectedTimeslot, setBookerState]); useEffect(() => { if (layout === "mobile") { timeslotsRef.current?.scrollIntoView({ behavior: "smooth" }); } }, [layout]); return ( <> {/* If we would render this on mobile, it would unset the mobile variant, since that's not a valid option, so it would set the layout to null. */} {!isMobile && (
, tooltip: t("switch_monthly"), }, { value: "large_calendar", label: , tooltip: t("switch_weekly"), }, { value: "large_timeslots", label: , tooltip: t("switch_multiday"), }, ]} />
)}
{layout !== "small_calendar" && !(layout === "mobile" && bookerState === "booking") && (
)}
setSelectedTimeslot(null)} /> {layout === "large_timeslots" && }
{!hideBranding ? : null}
setSelectedTimeslot(null)} /> ); }; export const Booker = (props: BookerProps) => { if (props.isAway) return ; return ( ); };