feat: atoms enhancements (#27224)

This commit is contained in:
Rajiv Sahal authored and GitHub committed 2026-01-26 13:28:35 +01:00
1 parent 0171b5a08c
commit 9a927206bd
12 files changed
+186 -170

No files matched your search

+6 -1
View File
@@ -36,6 +36,8 @@ interface AppListProps {
data: RouterOutputs["viewer"]["apps"]["integrations"];
handleDisconnect: HandleDisconnect;
listClassName?: string;
appCardClassName?: string;
appCardMenuClassName?: string;
defaultConferencingApp: RouterOutputs["viewer"]["apps"]["getUsersDefaultConferencingApp"];
handleUpdateUserDefaultConferencingApp: (params: UpdateUsersDefaultConferencingAppParams) => void;
handleBulkUpdateDefaultLocation: (params: BulkUpdatParams) => void;
@@ -51,6 +53,8 @@ export const AppList = ({
handleDisconnect,
variant,
listClassName,
appCardClassName,
appCardMenuClassName,
defaultConferencingApp,
handleUpdateUserDefaultConferencingApp,
handleBulkUpdateDefaultLocation,
@@ -85,6 +89,7 @@ export const AppList = ({
slug={item.slug}
invalidCredential={item?.invalidCredentialIds ? item.invalidCredentialIds.length > 0 : false}
credentialOwner={item?.credentialOwner}
className={appCardClassName}
actions={
!item.credentialOwner?.readOnly ? (
<div className="flex justify-end">
@@ -92,7 +97,7 @@ export const AppList = ({
<DropdownMenuTrigger asChild>
<Button StartIcon="ellipsis" variant="icon" color="secondary" />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuContent className={appCardMenuClassName}>
{!appIsDefault && variant === "conferencing" && (
<DropdownMenuItem>
<DropdownItem
@@ -91,6 +91,7 @@ const BookerComponent = ({
timeZones,
eventMetaChildren,
roundRobinHideOrgAndTeam,
hideOrgTeamAvatar,
showNoAvailabilityDialog,
}: BookerProps & WrappedBookerProps) => {
const searchParams = useCompatSearchParams();
@@ -470,6 +471,7 @@ const BookerComponent = ({
locale={userLocale}
timeZones={timeZones}
roundRobinHideOrgAndTeam={roundRobinHideOrgAndTeam}
hideOrgTeamAvatar={hideOrgTeamAvatar}
hideEventTypeDetails={hideEventTypeDetails}>
{eventMetaChildren}
</EventMeta>
@@ -1,14 +1,9 @@
import { m } from "framer-motion";
import dynamic from "next/dynamic";
import { useEffect, useMemo } from "react";
import { shallow } from "zustand/shallow";
import { Timezone as PlatformTimezoneSelect } from "@calcom/atoms/timezone";
import { EventDetails, EventMembers, EventMetaSkeleton, EventTitle } from "./event-meta";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
import { fadeInUp } from "@calcom/features/bookings/Booker/config";
import type { Timezone } from "@calcom/features/bookings/Booker/types";
import { SeatsAvailabilityText } from "@calcom/web/modules/bookings/components/SeatsAvailabilityText";
import { EventMetaBlock } from "@calcom/web/modules/bookings/components/event-meta/Details";
import { FromToTime } from "@calcom/features/bookings/Booker/utils/dates";
import { useTimePreferences } from "@calcom/features/bookings/lib";
import type { BookerEvent } from "@calcom/features/bookings/types";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -16,12 +11,15 @@ import { markdownToSafeHTMLClient } from "@calcom/lib/markdownToSafeHTMLClient";
import { CURRENT_TIMEZONE } from "@calcom/lib/timezoneConstants";
import type { EventTypeTranslation } from "@calcom/prisma/client";
import { EventTypeAutoTranslatedField } from "@calcom/prisma/enums";
import { EventMetaBlock } from "@calcom/web/modules/bookings/components/event-meta/Details";
import { SeatsAvailabilityText } from "@calcom/web/modules/bookings/components/SeatsAvailabilityText";
import { m } from "framer-motion";
import dynamic from "next/dynamic";
import { useEffect, useMemo } from "react";
import { shallow } from "zustand/shallow";
import i18nConfigration from "../../../../../i18n.json";
import { fadeInUp } from "@calcom/features/bookings/Booker/config";
import { FromToTime } from "@calcom/features/bookings/Booker/utils/dates";
import { EventDetails, EventMembers, EventMetaSkeleton, EventTitle } from "./event-meta";
import { ScrollableWithGradients } from "./ScrollableWithGradients";
import { useBookerTime } from "@calcom/features/bookings/Booker/components/hooks/useBookerTime";
const WebTimezoneSelect = dynamic(
() => import("@calcom/web/modules/timezone/components/TimezoneSelect").then((mod) => mod.TimezoneSelect),
@@ -56,6 +54,7 @@ export const EventMeta = ({
children,
selectedTimeslot,
roundRobinHideOrgAndTeam,
hideOrgTeamAvatar,
hideEventTypeDetails = false,
}: {
event?: Pick<
@@ -96,6 +95,7 @@ export const EventMeta = ({
children?: React.ReactNode;
selectedTimeslot: string | null;
roundRobinHideOrgAndTeam?: boolean;
hideOrgTeamAvatar?: boolean;
hideEventTypeDetails?: boolean;
}) => {
const { timeFormat, timezone } = useBookerTime();
@@ -141,8 +141,8 @@ export const EventMeta = ({
const colorClass = isNearlyFull
? "text-rose-600"
: isHalfFull
? "text-yellow-500"
: "text-bookinghighlight";
? "text-yellow-500"
: "text-bookinghighlight";
const userLocale = locale ?? navigator.language;
const translatedDescription = getTranslatedField(
event?.fieldTranslations ?? [],
@@ -171,6 +171,7 @@ export const EventMeta = ({
entity={event.entity}
isPrivateLink={isPrivateLink}
roundRobinHideOrgAndTeam={roundRobinHideOrgAndTeam}
hideOrgTeamAvatar={hideOrgTeamAvatar}
/>
<EventTitle className={`${classNames?.eventMetaTitle} my-2`}>
{translatedTitle ?? event?.title}
@@ -19,6 +19,7 @@ export interface EventMembersProps {
entity: BookerEvent["entity"];
isPrivateLink: boolean;
roundRobinHideOrgAndTeam?: boolean;
hideOrgTeamAvatar?: boolean;
}
export const EventMembers = ({
@@ -28,6 +29,7 @@ export const EventMembers = ({
entity,
isPrivateLink,
roundRobinHideOrgAndTeam,
hideOrgTeamAvatar,
}: EventMembersProps) => {
const username = useBookerStore((state) => state.username);
const isDynamic = !!(username && username.indexOf("+") > -1);
@@ -46,8 +48,12 @@ export const EventMembers = ({
return <div className="h-6" />;
}
if (schedulingType === SchedulingType.ROUND_ROBIN && hideOrgTeamAvatar) {
return <p className="text-subtle pt-6 text-sm font-semibold">{profile.name}</p>;
}
const orgOrTeamAvatarItem =
isDynamic || (!profile.image && !entity.logoUrl) || !entity.teamSlug
hideOrgTeamAvatar || isDynamic || (!profile.image && !entity.logoUrl) || !entity.teamSlug
? []
: [
{
@@ -56,8 +62,8 @@ export const EventMembers = ({
isEmbed || isPlatform || isPrivateLink || entity.hideProfileLink
? null
: entity.teamSlug
? getTeamUrlSync({ orgSlug: entity.orgSlug, teamSlug: entity.teamSlug })
: getBookerBaseUrlSync(entity.orgSlug),
? getTeamUrlSync({ orgSlug: entity.orgSlug, teamSlug: entity.teamSlug })
: getBookerBaseUrlSync(entity.orgSlug),
image: entity.logoUrl ?? profile.image ?? "",
alt: entity.name ?? profile.name ?? "",
title: entity.name ?? profile.name ?? "",
@@ -52,6 +52,7 @@ export type EventTeamAssignmentTabBaseProps = Pick<
customClassNames?: EventTeamAssignmentTabCustomClassNames;
orgId: number | null;
isSegmentApplicable: boolean;
hideFixedHostsForCollective?: boolean;
};
export const mapMemberToChildrenOption = (
@@ -615,6 +616,7 @@ const Hosts = ({
setAssignAllTeamMembers,
customClassNames,
isSegmentApplicable,
hideFixedHostsForCollective = false,
}: {
orgId: number | null;
teamId: number;
@@ -623,6 +625,7 @@ const Hosts = ({
setAssignAllTeamMembers: Dispatch<SetStateAction<boolean>>;
customClassNames?: HostsCustomClassNames;
isSegmentApplicable: boolean;
hideFixedHostsForCollective?: boolean;
}) => {
const {
control,
@@ -662,10 +665,10 @@ const Hosts = ({
return existingHost
? {
...newValue,
scheduleId: existingHost.scheduleId,
groupId: existingHost.groupId,
}
...newValue,
scheduleId: existingHost.scheduleId,
groupId: existingHost.groupId,
}
: newValue;
});
};
@@ -675,7 +678,9 @@ const Hosts = ({
name="hosts"
render={({ field: { onChange, value } }) => {
const schedulingTypeRender = {
COLLECTIVE: (
COLLECTIVE: hideFixedHostsForCollective ? (
<></>
) : (
<FixedHosts
teamId={teamId}
teamMembers={teamMembers}
@@ -733,6 +738,7 @@ export const EventTeamAssignmentTab = ({
customClassNames,
orgId,
isSegmentApplicable,
hideFixedHostsForCollective = false,
}: EventTeamAssignmentTabBaseProps) => {
const { t } = useLocale();
@@ -741,17 +747,17 @@ export const EventTeamAssignmentTab = ({
label: string;
// description: string;
}[] = [
{
value: "COLLECTIVE",
label: t("collective"),
// description: t("collective_description"),
},
{
value: "ROUND_ROBIN",
label: t("round_robin"),
// description: t("round_robin_description"),
},
];
{
value: "COLLECTIVE",
label: t("collective"),
// description: t("collective_description"),
},
{
value: "ROUND_ROBIN",
label: t("round_robin"),
// description: t("round_robin_description"),
},
];
const pendingMembers = (member: (typeof teamMembers)[number]) =>
!!eventType.team?.parentId || !!member.username;
const teamMembersOptions = teamMembers
@@ -883,11 +889,11 @@ export const EventTeamAssignmentTab = ({
</RadioArea.Item>
{(eventType.team?.rrTimestampBasis &&
eventType.team?.rrTimestampBasis !== RRTimestampBasis.CREATED_AT) ||
hostGroups?.length > 1 ? (
hostGroups?.length > 1 ? (
<Tooltip
content={
eventType.team?.rrTimestampBasis &&
eventType.team?.rrTimestampBasis !== RRTimestampBasis.CREATED_AT
eventType.team?.rrTimestampBasis !== RRTimestampBasis.CREATED_AT
? t("rr_load_balancing_disabled")
: t("rr_load_balancing_disabled_with_groups")
}>
@@ -942,6 +948,7 @@ export const EventTeamAssignmentTab = ({
setAssignAllTeamMembers={setAssignAllTeamMembers}
teamMembers={teamMembersOptions}
customClassNames={customClassNames?.hosts}
hideFixedHostsForCollective={hideFixedHostsForCollective}
/>
</>
)}
+3 -3
View File
@@ -1,5 +1,3 @@
import type React from "react";
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";
@@ -10,7 +8,7 @@ import type { UseVerifyEmailReturnType } from "@calcom/features/bookings/Booker/
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 React from "react";
import type { GetBookingType } from "../lib/get-booking";
export type Timezone = (typeof IntlSupportedTimeZones)[number];
@@ -141,12 +139,14 @@ export type WrappedBookerPropsForPlatform = WrappedBookerPropsMain & {
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;
@@ -1,23 +1,11 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { useQueryClient } from "@tanstack/react-query";
import debounce from "lodash/debounce";
import {
useMemo,
useEffect,
useCallback,
useState,
useRef,
useContext,
} from "react";
import { shallow } from "zustand/shallow";
import dayjs from "@calcom/dayjs";
import { Booker as BookerComponent } from "@calcom/web/modules/bookings/components/Booker";
import {
BookerStoreProvider,
useInitializeBookerStoreContext,
useBookerStoreContext,
BookerStoreContext,
BookerStoreProvider,
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";
@@ -30,13 +18,17 @@ import { useTimesForSchedule } from "@calcom/features/schedules/lib/use-schedule
import { getRoutedTeamMemberIdsFromSearchParams } from "@calcom/lib/bookings/getRoutedTeamMemberIdsFromSearchParams";
import { localStorage } from "@calcom/lib/webstorage";
import { BookerLayouts } from "@calcom/prisma/zod-utils";
import { Booker as BookerComponent } from "@calcom/web/modules/bookings/components/Booker";
import { useQueryClient } from "@tanstack/react-query";
import debounce from "lodash/debounce";
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
import { shallow } from "zustand/shallow";
import { useCreateBooking } from "../hooks/bookings/useCreateBooking";
import { useCreateInstantBooking } from "../hooks/bookings/useCreateInstantBooking";
import { useCreateRecurringBooking } from "../hooks/bookings/useCreateRecurringBooking";
import {
useGetBookingForReschedule,
QUERY_KEY as BOOKING_RESCHEDULE_KEY,
useGetBookingForReschedule,
} from "../hooks/bookings/useGetBookingForReschedule";
import { useHandleBookEvent } from "../hooks/bookings/useHandleBookEvent";
import { useAtomGetPublicEvent } from "../hooks/event-types/public/useAtomGetPublicEvent";
@@ -56,9 +48,7 @@ import type {
} from "./types";
const BookerPlatformWrapperComponent = (
props:
| BookerPlatformWrapperAtomPropsForIndividual
| BookerPlatformWrapperAtomPropsForTeam
props: BookerPlatformWrapperAtomPropsForIndividual | BookerPlatformWrapperAtomPropsForTeam
) => {
const {
view = "MONTH_VIEW",
@@ -80,33 +70,24 @@ const BookerPlatformWrapperComponent = (
hideEventMetadata = false,
defaultPhoneCountry,
rrHostSubsetIds,
hideOrgTeamAvatar = false,
} = props;
const layout = BookerLayouts[view];
const { clientId } = useAtomsContext();
const teamId: number | undefined = props.isTeamEvent
? props.teamId
: undefined;
const teamId: number | undefined = props.isTeamEvent ? props.teamId : undefined;
const [_bookerState, setBookerState] = useBookerStoreContext(
(state) => [state.state, state.setState],
shallow
);
const setSelectedDate = useBookerStoreContext(
(state) => state.setSelectedDate
);
const setSelectedDuration = useBookerStoreContext(
(state) => state.setSelectedDuration
);
const setSelectedDate = useBookerStoreContext((state) => state.setSelectedDate);
const setSelectedDuration = useBookerStoreContext((state) => state.setSelectedDuration);
const setBookingData = useBookerStoreContext((state) => state.setBookingData);
const setOrg = useBookerStoreContext((state) => state.setOrg);
const bookingData = useBookerStoreContext((state) => state.bookingData);
const setSelectedTimeslot = useBookerStoreContext(
(state) => state.setSelectedTimeslot
);
const setSelectedTimeslot = useBookerStoreContext((state) => state.setSelectedTimeslot);
const setSelectedMonth = useBookerStoreContext((state) => state.setMonth);
const selectedDuration = useBookerStoreContext(
(state) => state.selectedDuration
);
const selectedDuration = useBookerStoreContext((state) => state.selectedDuration);
const [isOverlayCalendarEnabled, setIsOverlayCalendarEnabled] = useState(
Boolean(localStorage?.getItem?.("overlayCalendarSwitchDefault"))
@@ -121,14 +102,9 @@ const BookerPlatformWrapperComponent = (
}, []);
const debouncedStateChange = useMemo(() => {
return debounce(
(
currentStateValues: BookerStoreValues,
callback: (values: BookerStoreValues) => void
) => {
(currentStateValues: BookerStoreValues, callback: (values: BookerStoreValues) => void) => {
const prevState = prevStateRef.current;
const stateChanged =
!prevState ||
JSON.stringify(prevState) !== JSON.stringify(currentStateValues);
const stateChanged = !prevState || JSON.stringify(prevState) !== JSON.stringify(currentStateValues);
if (stateChanged) {
callback(currentStateValues);
@@ -156,12 +132,7 @@ const BookerPlatformWrapperComponent = (
unsubscribe();
debouncedStateChange.cancel();
};
}, [
onBookerStateChange,
getStateValues,
debouncedStateChange,
bookerStoreContext,
]);
}, [onBookerStateChange, getStateValues, debouncedStateChange, bookerStoreContext]);
useGetBookingForReschedule({
uid: props.rescheduleUid ?? props.bookingUid ?? "",
@@ -240,10 +211,7 @@ const BookerPlatformWrapperComponent = (
allowUpdatingUrlParams,
defaultPhoneCountry,
});
const [dayCount] = useBookerStoreContext(
(state) => [state.dayCount, state.setDayCount],
shallow
);
const [dayCount] = useBookerStoreContext((state) => [state.dayCount, state.setDayCount], shallow);
const selectedDate = useBookerStoreContext((state) => state.selectedDate);
const month = useBookerStoreContext((state) => state.month);
@@ -251,11 +219,7 @@ const BookerPlatformWrapperComponent = (
const { data: session } = useMe();
const hasSession = !!session;
const {
name: defaultName,
guests: defaultGuests,
...restFormValues
} = props.defaultFormValues ?? {};
const { name: defaultName, guests: defaultGuests, ...restFormValues } = props.defaultFormValues ?? {};
const prefillFormParamName = useMemo(() => {
if (defaultName) {
@@ -289,8 +253,7 @@ const BookerPlatformWrapperComponent = (
});
const startTime =
customStartTime &&
dayjs(customStartTime).isAfter(dayjs(calculatedStartTime))
customStartTime && dayjs(customStartTime).isAfter(dayjs(calculatedStartTime))
? dayjs(customStartTime).toISOString()
: calculatedStartTime;
const endTime = calculatedEndTime;
@@ -306,10 +269,8 @@ const BookerPlatformWrapperComponent = (
? new URLSearchParams(routingFormSearchParams)
: new URLSearchParams(window.location.search);
const routedTeamMemberIds =
getRoutedTeamMemberIdsFromSearchParams(searchParams);
const skipContactOwner =
searchParams.get("cal.skipContactOwner") === "true";
const routedTeamMemberIds = getRoutedTeamMemberIdsFromSearchParams(searchParams);
const skipContactOwner = searchParams.get("cal.skipContactOwner") === "true";
const isBookingDryRun =
searchParams?.get("cal.isBookingDryRun")?.toLowerCase() === "true" ||
@@ -349,12 +310,7 @@ const BookerPlatformWrapperComponent = (
});
useEffect(() => {
if (
schedule.data &&
!schedule.isPending &&
!schedule.error &&
onTimeslotsLoaded
) {
if (schedule.data && !schedule.isPending && !schedule.error && onTimeslotsLoaded) {
onTimeslotsLoaded(schedule.data.slots);
}
}, [schedule.data, schedule.isPending, schedule.error, onTimeslotsLoaded]);
@@ -429,17 +385,14 @@ const BookerPlatformWrapperComponent = (
onReserveSlotError: props.onReserveSlotError,
onDeleteSlotSuccess: props.onDeleteSlotSuccess,
onDeleteSlotError: props.onDeleteSlotError,
isBookingDryRun: props.isBookingDryRun
? props.isBookingDryRun
: routingParams?.isBookingDryRun,
isBookingDryRun: props.isBookingDryRun ? props.isBookingDryRun : routingParams?.isBookingDryRun,
handleSlotReservation,
});
const verifyEmail = useVerifyEmail({
email: bookerForm.formEmail,
name: bookerForm.formName,
requiresBookerEmailVerification:
event?.data?.requiresBookerEmailVerification,
requiresBookerEmailVerification: event?.data?.requiresBookerEmailVerification,
onVerifyEmail: bookerForm.beforeVerifyEmail,
});
@@ -453,10 +406,9 @@ const BookerPlatformWrapperComponent = (
},
});
const { data: connectedCalendars, isPending: fetchingConnectedCalendars } =
useConnectedCalendars({
enabled: hasSession,
});
const { data: connectedCalendars, isPending: fetchingConnectedCalendars } = useConnectedCalendars({
enabled: hasSession,
});
const calendars = connectedCalendars as ConnectedDestinationCalendars;
const { set, clearSet } = useLocalSet<{
@@ -477,11 +429,7 @@ const BookerPlatformWrapperComponent = (
onError: () => {
clearSet();
},
enabled: Boolean(
hasSession &&
isOverlayCalendarEnabled &&
latestCalendarsToLoad?.length > 0
),
enabled: Boolean(hasSession && isOverlayCalendarEnabled && latestCalendarsToLoad?.length > 0),
});
const handleBookEvent = useHandleBookEvent({
@@ -548,9 +496,7 @@ const BookerPlatformWrapperComponent = (
if (isOverlayCalendarEnabled && view === "MONTH_VIEW") {
localStorage?.removeItem("overlayCalendarSwitchDefault");
}
setIsOverlayCalendarEnabled(
Boolean(localStorage?.getItem?.("overlayCalendarSwitchDefault"))
);
setIsOverlayCalendarEnabled(Boolean(localStorage?.getItem?.("overlayCalendarSwitchDefault")));
}, [view, isOverlayCalendarEnabled]);
return (
@@ -579,16 +525,16 @@ const BookerPlatformWrapperComponent = (
confirmButtonDisabled={confirmButtonDisabled}
fromUserNameRedirected=""
hasSession={hasSession}
onGoBackInstantMeeting={function (): void {
onGoBackInstantMeeting={(): void => {
throw new Error("Function not implemented.");
}}
onConnectNowInstantMeeting={function (): void {
onConnectNowInstantMeeting={(): void => {
throw new Error("Function not implemented.");
}}
onOverlayClickNoCalendar={function (): void {
onOverlayClickNoCalendar={(): void => {
throw new Error("Function not implemented.");
}}
onClickOverlayContinue={function (): void {
onClickOverlayContinue={(): void => {
throw new Error("Function not implemented.");
}}
onOverlaySwitchStateChange={onOverlaySwitchStateChange}
@@ -602,14 +548,8 @@ const BookerPlatformWrapperComponent = (
bookingForm: bookerForm.bookingForm,
bookerFormErrorRef: bookerForm.bookerFormErrorRef,
errors: {
hasDataErrors:
isCreateBookingError ||
isCreateRecBookingError ||
isCreateInstantBookingError,
dataErrors:
createBookingError ||
createRecBookingError ||
createInstantBookingError,
hasDataErrors: isCreateBookingError || isCreateRecBookingError || isCreateInstantBookingError,
dataErrors: createBookingError || createRecBookingError || createInstantBookingError,
},
loadingStates: {
creatingBooking: creatingBooking,
@@ -645,15 +585,14 @@ const BookerPlatformWrapperComponent = (
isBookingDryRun={isBookingDryRun ?? routingParams?.isBookingDryRun}
eventMetaChildren={props.eventMetaChildren}
roundRobinHideOrgAndTeam={props.roundRobinHideOrgAndTeam}
hideOrgTeamAvatar={hideOrgTeamAvatar}
/>
</AtomsWrapper>
);
};
export const BookerPlatformWrapper = (
props:
| BookerPlatformWrapperAtomPropsForIndividual
| BookerPlatformWrapperAtomPropsForTeam
props: BookerPlatformWrapperAtomPropsForIndividual | BookerPlatformWrapperAtomPropsForTeam
) => {
return (
<BookerStoreProvider>
+3 -4
View File
@@ -1,17 +1,15 @@
import type React from "react";
import type { BookerProps } from "@calcom/features/bookings/Booker";
import type { BookerStore, CountryCode } from "@calcom/features/bookings/Booker/store";
import type { Timezone, VIEW_TYPE } from "@calcom/features/bookings/Booker/types";
import type { BookingCreateBody } from "@calcom/features/bookings/lib/bookingCreateBodySchema";
import type { BookingResponse } from "@calcom/platform-libraries";
import type {
ApiSuccessResponse,
ApiErrorResponse,
ApiSuccessResponse,
ApiSuccessResponseWithoutData,
RoutingFormSearchParams,
} from "@calcom/platform-types";
import type React from "react";
import type { UseCreateBookingInput } from "../hooks/bookings/useCreateBooking";
export type Slot = {
@@ -96,6 +94,7 @@ export type BookerPlatformWrapperAtomProps = Omit<
silentlyHandleCalendarFailures?: boolean;
hideEventMetadata?: boolean;
defaultPhoneCountry?: CountryCode;
hideOrgTeamAvatar?: boolean;
};
export type BookerPlatformWrapperAtomPropsForIndividual = BookerPlatformWrapperAtomProps & {
@@ -1,11 +1,6 @@
"use client";
import { useQueryClient } from "@tanstack/react-query";
import { useReducer, useState } from "react";
import AccountDialog from "@calcom/app-store/office365video/components/AccountDialog";
import { AppList } from "@calcom/web/modules/apps/components/AppList";
import DisconnectIntegrationModal from "@calcom/web/modules/apps/components/DisconnectIntegrationModal";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { GOOGLE_MEET, OFFICE_365_VIDEO, ZOOM } from "@calcom/platform-constants";
@@ -14,16 +9,20 @@ import type { App } from "@calcom/types/App";
import { Button } from "@calcom/ui/components/button";
import {
Dropdown,
DropdownMenuTrigger,
DropdownItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownItem,
DropdownMenuTrigger,
} from "@calcom/ui/components/dropdown";
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
import { SkeletonContainer, SkeletonText } from "@calcom/ui/components/skeleton";
import { AppList } from "@calcom/web/modules/apps/components/AppList";
import DisconnectIntegrationModal from "@calcom/web/modules/apps/components/DisconnectIntegrationModal";
import { useQueryClient } from "@tanstack/react-query";
import { useReducer, useState } from "react";
import { AtomsWrapper } from "../../src/components/atoms-wrapper";
import { useToast } from "../../src/components/ui/use-toast";
import { cn } from "../../src/lib/utils";
import type {
BulkUpdatParams,
UpdateUsersDefaultConferencingAppParams,
@@ -31,19 +30,35 @@ import type {
import { useAtomBulkUpdateEventTypesToDefaultLocation } from "./hooks/useAtomBulkUpdateEventTypesToDefaultLocation";
import { useAtomGetEventTypes } from "./hooks/useAtomGetEventTypes";
import {
useAtomsGetInstalledConferencingApps,
QUERY_KEY as atomsConferencingAppsQueryKey,
useAtomsGetInstalledConferencingApps,
} from "./hooks/useAtomsGetInstalledConferencingApps";
import { useConnect } from "./hooks/useConnect";
import { useDeleteCredential } from "./hooks/useDeleteCredential";
import {
useGetDefaultConferencingApp,
QUERY_KEY as defaultConferencingAppQueryKey,
useGetDefaultConferencingApp,
} from "./hooks/useGetDefaultConferencingApp";
import { useUpdateUserDefaultConferencingApp } from "./hooks/useUpdateUserDefaultConferencingApp";
type ConferencingAppSlug = typeof GOOGLE_MEET | typeof ZOOM | typeof OFFICE_365_VIDEO;
export type ConferencingAppsCustomClassNames = {
containerClassName?: string;
headerClassName?: string;
headerTitleClassName?: string;
headerDescriptionClassName?: string;
addButtonClassName?: string;
addDropdownClassName?: string;
appListClassName?: string;
appCardClassName?: string;
appCardMenuClassName?: string;
emptyScreenClassName?: string;
emptyScreenIconWrapperClassName?: string;
emptyScreenIconClassName?: string;
skeletonClassName?: string;
};
type ConferencingAppsViewPlatformWrapperProps = {
disableToasts?: boolean;
returnTo?: string;
@@ -51,13 +66,14 @@ type ConferencingAppsViewPlatformWrapperProps = {
teamId?: number;
apps?: ConferencingAppSlug[];
disableBulkUpdateEventTypes?: boolean;
customClassNames?: ConferencingAppsCustomClassNames;
};
type RemoveAppParams = { callback: () => void; app?: App["slug"] };
const SkeletonLoader = () => {
const SkeletonLoader = ({ className }: { className?: string }) => {
return (
<SkeletonContainer>
<SkeletonContainer className={className}>
<div className="divide-subtle border-subtle stack-y-6 rounded-b-lg border border-t-0 px-6 py-4">
<SkeletonText className="h-8 w-full" />
<SkeletonText className="h-8 w-full" />
@@ -79,6 +95,7 @@ export const ConferencingAppsViewPlatformWrapper = ({
teamId,
apps,
disableBulkUpdateEventTypes = false,
customClassNames,
}: ConferencingAppsViewPlatformWrapperProps) => {
const { t } = useLocale();
const queryClient = useQueryClient();
@@ -200,15 +217,23 @@ export const ConferencingAppsViewPlatformWrapper = ({
teamId,
});
const AddConferencingButtonPlatform = ({ installedApps }: { installedApps?: Array<{ slug: string }> }) => {
const AddConferencingButtonPlatform = ({
installedApps,
buttonClassName,
dropdownClassName,
}: {
installedApps?: Array<{ slug: string }>;
buttonClassName?: string;
dropdownClassName?: string;
}) => {
return (
<Dropdown>
<DropdownMenuTrigger asChild>
<Button color="secondary" StartIcon="plus">
<Button color="secondary" StartIcon="plus" className={buttonClassName}>
{t("add")}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuContent className={dropdownClassName}>
{/* Show Google Meet if it's not installed and either no apps filter is provided or it's in the apps filter */}
{installedApps &&
!installedApps.find((app) => app.slug === GOOGLE_MEET) &&
@@ -247,17 +272,23 @@ export const ConferencingAppsViewPlatformWrapper = ({
};
return (
<AtomsWrapper>
<AtomsWrapper customClassName={customClassNames?.containerClassName}>
<SettingsHeader
title={t("conferencing")}
description={t("conferencing_description")}
CTA={<AddConferencingButtonPlatform installedApps={installedIntegrationsQuery.data?.items} />}
CTA={
<AddConferencingButtonPlatform
installedApps={installedIntegrationsQuery.data?.items}
buttonClassName={customClassNames?.addButtonClassName}
dropdownClassName={customClassNames?.addDropdownClassName}
/>
}
borderInShellHeader={true}>
<>
<div className="bg-default w-full sm:mx-0 xl:mt-0">
<QueryCell
query={installedIntegrationsQuery}
customLoader={<SkeletonLoader />}
customLoader={<SkeletonLoader className={customClassNames?.skeletonClassName} />}
success={({ data }) => {
if (!data.items.length) {
return (
@@ -267,13 +298,27 @@ export const ConferencingAppsViewPlatformWrapper = ({
category: t("conferencing").toLowerCase(),
})}
description={t("no_category_apps_description_conferencing")}
buttonRaw={<AddConferencingButtonPlatform installedApps={data?.items} />}
buttonRaw={
<AddConferencingButtonPlatform
installedApps={data?.items}
buttonClassName={customClassNames?.addButtonClassName}
dropdownClassName={customClassNames?.addDropdownClassName}
/>
}
className={customClassNames?.emptyScreenClassName}
iconWrapperClassName={customClassNames?.emptyScreenIconWrapperClassName}
iconClassName={customClassNames?.emptyScreenIconClassName}
/>
);
}
return (
<AppList
listClassName="rounded-lg rounded-t-none border-t-0 max-w-full"
listClassName={cn(
"rounded-lg rounded-t-none border-t-0 max-w-full",
customClassNames?.appListClassName
)}
appCardClassName={customClassNames?.appCardClassName}
appCardMenuClassName={customClassNames?.appCardMenuClassName}
handleDisconnect={handleDisconnect}
data={data}
variant="conferencing"
@@ -4,10 +4,10 @@ import {
} from "@calcom/web/modules/event-types/components/tabs/assignment/EventTeamAssignmentTab";
const EventTeamAssignmentTabPlatformWrapper = (
props: Omit<EventTeamAssignmentTabBaseProps, "isSegmentApplicable">
props: Omit<EventTeamAssignmentTabBaseProps, "isSegmentApplicable" | "hideFixedHostsForCollective">
) => {
// todo: implement attributes for platform orgs for segment
return <EventTeamAssignmentTab {...props} isSegmentApplicable={false} />;
return <EventTeamAssignmentTab {...props} isSegmentApplicable={false} hideFixedHostsForCollective={true} />;
};
export default EventTeamAssignmentTabPlatformWrapper;
+1
View File
@@ -32,6 +32,7 @@ export { EventTypePlatformWrapper as EventTypeSettings } from "./event-types/wra
export type { EventSettingsFromRef } from "./event-types/wrappers/types";
export type { AvailabilitySettingsFormRef } from "./availability/types";
export { ConferencingAppsViewPlatformWrapper as ConferencingAppsSettings } from "./connect/conferencing-apps/ConferencingAppsViewPlatformWrapper";
export type { ConferencingAppsCustomClassNames } from "./connect/conferencing-apps/ConferencingAppsViewPlatformWrapper";
export { StripeConnect } from "./connect/stripe/StripeConnect";
export { CreateEventTypePlatformWrapper as CreateEventType } from "./event-types/wrappers/CreateEventTypePlatformWrapper";
export { PaymentForm } from "./event-types/payments/PaymentForm";
@@ -1,21 +1,32 @@
import { Navbar } from "@/components/Navbar";
import { type ConferencingAppsCustomClassNames, ConferencingAppsSettings } from "@calcom/atoms";
import { Inter } from "next/font/google";
import { usePathname } from "next/navigation";
import { ConferencingAppsSettings } from "@calcom/atoms";
import { Navbar } from "@/components/Navbar";
const inter = Inter({ subsets: ["latin"] });
export default function ConferencingApps(props: { calUsername: string; calEmail: string }) {
const pathname = usePathname();
const callbackUri = `${window.location.origin}${pathname}`;
const customClassNames: ConferencingAppsCustomClassNames = {
containerClassName: "bg-red-50 p-4 rounded-lg",
addButtonClassName: "bg-blue-600 hover:bg-blue-700 text-white",
addDropdownClassName: "bg-indigo-50 border-2 border-indigo-300",
appListClassName: "border-2 border-green-500",
appCardClassName: "bg-blue-50 border-b-2 border-blue-300",
appCardMenuClassName: "bg-pink-50 border-2 border-pink-300",
emptyScreenClassName: "bg-yellow-50 border-yellow-300",
emptyScreenIconWrapperClassName: "bg-purple-100",
emptyScreenIconClassName: "text-purple-600",
skeletonClassName: "bg-gray-300",
};
return (
<main
className={`flex min-h-screen flex-col ${inter.className} main text-default flex min-h-full w-full flex-col items-center overflow-visible`}>
<Navbar username={props.calUsername} />
<div className="my-8">
<ConferencingAppsSettings returnTo={callbackUri} onErrorReturnTo={callbackUri} />
<ConferencingAppsSettings returnTo={callbackUri} onErrorReturnTo={callbackUri} customClassNames={customClassNames}/>
</div>
</main>
);