feat: new booker embed (#9023)

* Added embed files for new booker routes.

* Updated nextjs config to redirect to correct embed url.

* Notify embed of booker size after animation completes.

* Don't use animations and 100vh in embed.

* Added booker layout toggles to embed settings (wip)

* Booker header positioning tweaks

* Show correct layout in embed

* Don't show pricing event meta if stripe app is not installed

* Support custom border booker for embed

* wip on passing layout prop to iframe for previews

* Only show booker layout settings in embed-create-popup if booker layout feature is enabled.

* Made layout prop in preview state for embed optional

* Made layout prop in preview state for embed optional

* Get layout toggle working

* Fixed types

* Added themebasis prop

* Fix potential circular import that should then fix type errors

* Fixed type error by fixing casing.

* Type fixes

* Added translations

* More translations

* Remove comment

* Removed uneccessary addition to package.json

* Removed todo since it's solved in another way

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
This commit is contained in:
Jeroen ReumkensandHariom Balhara authored and GitHub committed 2023-06-14 09:22:44 +00:00
1 parent f0992b4914
commit 8331059b7c
21 files changed
+176 -36

No files matched your search

+44 -2
View File
@@ -8,8 +8,11 @@ import { createRef, forwardRef, useRef, useState } from "react";
import type { ControlProps } from "react-select";
import { components } from "react-select";
import type { BookerLayout } from "@calcom/features/bookings/Booker/types";
import { useFlagMap } from "@calcom/features/flags/context/provider";
import { APP_NAME, EMBED_LIB_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { BookerLayouts } from "@calcom/prisma/zod-utils";
import {
Button,
Dialog,
@@ -41,12 +44,13 @@ type PreviewState = {
height: string;
};
theme: Theme;
floatingPopup: Record<string, string>;
floatingPopup: Record<string, string | boolean | undefined>;
elementClick: Record<string, string>;
palette: {
brandColor: string;
};
hideEventTypeDetails: boolean;
layout: BookerLayouts;
};
const queryParamsForDialog = ["embedType", "embedTabName", "embedUrl"];
@@ -129,11 +133,13 @@ const getEmbedUIInstructionString = ({
theme,
brandColor,
hideEventTypeDetails,
layout,
}: {
apiName: string;
theme?: string;
brandColor: string;
hideEventTypeDetails: boolean;
layout?: string;
}) => {
theme = theme !== "auto" ? theme : undefined;
return getInstructionString({
@@ -147,6 +153,7 @@ const getEmbedUIInstructionString = ({
},
},
hideEventTypeDetails: hideEventTypeDetails,
layout,
},
});
};
@@ -260,6 +267,7 @@ const getEmbedTypeSpecificString = ({
theme: PreviewState["theme"];
brandColor: string;
hideEventTypeDetails: boolean;
layout?: BookerLayout;
};
if (embedFramework === "react") {
uiInstructionStringArg = {
@@ -267,6 +275,7 @@ const getEmbedTypeSpecificString = ({
theme: previewState.theme,
brandColor: previewState.palette.brandColor,
hideEventTypeDetails: previewState.hideEventTypeDetails,
layout: previewState.layout,
};
} else {
uiInstructionStringArg = {
@@ -274,6 +283,7 @@ const getEmbedTypeSpecificString = ({
theme: previewState.theme,
brandColor: previewState.palette.brandColor,
hideEventTypeDetails: previewState.hideEventTypeDetails,
layout: previewState.layout,
};
}
if (!frameworkCodes[embedType]) {
@@ -626,6 +636,7 @@ const ThemeSelectControl = ({ children, ...props }: ControlProps<{ value: Theme;
const ChooseEmbedTypesDialogContent = () => {
const { t } = useLocale();
const router = useRouter();
return (
<DialogContent className="rounded-lg p-10" type="creation" size="lg">
<div className="mb-2">
@@ -671,6 +682,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
const router = useRouter();
const iframeRef = useRef<HTMLIFrameElement>(null);
const dialogContentRef = useRef<HTMLDivElement>(null);
const flags = useFlagMap();
const isBookerLayoutsEnabled = flags["booker-layouts"] === true;
const s = (href: string) => {
const searchParams = new URLSearchParams(router.asPath.split("?")[1] || "");
@@ -691,7 +704,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
const [isEmbedCustomizationOpen, setIsEmbedCustomizationOpen] = useState(true);
const [isBookingCustomizationOpen, setIsBookingCustomizationOpen] = useState(true);
const [previewState, setPreviewState] = useState({
const [previewState, setPreviewState] = useState<PreviewState>({
inline: {
width: "100%",
height: "100%",
@@ -703,6 +716,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
palette: {
brandColor: "#000000",
},
layout: BookerLayouts.MONTH_VIEW,
});
const close = () => {
@@ -765,6 +779,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
arg: {
theme: previewState.theme,
hideEventTypeDetails: previewState.hideEventTypeDetails,
layout: previewState.layout,
styles: {
branding: {
...previewState.palette,
@@ -798,6 +813,12 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
{ value: Theme.light, label: "Light Theme" },
];
const layoutOptions = [
{ value: BookerLayouts.MONTH_VIEW, label: t("bookerlayout_month_view") },
{ value: BookerLayouts.WEEK_VIEW, label: t("bookerlayout_week_view") },
{ value: BookerLayouts.COLUMN_VIEW, label: t("bookerlayout_column_view") },
];
const FloatingPopupPositionOptions = [
{
value: "bottom-right",
@@ -1070,6 +1091,27 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</div>
</Label>
))}
{isBookerLayoutsEnabled && (
<Label className="mb-6">
<div className="mb-2">{t("layout")}</div>
<Select
className="w-full"
defaultValue={layoutOptions[0]}
onChange={(option) => {
if (!option) {
return;
}
setPreviewState((previewState) => {
return {
...previewState,
layout: option.value,
};
});
}}
options={layoutOptions}
/>
</Label>
)}
</div>
</CollapsibleContent>
</Collapsible>
+3
View File
@@ -8,6 +8,7 @@ export default function withEmbedSsr(getServerSideProps: GetServerSideProps) {
return async (context: GetServerSidePropsContext): Promise<GetServerSidePropsResult<EmbedProps>> => {
const ssrResponse = await getServerSideProps(context);
const embed = context.query.embed;
const layout = context.query.layout;
if ("redirect" in ssrResponse) {
// Use a dummy URL https://base as the fallback base URL so that URL parsing works for relative URLs as well.
@@ -18,6 +19,8 @@ export default function withEmbedSsr(getServerSideProps: GetServerSideProps) {
destinationUrlObj.pathname +
"/embed?" +
destinationUrlObj.searchParams.toString() +
"&layout=" +
layout +
"&embed=" +
embed;
+7
View File
@@ -224,12 +224,19 @@ const nextConfig = {
destination: "/new-booker/:user/:type",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: `/:user((?!${pages.join("|")}).*)/:type/embed`,
destination: "/new-booker/:user/:type/embed",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type",
destination: "/new-booker/team/:slug/:type",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type/embed",
destination: "/new-booker/team/:slug/:type/embed",
source: "/d/:link/:slug",
destination: "/new-booker/d/:link/:slug",
has: [{ type: "cookie", key: "new-booker-enabled" }],
+5 -1
View File
@@ -5,6 +5,7 @@ import { Booker } from "@calcom/atoms";
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
import { getBookingByUidOrRescheduleUid } from "@calcom/features/bookings/lib/get-booking";
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
import { classNames } from "@calcom/lib";
import { getUsernameList } from "@calcom/lib/defaultEvents";
import prisma from "@calcom/prisma";
@@ -15,8 +16,9 @@ import PageWrapper from "@components/PageWrapper";
type PageProps = inferSSRProps<typeof getServerSideProps>;
export default function Type({ slug, user, booking, away, isBrandingHidden }: PageProps) {
const isEmbed = typeof window !== "undefined" && window?.isEmbed?.();
return (
<main className="flex h-full min-h-[100dvh] items-center justify-center">
<main className={classNames("flex h-full items-center justify-center", !isEmbed && "min-h-[100dvh]")}>
<BookerSeo
username={user}
eventSlug={slug}
@@ -84,6 +86,7 @@ async function getDynamicGroupPageProps(context: GetServerSidePropsContext) {
away: false,
trpcState: ssr.dehydrate(),
isBrandingHidden: false,
themeBasis: null,
},
};
}
@@ -133,6 +136,7 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
slug,
trpcState: ssr.dehydrate(),
isBrandingHidden: user?.hideBranding,
themeBasis: username,
},
};
}
@@ -0,0 +1,11 @@
import withEmbedSsr from "@lib/withEmbedSsr";
import { getServerSideProps as _getServerSideProps } from "../[type]";
export { default } from "../[type]";
// Somehow these types don't accept the {notFound: true} return type.
// Probably still need to fix this. I don't know why this isn't allowed yet.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
export const getServerSideProps = withEmbedSsr(_getServerSideProps);
@@ -0,0 +1,7 @@
import withEmbedSsr from "@lib/withEmbedSsr";
import { getServerSideProps as _getServerSideProps } from "../../[user]";
export { default } from "../../[user]";
export const getServerSideProps = withEmbedSsr(_getServerSideProps);
@@ -5,6 +5,7 @@ import { Booker } from "@calcom/atoms";
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
import { getBookingByUidOrRescheduleUid } from "@calcom/features/bookings/lib/get-booking";
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
import { classNames } from "@calcom/lib";
import prisma from "@calcom/prisma";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
@@ -14,8 +15,9 @@ import PageWrapper from "@components/PageWrapper";
type PageProps = inferSSRProps<typeof getServerSideProps>;
export default function Type({ slug, user, booking, away, isBrandingHidden }: PageProps) {
const isEmbed = typeof window !== "undefined" && window?.isEmbed?.();
return (
<main className="flex h-full min-h-[100dvh] items-center justify-center">
<main className={classNames("flex h-full items-center justify-center", !isEmbed && "min-h-[100dvh]")}>
<BookerSeo
username={user}
eventSlug={slug}
@@ -85,6 +87,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
slug: meetingSlug,
trpcState: ssr.dehydrate(),
isBrandingHidden: team?.hideBranding,
themeBasis: null,
},
};
};
@@ -0,0 +1,9 @@
import withEmbedSsr from "@lib/withEmbedSsr";
import { getServerSideProps as _getServerSideProps } from "../[type]";
export { default } from "../[type]";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
export const getServerSideProps = withEmbedSsr(_getServerSideProps);
@@ -0,0 +1,7 @@
import withEmbedSsr from "@lib/withEmbedSsr";
import { getServerSideProps as _getServerSideProps } from "../../../team/[slug]";
export { default } from "../../../team/[slug]";
export const getServerSideProps = withEmbedSsr(_getServerSideProps);
@@ -300,6 +300,7 @@
"failed": "Failed",
"password_has_been_reset_login": "Your password has been reset. You can now login with your newly created password.",
"bookerlayout_title": "Layout",
"layout": "Layout",
"bookerlayout_default_title": "Default view",
"bookerlayout_description": "You can select multiple and your bookers can switch views.",
"bookerlayout_user_settings_title": "Booking layout",
+6
View File
@@ -150,6 +150,12 @@
data-cal-link="forms/948ae412-d995-4865-875a-48302588de03">
Book through Routing Form [Dark Theme]
</button>
<button data-cal-namespace="popupPaidEvent" data-cal-config='{"layout":"week_view"}' data-cal-link="pro/paid">
Book Paid Event - weekly view
</button>
<button data-cal-namespace="popupPaidEvent" data-cal-config='{"layout":"column_view"}' data-cal-link="pro/paid">
Book Paid Event - column view
</button>
<!-- <div>
<h2>Embed for Pages behind authentication</h2>
<button data-cal-namespace="upcomingBookings" data-cal-config='{"theme":"dark"}' data-cal-link="bookings/upcoming">Show Upcoming Bookings</button>
@@ -3,6 +3,8 @@ import type { CSSProperties } from "react";
import { useState, useEffect } from "react";
import embedInit from "@calcom/embed-core/embed-iframe-init";
import type { BookerStore } from "@calcom/features/bookings/Booker/store";
import type { BookerLayouts } from "@calcom/prisma/zod-utils";
import type { Message } from "./embed";
import { sdkActionManager } from "./sdk-event";
@@ -16,6 +18,7 @@ export type UiConfig = {
styles?: EmbedStyles & EmbedNonStylesConfig;
//TODO: Extract from tailwind the list of all custom variables and support them in auto-completion as well as runtime validation. Followup with listing all variables in Embed Snippet Generator UI.
cssVarsPerTheme?: Record<Theme, Record<string, string>>;
layout?: BookerLayouts;
};
type SetStyles = React.Dispatch<React.SetStateAction<EmbedStyles>>;
@@ -44,6 +47,7 @@ declare global {
__logQueue?: unknown[];
embedStore: typeof embedStore;
applyCssVars: (cssVarsPerTheme: UiConfig["cssVarsPerTheme"]) => void;
setLayout?: BookerStore["setLayout"];
};
CalComPageStatus: string;
isEmbed?: () => boolean;
@@ -315,6 +319,10 @@ const methods = {
embedStore.setUiConfig(uiConfig);
}
if (uiConfig.layout) {
window.CalEmbed.setLayout?.(uiConfig.layout);
}
setEmbedStyles(stylesConfig || {});
setEmbedNonStyles(stylesConfig || {});
},
+25 -8
View File
@@ -1,6 +1,6 @@
import { LazyMotion, domAnimation, m, AnimatePresence } from "framer-motion";
import dynamic from "next/dynamic";
import { useEffect, useRef } from "react";
import { useEffect, useRef, useMemo } from "react";
import StickyBox from "react-sticky-box";
import { shallow } from "zustand/shallow";
@@ -20,6 +20,8 @@ import { extraDaysConfig, fadeInLeft, getBookerSizeClassNames, useBookerResizeAn
import { useBookerStore, useInitializeBookerStore } from "./store";
import type { BookerProps } from "./types";
import { useEvent } from "./utils/event";
import { validateLayout } from "./utils/layout";
import { getQueryParam } from "./utils/query-param";
import { useBrandColors } from "./utils/use-brand-colors";
const PoweredBy = dynamic(() => import("@calcom/ee/components/PoweredBy"));
@@ -42,6 +44,9 @@ const BookerComponent = ({
typeof window !== "undefined" ? new URLSearchParams(window.location.search).get("rescheduleUid") : null;
const event = useEvent();
const [layout, setLayout] = useBookerStore((state) => [state.layout, state.setLayout], shallow);
if (typeof window !== "undefined") {
window.CalEmbed.setLayout = setLayout;
}
const [bookerState, setBookerState] = useBookerStore((state) => [state.state, state.setState], shallow);
const selectedDate = useBookerStore((state) => state.selectedDate);
const [selectedTimeslot, setSelectedTimeslot] = useBookerStore(
@@ -52,6 +57,14 @@ const BookerComponent = ({
const extraDays = isTablet ? extraDaysConfig[layout].tablet : extraDaysConfig[layout].desktop;
const bookerLayouts = event.data?.profile?.bookerLayouts || defaultBookerLayoutSettings;
const animationScope = useBookerResizeAnimation(layout, bookerState);
const isEmbed = typeof window !== "undefined" && window?.isEmbed?.();
const isMonthView = layout === BookerLayouts.MONTH_VIEW;
// We only want the initial url value, that's why we memo it. The embed seems to change the url, which sometimes drops
// the layout query param.
const layoutFromQueryParam = useMemo(() => validateLayout(getQueryParam("layout") as BookerLayouts), []);
const defaultLayout = isEmbed
? layoutFromQueryParam || BookerLayouts.MONTH_VIEW
: bookerLayouts.defaultLayout;
useBrandColors({
brandColor: event.data?.profile.brandColor,
@@ -66,16 +79,16 @@ const BookerComponent = ({
eventId: event?.data?.id,
rescheduleUid,
rescheduleBooking,
layout: bookerLayouts.defaultLayout,
layout: defaultLayout,
});
useEffect(() => {
if (isMobile && layout !== "mobile") {
setLayout("mobile");
} else if (!isMobile && layout === "mobile") {
setLayout(BookerLayouts.MONTH_VIEW);
setLayout(defaultLayout);
}
}, [isMobile, setLayout, layout]);
}, [isMobile, setLayout, layout, defaultLayout]);
useEffect(() => {
if (event.isLoading) return setBookerState("loading");
@@ -96,17 +109,21 @@ const BookerComponent = ({
return (
<>
<div className="flex h-full w-full flex-col items-center">
<div className="text-default flex h-full w-full flex-col items-center overflow-x-clip">
<div
ref={animationScope}
className={classNames(
// Sets booker size css variables for the size of all the columns.
...getBookerSizeClassNames(layout, bookerState),
"bg-default dark:bg-muted grid max-w-full items-start overflow-clip dark:[color-scheme:dark] sm:transition-[width] sm:duration-300 sm:motion-reduce:transition-none md:flex-row",
layout === BookerLayouts.MONTH_VIEW && "border-subtle rounded-md border"
"bg-default dark:bg-muted grid max-w-full auto-rows-fr items-start dark:[color-scheme:dark] sm:duration-300 sm:motion-reduce:transition-none md:flex-row",
layout === BookerLayouts.MONTH_VIEW && "rounded-md border",
!isEmbed && "sm:transition-[width] sm:duration-300",
isEmbed && layout === BookerLayouts.MONTH_VIEW && "border-booker sm:border-booker-width",
!isEmbed && layout === BookerLayouts.MONTH_VIEW && "border-subtle",
layout === BookerLayouts.MONTH_VIEW && isEmbed && "mt-20"
)}>
<AnimatePresence>
<BookerSection area="header">
<BookerSection area="header" className={classNames(isMonthView && "fixed top-3 right-3 z-10")}>
<Header
enabledLayouts={bookerLayouts.enabledLayouts}
extraDays={extraDays}
@@ -26,6 +26,7 @@ export function Header({
const addToSelectedDate = useBookerStore((state) => state.addToSelectedDate);
const isMonthView = layout === BookerLayouts.MONTH_VIEW;
const selectedDate = dayjs(selectedDateString);
const isEmbed = typeof window !== "undefined" && window?.isEmbed?.();
const onLayoutToggle = useCallback(
(newLayout: string) => {
@@ -35,7 +36,7 @@ export function Header({
[setLayout, layout]
);
if (isMobile || !enabledLayouts) return null;
if (isMobile || isEmbed || !enabledLayouts) return null;
// Only reason we create this component, is because it is used 3 times in this component,
// and this way we can't forget to update one of the props in all places :)
@@ -46,11 +47,7 @@ export function Header({
// In month view we only show the layout toggle.
if (isMonthView) {
if (enabledLayouts.length <= 1) return null;
return (
<div className="fixed top-3 right-3 z-10">
<LayoutToggleWithData />
</div>
);
return <LayoutToggleWithData />;
}
return (
+13 -6
View File
@@ -142,7 +142,8 @@ export const getBookerSizeClassNames = (layout: BookerLayout, bookerState: Booke
export const useBookerResizeAnimation = (layout: BookerLayout, state: BookerState) => {
const prefersReducedMotion = useReducedMotion();
const [animationScope, animate] = useAnimate();
const isEmbed = typeof window !== "undefined" && window?.isEmbed?.();
``;
useEffect(() => {
const animationConfig = resizeAnimationConfig[layout][state] || resizeAnimationConfig[layout].default;
@@ -163,12 +164,18 @@ export const useBookerResizeAnimation = (layout: BookerLayout, state: BookerStat
minHeight: animationConfig?.minHeight,
};
// We don't animate if users has set prefers-reduced-motion,
// or when the layout is mobile.
if (prefersReducedMotion || layout === "mobile") {
// In this cases we don't animate the booker at all.
if (prefersReducedMotion || layout === "mobile" || isEmbed) {
const styles = { ...nonAnimatedProperties, ...animatedProperties };
Object.keys(styles).forEach((property) => {
animationScope.current.style[property] = styles[property as keyof typeof styles];
if (property === "height") {
// Change 100vh to 100% in embed, since 100vh in iframe will behave weird, because
// the iframe will constantly grow. 100% will simply make sure it grows with the iframe.
animationScope.current.style.height =
animatedProperties.height === "100vh" && isEmbed ? "100%" : animatedProperties.height;
} else {
animationScope.current.style[property] = styles[property as keyof typeof styles];
}
});
} else {
Object.keys(nonAnimatedProperties).forEach((property) => {
@@ -180,7 +187,7 @@ export const useBookerResizeAnimation = (layout: BookerLayout, state: BookerStat
ease: cubicBezier(0.4, 0, 0.2, 1),
});
}
}, [animate, animationScope, layout, prefersReducedMotion, state]);
}, [animate, isEmbed, animationScope, layout, prefersReducedMotion, state]);
return animationScope;
};
+4 -7
View File
@@ -2,10 +2,11 @@ import { useEffect } from "react";
import { create } from "zustand";
import dayjs from "@calcom/dayjs";
import { BookerLayouts, bookerLayoutOptions } from "@calcom/prisma/zod-utils";
import { BookerLayouts } from "@calcom/prisma/zod-utils";
import type { GetBookingType } from "../lib/get-booking";
import type { BookerState, BookerLayout } from "./types";
import { validateLayout } from "./utils/layout";
import { updateQueryParam, getQueryParam, removeQueryParam } from "./utils/query-param";
/**
@@ -23,7 +24,7 @@ type StoreInitializeType = {
layout: BookerLayout;
};
type BookerStore = {
export type BookerStore = {
/**
* Event details. These are stored in store for easier
* access in child components.
@@ -90,10 +91,6 @@ type BookerStore = {
setFormValues: (values: Record<string, any>) => void;
};
const checkLayout = (layout: BookerLayout) => {
return bookerLayoutOptions.find((validLayout) => validLayout === layout);
};
/**
* The booker store contains the data of the component's
* current state. This data can be reused within child components
@@ -104,7 +101,7 @@ const checkLayout = (layout: BookerLayout) => {
export const useBookerStore = create<BookerStore>((set, get) => ({
state: "loading",
setState: (state: BookerState) => set({ state }),
layout: checkLayout(getQueryParam("layout") as BookerLayout) || BookerLayouts.MONTH_VIEW,
layout: validateLayout(getQueryParam("layout") as BookerLayouts) || BookerLayouts.MONTH_VIEW,
setLayout: (layout: BookerLayout) => {
// If we switch to a large layout and don't have a date selected yet,
// we selected it here, so week title is rendered properly.
@@ -0,0 +1,6 @@
import type { BookerLayouts } from "@calcom/prisma/zod-utils";
import { bookerLayoutOptions } from "@calcom/prisma/zod-utils";
export const validateLayout = (layout?: BookerLayouts | null) => {
return bookerLayoutOptions.find((validLayout) => validLayout === layout);
};
@@ -40,7 +40,7 @@ export const AvailableTimes = ({
const isToday = dayjs().isSame(date, "day");
return (
<div className={classNames("text-default", className)}>
<div className={classNames("text-default flex flex-col", className)}>
<header className="bg-default before:bg-default dark:bg-muted dark:before:bg-muted mb-3 flex w-full flex-row items-center font-medium">
<span
className={classNames(
@@ -11,7 +11,13 @@ import { useFilterQuery } from "../lib/useFilterQuery";
export const TeamsMemberFilter = () => {
const { t } = useLocale();
const session = useSession();
const { data: query, pushItemToKey, removeItemByKeyAndValue, removeByKey, removeAllQueryParams } = useFilterQuery();
const {
data: query,
pushItemToKey,
removeItemByKeyAndValue,
removeByKey,
removeAllQueryParams,
} = useFilterQuery();
const { data } = trpc.viewer.teams.list.useQuery();
const [dropdownTitle, setDropdownTitle] = useState<string>(t("all_bookings_filter_label"));
@@ -2,6 +2,7 @@ import { Fragment } from "react";
import React from "react";
import classNames from "@calcom/lib/classNames";
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Clock, CheckSquare, RefreshCcw, CreditCard } from "@calcom/ui/components/icon";
@@ -145,7 +146,8 @@ export const EventDetails = ({ event, blocks = defaultEventDetailsBlocks }: Even
);
case EventDetailBlocks.PRICE:
if (event.price === 0) return null;
const paymentAppData = getPaymentAppData(event);
if (event.price <= 0 || paymentAppData.price <= 0) return null;
return (
<EventMetaBlock key={block} icon={CreditCard}>
@@ -64,10 +64,10 @@ function MembersList(props: MembersListProps) {
const MembersView = () => {
const { t, i18n } = useLocale();
const router = useRouter();
const session = useSession();
const utils = trpc.useContext();
const teamId = Number(router.query.id);