refactor: event advanced web wrapper (#16711)

* chore: EventTypeWebWrapper base pr

* fixup! chore: EventTypeWebWrapper base pr

* fixup! Merge branch 'main' into morgan/cal-4183-eventtypeatom-web-wrapper-base

* wip

* fix: error

* Event type atom wrapper handles app dir

* fixup! Merge branch 'main' into morgan/cal-4183-eventtypeatom-web-wrapper-base

* fixup! fixup! Merge branch 'main' into morgan/cal-4183-eventtypeatom-web-wrapper-base

* remove console log

* review comments

* fixup! review comments

* refactor: event advanced web wrapper

* rename `connectedCalendars` -> `calendarsQueryData`

* Update BookerLayoutSelector.tsx

* moved useMeQuery up the parent tree

---------

Co-authored-by: Morgan Vernay <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
This commit is contained in:
Somay Chauhan
2024-09-30 12:15:11 +03:00
committed by GitHub
co-authored by Morgan Vernay Morgan
parent 34ba27407b
commit c834b927b6
7 changed files with 98 additions and 31 deletions
@@ -11,6 +11,7 @@ const CreateEventsOnCalendarSelect = (props: ICreateEventsOnCalendarSelectProps)
const { calendar } = props;
const { t } = useLocale();
const mutation = trpc.viewer.setDestinationCalendar.useMutation();
const connectedCalendarsQuery = trpc.viewer.connectedCalendars.useQuery();
return (
<>
@@ -26,6 +27,7 @@ const CreateEventsOnCalendarSelect = (props: ICreateEventsOnCalendarSelectProps)
mutation.mutate(calendar);
}}
hidePlaceholder
calendarsQueryData={connectedCalendarsQuery.data}
/>
</div>
</div>
@@ -304,6 +304,7 @@ const AppearanceView = ({
description={t("bookerlayout_user_settings_description")}
isDisabled={isBookerLayoutFormSubmitting || !isBookerLayoutFormDirty}
isLoading={mutation.isPending}
user={user}
/>
</Form>
@@ -4,19 +4,18 @@ import type { OptionProps, SingleValueProps } from "react-select";
import { components } from "react-select";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { DestinationCalendar } from "@calcom/prisma/client";
import { trpc } from "@calcom/trpc/react";
import type { RouterOutputs } from "@calcom/trpc/react";
import { Badge, Icon, Select } from "@calcom/ui";
interface Props {
onChange: (value: { externalId: string; integration: string }) => void;
isPending?: boolean;
hidePlaceholder?: boolean;
/** The external Id of the connected calendar */
destinationCalendar?: DestinationCalendar | null;
/** The external Id of the connected calendar */ // destinationCalendar?: DestinationCalendar | null;
value: string | undefined;
maxWidth?: number;
hideAdvancedText?: boolean;
calendarsQueryData?: RouterOutputs["viewer"]["connectedCalendars"];
}
interface Option {
@@ -53,9 +52,12 @@ const DestinationCalendarSelector = ({
hidePlaceholder,
hideAdvancedText,
maxWidth,
calendarsQueryData,
}: Props): JSX.Element | null => {
const { t } = useLocale();
const query = trpc.viewer.connectedCalendars.useQuery();
const connectedCalendarsList = calendarsQueryData?.connectedCalendars;
const destinationCalendar = calendarsQueryData?.destinationCalendar;
const [selectedOption, setSelectedOption] = useState<{
value: string;
label: string;
@@ -80,13 +82,13 @@ const DestinationCalendarSelector = ({
};
useEffect(() => {
const selected = query.data?.connectedCalendars
.map((connected) => connected.calendars ?? [])
const selected = connectedCalendarsList
?.map((connected) => connected.calendars ?? [])
.flat()
.find((cal) => cal.externalId === value);
if (selected) {
const selectedIntegration = query.data?.connectedCalendars.find((integration) =>
const selectedIntegration = connectedCalendarsList?.find((integration) =>
integration.calendars?.some((calendar) => calendar.externalId === selected.externalId)
);
@@ -98,13 +100,13 @@ const DestinationCalendarSelector = ({
})`,
});
}
}, [query.data?.connectedCalendars]);
}, [connectedCalendarsList]);
if (!query.data?.connectedCalendars.length) {
if (!connectedCalendarsList?.length) {
return null;
}
const options =
query.data.connectedCalendars.map((selectedCalendar) => ({
connectedCalendarsList?.map((selectedCalendar) => ({
key: selectedCalendar.credentialId,
label: `${selectedCalendar.integration.title?.replace(/calendar/i, "")} (${
selectedCalendar.primary?.integration === "office365_calendar"
@@ -122,8 +124,6 @@ const DestinationCalendarSelector = ({
})),
})) ?? [];
const queryDestinationCalendar = query.data.destinationCalendar;
return (
<div
className="relative table w-full table-fixed"
@@ -136,8 +136,8 @@ const DestinationCalendarSelector = ({
) : (
<span className="text-default min-w-0 overflow-hidden truncate whitespace-nowrap">
<Badge variant="blue">Default</Badge>{" "}
{queryDestinationCalendar.name &&
`${queryDestinationCalendar.name} (${queryDestinationCalendar?.integrationTitle} - ${queryDestinationCalendar.primaryEmail})`}
{destinationCalendar?.name &&
`${destinationCalendar.name} (${destinationCalendar?.integrationTitle} - ${destinationCalendar.primaryEmail})`}
</span>
)
}
@@ -3,6 +3,7 @@ import { useEffect, useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import type { z } from "zod";
import type { EventAdvancedWebWrapperProps } from "@calcom/atoms/event-types/wrappers/EventAdvancedWebWrapper";
import type { EventNameObjectType } from "@calcom/core/event";
import { getEventName } from "@calcom/core/event";
import getLocationsOptionsForSelect from "@calcom/features/bookings/lib/getLocationOptionsForSelect";
@@ -12,21 +13,26 @@ import {
allowDisablingAttendeeConfirmationEmails,
allowDisablingHostConfirmationEmails,
} from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails";
import type { FormValues, EventTypeSetupProps } from "@calcom/features/eventtypes/lib/types";
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
import { FormBuilder } from "@calcom/features/form-builder/FormBuilder";
import type { fieldSchema } from "@calcom/features/form-builder/schema";
import type { EditableSchema } from "@calcom/features/form-builder/schema";
import { BookerLayoutSelector } from "@calcom/features/settings/BookerLayoutSelector";
import { classNames } from "@calcom/lib";
import cx from "@calcom/lib/classNames";
import { DEFAULT_LIGHT_BRAND_COLOR, DEFAULT_DARK_BRAND_COLOR } from "@calcom/lib/constants";
import { APP_NAME, IS_VISUAL_REGRESSION_TESTING, WEBSITE_URL } from "@calcom/lib/constants";
import {
DEFAULT_LIGHT_BRAND_COLOR,
DEFAULT_DARK_BRAND_COLOR,
APP_NAME,
IS_VISUAL_REGRESSION_TESTING,
WEBSITE_URL,
} from "@calcom/lib/constants";
import { generateHashedLink } from "@calcom/lib/generateHashedLink";
import { checkWCAGContrastColor } from "@calcom/lib/getBrandColours";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { Prisma } from "@calcom/prisma/client";
import { SchedulingType } from "@calcom/prisma/enums";
import { trpc } from "@calcom/trpc/react";
import type { RouterOutputs } from "@calcom/trpc/react";
import {
Alert,
Button,
@@ -50,9 +56,19 @@ type BookingField = z.infer<typeof fieldSchema>;
const CustomEventTypeModal = dynamic(() => import("./CustomEventTypeModal"));
export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps, "eventType" | "team">) => {
const connectedCalendarsQuery = trpc.viewer.connectedCalendars.useQuery();
const { data: user } = trpc.viewer.me.useQuery();
export type EventAdvancedTabProps = EventAdvancedWebWrapperProps & {
calendarsQueryData?: RouterOutputs["viewer"]["connectedCalendars"];
user?: RouterOutputs["viewer"]["me"];
isUserLoading?: boolean;
};
export const EventAdvancedTab = ({
eventType,
team,
calendarsQueryData,
user,
isUserLoading,
}: EventAdvancedTabProps) => {
const formMethods = useFormContext<FormValues>();
const { t } = useLocale();
const [showEventNameTip, setShowEventNameTip] = useState(false);
@@ -154,7 +170,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
);
const displayDestinationCalendarSelector =
!!connectedCalendarsQuery.data?.connectedCalendars.length && (!team || isChildrenManagedEventType);
!!calendarsQueryData?.connectedCalendars?.length && (!team || isChildrenManagedEventType);
const verifiedSecondaryEmails = [
{
@@ -186,6 +202,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
onChange={onChange}
hidePlaceholder
hideAdvancedText
calendarsQueryData={calendarsQueryData}
/>
)}
/>
@@ -261,7 +278,13 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
)}
</div>
</div>
<BookerLayoutSelector fallbackToUserSettings isDark={selectedThemeIsDark} isOuterBorder={true} />
<BookerLayoutSelector
fallbackToUserSettings
isDark={selectedThemeIsDark}
isOuterBorder={true}
user={user}
isUserLoading={isUserLoading}
/>
<div className="border-subtle space-y-6 rounded-lg border p-6">
<FormBuilder
title={t("booking_questions_title")}
@@ -8,7 +8,7 @@ import { classNames } from "@calcom/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { BookerLayouts, defaultBookerLayoutSettings } from "@calcom/prisma/zod-utils";
import { bookerLayoutOptions, type BookerLayoutSettings } from "@calcom/prisma/zod-utils";
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
import type { RouterOutputs } from "@calcom/trpc/react";
import { Label, CheckboxField, Button } from "@calcom/ui";
import SectionBottomActions from "./SectionBottomActions";
@@ -34,6 +34,8 @@ type BookerLayoutSelectorProps = {
isLoading?: boolean;
isDisabled?: boolean;
isOuterBorder?: boolean;
user?: RouterOutputs["viewer"]["me"];
isUserLoading?: boolean;
};
const defaultFieldName = "metadata.bookerLayouts";
@@ -47,6 +49,8 @@ export const BookerLayoutSelector = ({
isDisabled = false,
isOuterBorder = false,
isLoading = false,
user,
isUserLoading,
}: BookerLayoutSelectorProps) => {
const { control, getValues } = useFormContext();
const { t } = useLocale();
@@ -77,6 +81,8 @@ export const BookerLayoutSelector = ({
onChange={onChange}
isDark={isDark}
isOuterBorder={isOuterBorder}
user={user}
isUserLoading={isUserLoading}
/>
{!isOuterBorder && (
<SectionBottomActions align="end">
@@ -98,6 +104,8 @@ type BookerLayoutFieldsProps = {
showUserSettings: boolean;
isDark?: boolean;
isOuterBorder?: boolean;
user?: RouterOutputs["viewer"]["me"];
isUserLoading?: boolean;
};
type BookerLayoutState = { [key in BookerLayouts]: boolean };
@@ -108,9 +116,10 @@ const BookerLayoutFields = ({
showUserSettings,
isDark,
isOuterBorder,
user,
isUserLoading,
}: BookerLayoutFieldsProps) => {
const { t } = useLocale();
const { isPending: isUserLoading, data: user } = useMeQuery();
const [isOverridingSettings, setIsOverridingSettings] = useState(false);
const disableFields = showUserSettings && !isOverridingSettings;
@@ -0,0 +1,27 @@
import { EventAdvancedTab } from "@calcom/features/eventtypes/components/tabs/advanced/EventAdvancedTab";
import type { EventTypeSetupProps } from "@calcom/features/eventtypes/lib/types";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
export type EventAdvancedWebWrapperProps = Pick<EventTypeSetupProps, "eventType" | "team"> & {
loggedInUser?: RouterOutputs["viewer"]["me"];
isLoggedInUserPending?: boolean;
};
const EventAdvancedWebWrapper = ({
loggedInUser,
isLoggedInUserPending,
...props
}: EventAdvancedWebWrapperProps) => {
const connectedCalendarsQuery = trpc.viewer.connectedCalendars.useQuery();
return (
<EventAdvancedTab
{...props}
calendarsQueryData={connectedCalendarsQuery.data}
user={loggedInUser}
isUserLoading={isLoggedInUserPending}
/>
);
};
export default EventAdvancedWebWrapper;
@@ -54,9 +54,7 @@ const EventLimitsTab = dynamic(() =>
const EventAdvancedTab = dynamic(() =>
// import web wrapper when it's ready
import("@calcom/features/eventtypes/components/tabs/advanced/EventAdvancedTab").then(
(mod) => mod.EventAdvancedTab
)
import("./EventAdvancedWebWrapper").then((mod) => mod)
);
const EventInstantTab = dynamic(() =>
@@ -143,7 +141,7 @@ const EventTypeWeb = ({
const { t } = useLocale();
const utils = trpc.useUtils();
const { data: loggedInUser } = useMeQuery();
const { data: loggedInUser, isPending: isLoggedInUserPending } = useMeQuery();
const isTeamEventTypeDeleted = useRef(false);
const leaveWithoutAssigningHosts = useRef(false);
const telemetry = useTelemetry();
@@ -229,7 +227,14 @@ const EventTypeWeb = ({
),
team: <EventTeamAssignmentTab teamMembers={teamMembers} team={team} eventType={eventType} />,
limits: <EventLimitsTab eventType={eventType} />,
advanced: <EventAdvancedTab eventType={eventType} team={team} />,
advanced: (
<EventAdvancedTab
eventType={eventType}
team={team}
loggedInUser={loggedInUser}
isLoggedInUserPending={isLoggedInUserPending}
/>
),
instant: <EventInstantTab eventType={eventType} isTeamEvent={!!team} />,
recurring: <EventRecurringTab eventType={eventType} />,
apps: <EventAppsTab eventType={{ ...eventType, URL: permalink }} />,