From ac300ff6703df1d8e894ea92c4ccad0af3f343a0 Mon Sep 17 00:00:00 2001 From: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:14:04 +0530 Subject: [PATCH] refactor: enable infinite scrolling for all (#16463) * refactor: enable infinite scrolling for all * chore: remove unused code * fix: tests * chore: type err * chore: update email embed test * chore: update tests * chore: update tests * chore: fix horizontal tabitem * chore * fix: conflicts * chore: conflict * fix: E2E tests * chore: remove idle * fix test --------- Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> --- apps/web/app/WithAppDirSsr.tsx | 3 - .../lib/event-types/getServerSideProps.tsx | 7 +- .../views/event-types-listing-view.tsx | 852 +----------------- .../settings/teams/[id]/event-types-view.tsx | 1 - .../manage-booking-questions.e2e.ts | 6 +- .../web/playwright/managed-event-types.e2e.ts | 10 +- .../organization/across-org/across-org.e2e.ts | 17 +- apps/web/playwright/teams.e2e.ts | 2 +- .../ee/teams/components/TeamEventTypeForm.tsx | 8 +- .../components/CreateEventTypeDialog.tsx | 9 +- .../eventtypes/components/DuplicateDialog.tsx | 16 +- packages/lib/constants.ts | 4 - packages/lib/hooks/useCreateEventType.ts | 15 +- turbo.json | 1 - 14 files changed, 67 insertions(+), 884 deletions(-) diff --git a/apps/web/app/WithAppDirSsr.tsx b/apps/web/app/WithAppDirSsr.tsx index 5fe8492c85..110c3ce2df 100644 --- a/apps/web/app/WithAppDirSsr.tsx +++ b/apps/web/app/WithAppDirSsr.tsx @@ -21,8 +21,5 @@ export const withAppDirSsr = ...("trpcState" in props && { dehydratedState: props.trpcState, }), - ...("isInfiniteScrollEnabled" in props && { - isInfiniteScrollEnabled: props.isInfiniteScrollEnabled, - }), }; }; diff --git a/apps/web/lib/event-types/getServerSideProps.tsx b/apps/web/lib/event-types/getServerSideProps.tsx index a15ceaea62..76726887e5 100644 --- a/apps/web/lib/event-types/getServerSideProps.tsx +++ b/apps/web/lib/event-types/getServerSideProps.tsx @@ -1,7 +1,6 @@ import type { GetServerSidePropsContext } from "next"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; -import { ENABLE_INFINITE_EVENT_TYPES_FOR_ORG } from "@calcom/lib/constants"; import { ssrInit } from "@server/lib/ssr"; @@ -18,9 +17,5 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => }; } - const isInfiniteScrollEnabled = session.user?.org?.slug - ? ENABLE_INFINITE_EVENT_TYPES_FOR_ORG.includes(session.user.org.slug) - : false; - - return { props: { trpcState: ssr.dehydrate(), isInfiniteScrollEnabled } }; + return { props: { trpcState: ssr.dehydrate() } }; }; diff --git a/apps/web/modules/event-types/views/event-types-listing-view.tsx b/apps/web/modules/event-types/views/event-types-listing-view.tsx index 5f41104e13..4ff1eff4f9 100644 --- a/apps/web/modules/event-types/views/event-types-listing-view.tsx +++ b/apps/web/modules/event-types/views/event-types-listing-view.tsx @@ -14,10 +14,7 @@ import { EventTypeEmbedButton, EventTypeEmbedDialog } from "@calcom/features/emb import { EventTypeDescription } from "@calcom/features/eventtypes/components"; import CreateEventTypeDialog from "@calcom/features/eventtypes/components/CreateEventTypeDialog"; import { DuplicateDialog } from "@calcom/features/eventtypes/components/DuplicateDialog"; -import SkeletonLoader, { - InfiniteSkeletonLoader, -} from "@calcom/features/eventtypes/components/SkeletonLoader"; -import { TeamsFilter } from "@calcom/features/filters/components/TeamsFilter"; +import { InfiniteSkeletonLoader } from "@calcom/features/eventtypes/components/SkeletonLoader"; import { getTeamsFiltersFromQuery } from "@calcom/features/filters/lib/getTeamsFiltersFromQuery"; import Shell from "@calcom/features/shell/Shell"; import { parseEventTypeColor } from "@calcom/lib"; @@ -25,7 +22,6 @@ import { APP_NAME } from "@calcom/lib/constants"; import { WEBSITE_URL } from "@calcom/lib/constants"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import useMediaQuery from "@calcom/lib/hooks/useMediaQuery"; import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery"; import { useGetTheme } from "@calcom/lib/hooks/useTheme"; import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery"; @@ -38,7 +34,6 @@ import { trpc, TRPCClientError } from "@calcom/trpc/react"; import type { UserProfile } from "@calcom/types/UserProfile"; import { Alert, - Avatar, Badge, Button, ButtonGroup, @@ -62,7 +57,6 @@ import { Tooltip, ArrowButton, UserAvatarGroup, - Icon, } from "@calcom/ui"; import type { AppProps } from "@lib/app-providers"; @@ -76,8 +70,6 @@ type InfiniteEventTypeGroup = GetUserEventGroupsResponse["eventTypeGroups"][numb type InfiniteEventType = GetEventTypesFromGroupsResponse["eventTypes"][number]; type EventTypeGroups = RouterOutputs["viewer"]["eventTypes"]["getByViewer"]["eventTypeGroups"]; -type EventTypeGroupProfile = EventTypeGroups[number]["profile"]; -type GetByViewerResponse = RouterOutputs["viewer"]["eventTypes"]["getByViewer"] | undefined; type EventTypeGroup = EventTypeGroups[number]; type EventType = EventTypeGroup["eventTypes"][number]; @@ -89,19 +81,8 @@ type DeNormalizedEventType = Omit & { })[]; }; -type DeNormalizedEventTypeGroup = Omit & { - eventTypes: DeNormalizedEventType[]; -}; - const LIMIT = 10; -interface EventTypeListHeadingProps { - profile: EventTypeGroupProfile; - membershipCount: number; - teamId?: number | null; - bookerUrl: string; -} - interface InfiniteEventTypeListProps { group: InfiniteEventTypeGroup; readOnly: boolean; @@ -111,56 +92,15 @@ interface InfiniteEventTypeListProps { isPending?: boolean; } -interface EventTypeListProps { - group: DeNormalizedEventTypeGroup; - groupIndex: number; - readOnly: boolean; - bookerUrl: string | null; - types: DeNormalizedEventType[]; - lockedByOrg?: boolean; -} - -interface InfiniteMobileTeamsTabProps { +interface InfiniteTeamsTabProps { activeEventTypeGroup: InfiniteEventTypeGroup; } -interface MobileTeamsTabProps { - eventTypeGroups: DeNormalizedEventTypeGroup[]; -} - const querySchema = z.object({ teamId: z.nullable(z.coerce.number()).optional().default(null), }); -const MobileTeamsTab: FC = (props) => { - const { eventTypeGroups } = props; - const tabs = eventTypeGroups.map((item) => ({ - name: item.profile.name ?? "", - href: item.teamId ? `/event-types?teamId=${item.teamId}` : "/event-types?noTeam", - avatar: item.profile.image, - })); - const { data } = useTypedQuery(querySchema); - const events = eventTypeGroups.filter((item) => item.teamId === data.teamId); - - return ( -
- - {events.length > 0 ? ( - - ) : ( - - )} -
- ); -}; - -const InfiniteMobileTeamsTab: FC = (props) => { +const InfiniteTeamsTab: FC = (props) => { const { activeEventTypeGroup } = props; const { t } = useLocale(); @@ -292,510 +232,6 @@ const Item = ({ const MemoizedItem = memo(Item); -export const EventTypeList = ({ - group, - groupIndex, - readOnly, - types, - bookerUrl, - lockedByOrg, -}: EventTypeListProps): JSX.Element => { - const { t } = useLocale(); - const router = useRouter(); - const pathname = usePathname(); - const searchParams = useCompatSearchParams(); - const [parent] = useAutoAnimate(); - const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); - const [deleteDialogTypeId, setDeleteDialogTypeId] = useState(0); - const [deleteDialogTypeSchedulingType, setDeleteDialogSchedulingType] = useState( - null - ); - const utils = trpc.useUtils(); - const mutation = trpc.viewer.eventTypeOrder.useMutation({ - onError: async (err) => { - console.error(err.message); - await utils.viewer.eventTypes.getByViewer.cancel(); - await utils.viewer.eventTypes.invalidate(); - }, - onSettled: () => { - utils.viewer.eventTypes.invalidate(); - }, - }); - - const setHiddenMutation = trpc.viewer.eventTypes.update.useMutation({ - onMutate: async ({ id }) => { - await utils.viewer.eventTypes.getByViewer.cancel(); - const previousValue = utils.viewer.eventTypes.getByViewer.getData(); - if (previousValue) { - const newList = [...types.map(normalizeEventType)]; - const itemIndex = newList.findIndex((item) => item.id === id); - if (itemIndex !== -1 && newList[itemIndex]) { - newList[itemIndex].hidden = !newList[itemIndex].hidden; - } - utils.viewer.eventTypes.getByViewer.setData(undefined, { - ...previousValue, - eventTypeGroups: [ - ...previousValue.eventTypeGroups.slice(0, groupIndex), - { ...group, eventTypes: newList }, - ...previousValue.eventTypeGroups.slice(groupIndex + 1), - ], - }); - } - return { previousValue }; - }, - onError: async (err, _, context) => { - if (context?.previousValue) { - utils.viewer.eventTypes.getByViewer.setData(undefined, context.previousValue); - } - console.error(err.message); - }, - onSettled: () => { - // REVIEW: Should we invalidate the entire router or just the `getByViewer` query? - utils.viewer.eventTypes.invalidate(); - }, - }); - - async function moveEventType(index: number, increment: 1 | -1) { - const newList = [...types.map(normalizeEventType)]; - - const type = types[index]; - const tmp = types[index + increment]; - if (tmp) { - newList[index] = normalizeEventType(tmp); - newList[index + increment] = normalizeEventType(type); - } - - await utils.viewer.eventTypes.getByViewer.cancel(); - - const previousValue = utils.viewer.eventTypes.getByViewer.getData(); - if (previousValue) { - utils.viewer.eventTypes.getByViewer.setData(undefined, { - ...previousValue, - eventTypeGroups: [ - ...previousValue.eventTypeGroups.slice(0, groupIndex), - { ...group, eventTypes: newList }, - ...previousValue.eventTypeGroups.slice(groupIndex + 1), - ], - }); - } - - mutation.mutate({ - ids: newList.map((type) => type.id), - }); - } - - async function deleteEventTypeHandler(id: number) { - const payload = { id }; - deleteMutation.mutate(payload); - } - - // inject selection data into url for correct router history - const openDuplicateModal = (eventType: DeNormalizedEventType, group: DeNormalizedEventTypeGroup) => { - const newSearchParams = new URLSearchParams(searchParams ?? undefined); - function setParamsIfDefined(key: string, value: string | number | boolean | null | undefined) { - if (value) newSearchParams.set(key, value.toString()); - if (value === null) newSearchParams.delete(key); - } - setParamsIfDefined("dialog", "duplicate"); - setParamsIfDefined("title", eventType.title); - setParamsIfDefined("description", eventType.description); - setParamsIfDefined("slug", eventType.slug); - setParamsIfDefined("id", eventType.id); - setParamsIfDefined("length", eventType.length); - setParamsIfDefined("pageSlug", group.profile.slug); - setParamsIfDefined("teamId", eventType.teamId); - setParamsIfDefined("parentId", eventType.parentId); - router.push(`${pathname}?${newSearchParams.toString()}`); - }; - - const deleteMutation = trpc.viewer.eventTypes.delete.useMutation({ - onSuccess: () => { - showToast(t("event_type_deleted_successfully"), "success"); - setDeleteDialogOpen(false); - }, - onMutate: async ({ id }) => { - await utils.viewer.eventTypes.getByViewer.cancel(); - const previousValue = utils.viewer.eventTypes.getByViewer.getData(); - if (previousValue) { - const newList = types.filter((item) => item.id !== id).map(normalizeEventType); - - utils.viewer.eventTypes.getByViewer.setData(undefined, { - ...previousValue, - eventTypeGroups: [ - ...previousValue.eventTypeGroups.slice(0, groupIndex), - { ...group, eventTypes: newList }, - ...previousValue.eventTypeGroups.slice(groupIndex + 1), - ], - }); - } - return { previousValue }; - }, - onError: (err, _, context) => { - if (context?.previousValue) { - utils.viewer.eventTypes.getByViewer.setData(undefined, context.previousValue); - } - if (err instanceof HttpError) { - const message = `${err.statusCode}: ${err.message}`; - showToast(message, "error"); - setDeleteDialogOpen(false); - } else if (err instanceof TRPCClientError) { - showToast(err.message, "error"); - } - }, - onSettled: () => { - // REVIEW: Should we invalidate the entire router or just the `getByViewer` query? - utils.viewer.eventTypes.invalidate(); - }, - }); - - const [isNativeShare, setNativeShare] = useState(true); - - useEffect(() => { - if (!navigator.share) { - setNativeShare(false); - } - }, []); - - if (!types.length) { - return group.teamId ? ( - - ) : !group.profile.eventTypesLockedByOrg ? ( - - ) : ( - <> - ); - } - - const firstItem = types[0]; - const lastItem = types[types.length - 1]; - const isManagedEventPrefix = () => { - return deleteDialogTypeSchedulingType === SchedulingType.MANAGED ? "_managed" : ""; - }; - return ( -
-
    - {types.map((type, index) => { - const embedLink = `${group.profile.slug}/${type.slug}`; - const calLink = `${bookerUrl}/${embedLink}`; - const isPrivateURLEnabled = type.hashedLink?.link; - const placeholderHashedLink = `${WEBSITE_URL}/d/${type.hashedLink?.link}/${type.slug}`; - const isManagedEventType = type.schedulingType === SchedulingType.MANAGED; - const isChildrenManagedEventType = - type.metadata?.managedEventConfig !== undefined && type.schedulingType !== SchedulingType.MANAGED; - return ( -
  • -
    -
    - {!(firstItem && firstItem.id === type.id) && ( - moveEventType(index, -1)} arrowDirection="up" /> - )} - - {!(lastItem && lastItem.id === type.id) && ( - moveEventType(index, 1)} arrowDirection="down" /> - )} - -
    -
    - {!!type.teamId && !isManagedEventType && ( - - )} - {isManagedEventType && type?.children && type.children?.length > 0 && ( - ch.users) ?? []} - /> - )} -
    - {!isManagedEventType && ( - <> - {type.hidden && {t("hidden")}} - -
    - { - setHiddenMutation.mutate({ id: type.id, hidden: !type.hidden }); - }} - /> -
    -
    - - )} - - - {!isManagedEventType && ( - <> - -
    -
    -
    -
    -
    - - -
    -
    -
  • - ); - })} -
- - { - e.preventDefault(); - deleteEventTypeHandler(deleteDialogTypeId); - }}> -

- , ul:

    }}> -
      -
    • Members assigned to this event type will also have their event types deleted.
    • -
    • - Anyone who they've shared their link with will no longer be able to book using it. -
    • -
    - -

    - -
-
- ); -}; - export const InfiniteEventTypeList = ({ group, readOnly, @@ -1339,66 +775,6 @@ export const InfiniteEventTypeList = ({ ); }; -const EventTypeListHeading = ({ - profile, - membershipCount, - teamId, - bookerUrl, -}: EventTypeListHeadingProps): JSX.Element => { - const { t } = useLocale(); - const router = useRouter(); - - const publishTeamMutation = trpc.viewer.teams.publish.useMutation({ - onSuccess(data) { - router.push(data.url); - }, - onError: (error) => { - showToast(error.message, "error"); - }, - }); - - return ( -
- -
- - {profile.name || ""} - - {membershipCount && teamId && ( - - - - - {membershipCount} - - - - )} - {profile.slug && ( - - {`${bookerUrl.replace("https://", "").replace("http://", "")}/${profile.slug}`} - - )} -
- {!profile.slug && !!teamId && ( - - )} -
- ); -}; - const CreateFirstEventTypeView = ({ slug }: { slug: string }) => { const { t } = useLocale(); @@ -1420,7 +796,6 @@ const CreateFirstEventTypeView = ({ slug }: { slug: string }) => { const CTA = ({ profileOptions, isOrganization, - isInfiniteScrollEnabled, }: { profileOptions: { teamId: number | null | undefined; @@ -1430,7 +805,6 @@ const CTA = ({ slug: string | null; }[]; isOrganization: boolean; - isInfiniteScrollEnabled: boolean; }) => { const { t } = useLocale(); @@ -1441,24 +815,11 @@ const CTA = ({ data-testid="new-event-type" subtitle={t("create_event_on").toUpperCase()} options={profileOptions} - createDialog={() => ( - - )} + createDialog={() => } /> ); }; -const Actions = (props: { showDivider: boolean }) => { - return ( -
- -
- ); -}; - const EmptyEventTypeList = ({ group }: { group: EventTypeGroup | InfiniteEventTypeGroup }) => { const { t } = useLocale(); return ( @@ -1478,99 +839,6 @@ const EmptyEventTypeList = ({ group }: { group: EventTypeGroup | InfiniteEventTy ); }; -const Main = ({ - status, - errorMessage, - data: rawData, - filters, -}: { - status: string; - data: GetByViewerResponse; - errorMessage?: string; - filters: ReturnType; -}) => { - const isMobile = useMediaQuery("(max-width: 768px)"); - const searchParams = useCompatSearchParams(); - - if (status === "error") { - return ; - } - - if (!rawData || status === "pending") { - return ; - } - - const isFilteredByOnlyOneItem = - (filters?.teamIds?.length === 1 || filters?.userIds?.length === 1) && - rawData.eventTypeGroups.length === 1; - - const data = denormalizePayload(rawData); - return ( - <> - {data.eventTypeGroups.length > 1 || isFilteredByOnlyOneItem ? ( - <> - {isMobile ? ( - - ) : ( - data.eventTypeGroups.map((group, index: number) => { - const eventsLockedByOrg = group.profile.eventTypesLockedByOrg; - const userHasManagedOrHiddenEventTypes = group.eventTypes.find( - (event) => event.metadata?.managedEventConfig || event.hidden - ); - if (eventsLockedByOrg && !userHasManagedOrHiddenEventTypes) return null; - return ( -
- {/* If the group is readonly and empty don't leave a floating header when the user cant see the create box due - to it being readonly for that user */} - {group.eventTypes.length === 0 && group.metadata.readOnly ? null : ( - - )} - - {group.eventTypes.length ? ( - - ) : group.teamId && !group.metadata.readOnly ? ( - - ) : !group.metadata.readOnly ? ( - - ) : null} -
- ); - }) - )} - - ) : ( - data.eventTypeGroups.length === 1 && ( - - ) - )} - {data.eventTypeGroups.length === 0 && } - - {searchParams?.get("dialog") === "duplicate" && } - - ); -}; - const InfiniteScrollMain = ({ status, errorMessage, @@ -1607,20 +875,20 @@ const InfiniteScrollMain = ({ {eventTypeGroups.length >= 1 && ( <> - + )} {eventTypeGroups.length === 0 && } - {searchParams?.get("dialog") === "duplicate" && } + {searchParams?.get("dialog") === "duplicate" && } ); }; -const EventTypesPage: React.FC<{ isInfiniteScrollEnabled?: boolean }> & { +const EventTypesPage: React.FC & { PageWrapper?: AppProps["Component"]["PageWrapper"]; getLayout?: AppProps["Component"]["getLayout"]; -} = ({ isInfiniteScrollEnabled = false }) => { +} = () => { const { t } = useLocale(); const searchParams = useCompatSearchParams(); const { open } = useIntercom(); @@ -1632,13 +900,6 @@ const EventTypesPage: React.FC<{ isInfiniteScrollEnabled?: boolean }> & { const filters = getTeamsFiltersFromQuery(routerQuery); const router = useRouter(); - const { data, status, error } = trpc.viewer.eventTypes.getByViewer.useQuery(filters && { filters }, { - refetchOnWindowFocus: false, - gcTime: 1 * 60 * 60 * 1000, - staleTime: 1 * 60 * 60 * 1000, - enabled: !isInfiniteScrollEnabled, - }); - // TODO: Maybe useSuspenseQuery to focus on success case only? Remember that it would crash the page when there is an error in query. Also, it won't support skeleton const { data: getUserEventGroupsData, @@ -1648,7 +909,6 @@ const EventTypesPage: React.FC<{ isInfiniteScrollEnabled?: boolean }> & { refetchOnWindowFocus: false, gcTime: 1 * 60 * 60 * 1000, staleTime: 1 * 60 * 60 * 1000, - enabled: isInfiniteScrollEnabled, }); useEffect(() => { @@ -1672,31 +932,18 @@ const EventTypesPage: React.FC<{ isInfiniteScrollEnabled?: boolean }> & { }, [orgBranding, user]); const profileOptions = - (data?.profiles - ? data?.profiles - .filter((profile) => !profile.readOnly) - .filter((profile) => !profile.eventTypesLockedByOrg) - .map((profile) => { - return { - teamId: profile.teamId, - label: profile.name || profile.slug, - image: profile.image, - membershipRole: profile.membershipRole, - slug: profile.slug, - }; - }) - : getUserEventGroupsData?.profiles - ?.filter((profile) => !profile.readOnly) - ?.filter((profile) => !profile.eventTypesLockedByOrg) - ?.map((profile) => { - return { - teamId: profile.teamId, - label: profile.name || profile.slug, - image: profile.image, - membershipRole: profile.membershipRole, - slug: profile.slug, - }; - })) ?? []; + getUserEventGroupsData?.profiles + ?.filter((profile) => !profile.readOnly) + ?.filter((profile) => !profile.eventTypesLockedByOrg) + ?.map((profile) => { + return { + teamId: profile.teamId, + label: profile.name || profile.slug, + image: profile.image, + membershipRole: profile.membershipRole, + slug: profile.slug, + }; + }) ?? []; return ( & { heading={t("event_types_page_title")} hideHeadingOnMobile subtitle={t("event_types_page_subtitle")} - beforeCTAactions={ - isInfiniteScrollEnabled ? undefined : 0} /> - } - CTA={ - - }> + CTA={}> - {isInfiniteScrollEnabled ? ( - - ) : ( -
- )} + + ); }; export default EventTypesPage; - -function normalizeEventType(eventType: DeNormalizedEventType): EventType { - return { - ...eventType, - userIds: eventType.users.map((user) => user.id), - }; -} - -function denormalizePayload(data: NonNullable) { - return { - ...data, - eventTypeGroups: data.eventTypeGroups.map((eventTypeGroup) => { - return { - ...eventTypeGroup, - eventTypes: eventTypeGroup.eventTypes.map((eventType) => { - const { userIds, ...rest } = eventType; - return { - ...rest, - users: userIds.map((userId) => { - const user = data.allUsersAcrossAllEventTypes.get(userId); - if (!user) { - throw new Error(`User with id ${userId} not found in allUsersAcrossAllEventTypes`); - } - return user; - }), - }; - }), - }; - }), - }; -} diff --git a/apps/web/modules/settings/teams/[id]/event-types-view.tsx b/apps/web/modules/settings/teams/[id]/event-types-view.tsx index 5478f25c91..1cbac636ce 100644 --- a/apps/web/modules/settings/teams/[id]/event-types-view.tsx +++ b/apps/web/modules/settings/teams/[id]/event-types-view.tsx @@ -42,7 +42,6 @@ export const CreateTeamEventType = () => { SubmitButton={SubmitButton} onSuccessMutation={onSuccessMutation} onErrorMutation={onErrorMutation} - isInfiniteScrollEnabled={false} /> ); }; diff --git a/apps/web/playwright/manage-booking-questions.e2e.ts b/apps/web/playwright/manage-booking-questions.e2e.ts index e03e79c8ba..ce2d3bd22e 100644 --- a/apps/web/playwright/manage-booking-questions.e2e.ts +++ b/apps/web/playwright/manage-booking-questions.e2e.ts @@ -249,6 +249,7 @@ test.describe("Manage Booking Questions", () => { }, select: { id: true, + name: true, }, }); @@ -256,7 +257,10 @@ test.describe("Manage Booking Questions", () => { const webhookReceiver = await addWebhook(undefined, teamId); await test.step("Go to First Team Event", async () => { - const $eventTypes = page.locator("[data-testid=event-types]").nth(1).locator("li a"); + await page.getByTestId(`horizontal-tab-${team?.name}`).click(); + await page.waitForLoadState("networkidle"); + + const $eventTypes = page.locator("[data-testid=event-types]").locator("li a"); const firstEventTypeElement = $eventTypes.first(); await firstEventTypeElement.click(); diff --git a/apps/web/playwright/managed-event-types.e2e.ts b/apps/web/playwright/managed-event-types.e2e.ts index d4f8b66bf4..13d0c6b485 100644 --- a/apps/web/playwright/managed-event-types.e2e.ts +++ b/apps/web/playwright/managed-event-types.e2e.ts @@ -38,7 +38,7 @@ async function setupManagedEvent({ const memberUser = users.get().find((u) => u.name === teamMateName)!; const { team } = await adminUser.getFirstTeamMembership(); const managedEvent = await adminUser.getFirstTeamEvent(team.id, SchedulingType.MANAGED); - return { adminUser, memberUser, managedEvent, teamMateName, teamEventTitle }; + return { adminUser, memberUser, managedEvent, teamMateName, teamEventTitle, teamId: team.id }; } /** Short hand to get elements by translation key */ @@ -129,7 +129,9 @@ test.describe("Managed Event Types", () => { test("Provides discrete field lock/unlock state for admin", async ({ page, users }) => { const { adminUser, teamEventTitle } = await setupManagedEvent({ users }); await adminUser.apiLogin(); - await page.goto("/event-types"); + const teamMembership = await adminUser.getFirstTeamMembership(); + + await page.goto(`/event-types?teamId=${teamMembership.team.id}`); await page.getByTestId("event-types").locator(`a[title="${teamEventTitle}"]`).click(); await page.waitForURL("event-types/**"); @@ -169,7 +171,7 @@ test.describe("Managed Event Types", () => { users, browser, }) => { - const { adminUser, memberUser, teamEventTitle } = await setupManagedEvent({ + const { adminUser, memberUser, teamEventTitle, teamId } = await setupManagedEvent({ users, unlockedFields: { title: true, @@ -185,7 +187,7 @@ test.describe("Managed Event Types", () => { // We edit the managed event as original owner const [adminContext, adminPage] = await adminUser.apiLoginOnNewBrowser(browser); - await adminPage.goto("/event-types"); + await adminPage.goto(`/event-types?teamId=${teamId}`); await adminPage.getByTestId("event-types").locator(`a[title="${teamEventTitle}"]`).click(); await adminPage.waitForURL("event-types/**"); await adminPage.locator('input[name="length"]').fill(`45`); diff --git a/apps/web/playwright/organization/across-org/across-org.e2e.ts b/apps/web/playwright/organization/across-org/across-org.e2e.ts index ae091b288a..b344d9cb40 100644 --- a/apps/web/playwright/organization/across-org/across-org.e2e.ts +++ b/apps/web/playwright/organization/across-org/across-org.e2e.ts @@ -47,19 +47,28 @@ test.describe("user1NotMemberOfOrg1 is part of team1MemberOfOrg1", () => { await user1NotMemberOfOrg1.apiLogin(); await page.goto("/event-types"); - const userEventLinksSelector = `[data-testid=slug-${user1NotMemberOfOrg1.username}] [data-testid="preview-link-button"]`; - await page.waitForSelector(userEventLinksSelector); + + await page.waitForSelector(`[data-testid="event-types"] [data-testid="preview-link-button"]`, { + timeout: 5000, + }); + const userEventLinksLocators = await page + .locator(`[data-testid="event-types"] [data-testid="preview-link-button"]`) + .all(); // Get all the event links - const userEventLinksLocators = await page.locator(userEventLinksSelector).all(); expect(userEventLinksLocators.length).toBeGreaterThan(0); for (const userEventLinkLocator of userEventLinksLocators) { const href = await userEventLinkLocator.getAttribute("href"); expect(href).toContain(WEBAPP_URL); } + await page.goto(`/event-types?teamId=${team1MemberOfOrg1.id}`); + + await page.waitForSelector(`[data-testid="event-types"] [data-testid="preview-link-button"]`, { + timeout: 5000, + }); const teamEventLinksLocators = await page - .locator(`[data-testid=slug-${team1MemberOfOrg1.slug}] [data-testid="preview-link-button"]`) + .locator(`[data-testid="event-types"] [data-testid="preview-link-button"]`) .all(); expect(teamEventLinksLocators.length).toBeGreaterThan(0); diff --git a/apps/web/playwright/teams.e2e.ts b/apps/web/playwright/teams.e2e.ts index 32ac1830c6..5daa229847 100644 --- a/apps/web/playwright/teams.e2e.ts +++ b/apps/web/playwright/teams.e2e.ts @@ -263,7 +263,7 @@ testBothFutureAndLegacyRoutes.describe("Teams - NonOrg", (routeVariant) => { id: teamEventId, } = await owner.getFirstTeamEvent(team.id); - await page.goto("/event-types"); + await page.goto(`/event-types?teamId=${team.id}`); await page.getByTestId(`event-type-options-${teamEventId}`).first().click(); await page.getByTestId("embed").click(); diff --git a/packages/features/ee/teams/components/TeamEventTypeForm.tsx b/packages/features/ee/teams/components/TeamEventTypeForm.tsx index c08c516246..f80bf1d8a0 100644 --- a/packages/features/ee/teams/components/TeamEventTypeForm.tsx +++ b/packages/features/ee/teams/components/TeamEventTypeForm.tsx @@ -17,7 +17,6 @@ type props = { SubmitButton: (isPending: boolean) => ReactNode; onSuccessMutation: (eventType: EventType) => void; onErrorMutation: (message: string) => void; - isInfiniteScrollEnabled: boolean; }; export const TeamEventTypeForm = ({ isTeamAdminOrOwner, @@ -25,7 +24,6 @@ export const TeamEventTypeForm = ({ SubmitButton, onSuccessMutation, onErrorMutation, - isInfiniteScrollEnabled, }: props) => { const { t } = useLocale(); const orgBranding = useOrgBranding(); @@ -35,11 +33,7 @@ export const TeamEventTypeForm = ({ ); const urlPrefix = orgBranding?.fullDomain ?? process.env.NEXT_PUBLIC_WEBSITE_URL; - const { form, createMutation, isManagedEventType } = useCreateEventType( - onSuccessMutation, - onErrorMutation, - isInfiniteScrollEnabled - ); + const { form, createMutation, isManagedEventType } = useCreateEventType(onSuccessMutation, onErrorMutation); const { register, setValue, formState } = form; return ( diff --git a/packages/features/eventtypes/components/CreateEventTypeDialog.tsx b/packages/features/eventtypes/components/CreateEventTypeDialog.tsx index 86dc42502d..f899f6dad2 100644 --- a/packages/features/eventtypes/components/CreateEventTypeDialog.tsx +++ b/packages/features/eventtypes/components/CreateEventTypeDialog.tsx @@ -64,7 +64,6 @@ const querySchema = z.object({ export default function CreateEventTypeDialog({ profileOptions, - isInfiniteScrollEnabled, }: { profileOptions: { teamId: number | null | undefined; @@ -72,7 +71,6 @@ export default function CreateEventTypeDialog({ image: string | undefined; membershipRole: MembershipRole | null | undefined; }[]; - isInfiniteScrollEnabled: boolean; }) { const { t } = useLocale(); const router = useRouter(); @@ -115,11 +113,7 @@ export default function CreateEventTypeDialog({ ); }; - const { form, createMutation, isManagedEventType } = useCreateEventType( - onSuccessMutation, - onErrorMutation, - isInfiniteScrollEnabled - ); + const { form, createMutation, isManagedEventType } = useCreateEventType(onSuccessMutation, onErrorMutation); const { register } = form; const urlPrefix = orgBranding?.fullDomain ?? process.env.NEXT_PUBLIC_WEBSITE_URL; @@ -149,7 +143,6 @@ export default function CreateEventTypeDialog({ SubmitButton={SubmitButton} onSuccessMutation={onSuccessMutation} onErrorMutation={onErrorMutation} - isInfiniteScrollEnabled={isInfiniteScrollEnabled} /> ) : (
{ +const DuplicateDialog = () => { const utils = trpc.useUtils(); const searchParams = useCompatSearchParams(); @@ -74,15 +74,11 @@ const DuplicateDialog = ({ isInfiniteScrollEnabled }: { isInfiniteScrollEnabled? onSuccess: async ({ eventType }) => { await router.replace(`/event-types/${eventType.id}`); - if (isInfiniteScrollEnabled) { - await utils.viewer.eventTypes.getUserEventGroups.invalidate(); - await utils.viewer.eventTypes.getEventTypesFromGroup.invalidate({ - limit: 10, - group: { teamId: eventType?.teamId, parentId: eventType?.parentId }, - }); - } else { - await utils.viewer.eventTypes.getByViewer.invalidate(); - } + await utils.viewer.eventTypes.getUserEventGroups.invalidate(); + await utils.viewer.eventTypes.getEventTypesFromGroup.invalidate({ + limit: 10, + group: { teamId: eventType?.teamId, parentId: eventType?.parentId }, + }); showToast( t("event_type_created_successfully", { diff --git a/packages/lib/constants.ts b/packages/lib/constants.ts index ba0eb02454..b4a2ed1013 100644 --- a/packages/lib/constants.ts +++ b/packages/lib/constants.ts @@ -115,10 +115,6 @@ export const ORGANIZATION_SELF_SERVE_PRICE = process.env.NEXT_PUBLIC_ORGANIZATIO export const ORGANIZATION_MIN_SEATS = 30; -export const ENABLE_INFINITE_EVENT_TYPES_FOR_ORG = JSON.parse( - `[${process.env.ENABLE_INFINITE_EVENT_TYPES_FOR_ORG || ""}]` -) as string[]; - // Needed for emails in E2E export const IS_MAILHOG_ENABLED = process.env.E2E_TEST_MAILHOG_ENABLED === "1"; export const CALCOM_VERSION = process.env.NEXT_PUBLIC_CALCOM_VERSION as string; diff --git a/packages/lib/hooks/useCreateEventType.ts b/packages/lib/hooks/useCreateEventType.ts index b78244535f..4b8cbdff07 100644 --- a/packages/lib/hooks/useCreateEventType.ts +++ b/packages/lib/hooks/useCreateEventType.ts @@ -13,8 +13,7 @@ import { trpc } from "@calcom/trpc/react"; export const useCreateEventType = ( onSuccessMutation: (eventType: EventType) => void, - onErrorMutation: (message: string) => void, - isInfiniteScrollEnabled: boolean + onErrorMutation: (message: string) => void ) => { const utils = trpc.useUtils(); const { t } = useLocale(); @@ -40,14 +39,10 @@ export const useCreateEventType = ( onSuccess: async ({ eventType }) => { onSuccessMutation(eventType); - if (isInfiniteScrollEnabled) { - await utils.viewer.eventTypes.getEventTypesFromGroup.fetchInfinite({ - group: { teamId: eventType.teamId, parentId: eventType.parentId }, - limit: 10, - }); - } else { - await utils.viewer.eventTypes.getByViewer.invalidate(); - } + await utils.viewer.eventTypes.getEventTypesFromGroup.fetchInfinite({ + group: { teamId: eventType.teamId, parentId: eventType.parentId }, + limit: 10, + }); form.reset(); }, diff --git a/turbo.json b/turbo.json index 8fcc1c12ce..b740e06c6b 100644 --- a/turbo.json +++ b/turbo.json @@ -315,7 +315,6 @@ "EMAIL_SERVER_PORT", "EMAIL_SERVER_USER", "EMAIL_SERVER", - "ENABLE_INFINITE_EVENT_TYPES_FOR_ORG", "EXCHANGE_DEFAULT_EWS_URL", "FORMBRICKS_FEEDBACK_SURVEY_ID", "AVATARAPI_USERNAME",