From 0ea673e5073f87f15fe105552e1aeba3f12bb1cf Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 21 Aug 2025 08:34:45 -0400 Subject: [PATCH] feat: update global feature flag to team feature flag for team booking page cache (#23154) * add team id fetcher based on slugs * export getOrgContext * update RSC * refactor * further refactor --- .../team/[slug]/[type]/page.tsx | 28 ++++++++++++++----- .../team/[slug]/[type]/pageWithCachedData.tsx | 6 ++-- .../team/[slug]/[type]/queries.ts | 12 ++++++++ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/page.tsx b/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/page.tsx index 25070801c2..6d9885d51f 100644 --- a/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/page.tsx +++ b/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/page.tsx @@ -1,6 +1,6 @@ import { CustomI18nProvider } from "app/CustomI18nProvider"; import { withAppDirSsr } from "app/WithAppDirSsr"; -import type { PageProps, SearchParams } from "app/_types"; +import type { PageProps, Params, SearchParams } from "app/_types"; import { generateMeetingMetadata } from "app/_utils"; import { cookies, headers } from "next/headers"; @@ -15,20 +15,34 @@ import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps"; import LegacyPage from "~/team/type-view"; import type { PageProps as LegacyPageProps } from "~/team/type-view"; -import CachedTeamBooker, { generateMetadata as generateCachedMetadata } from "./pageWithCachedData"; +import CachedTeamBooker, { + generateMetadata as generateCachedMetadata, + getOrgContext, +} from "./pageWithCachedData"; +import { getTeamId } from "./queries"; + +async function isCachedTeamBookingEnabled(params: Params, searchParams: SearchParams): Promise { + if (searchParams.experimentalTeamBookingPageCache !== "true") return false; + + const { teamSlug, currentOrgDomain, isValidOrgDomain } = await getOrgContext(params); + const orgSlug = isValidOrgDomain ? currentOrgDomain : null; + const teamId = await getTeamId(teamSlug, orgSlug); + + if (!teamId) return false; -async function isCachedTeamBookingEnabled(searchParams: SearchParams): Promise { const featuresRepository = new FeaturesRepository(prisma); - const isGloballyEnabled = await featuresRepository.checkIfFeatureIsEnabledGlobally( + const isTeamFeatureEnabled = await featuresRepository.checkIfTeamHasFeature( + teamId, "team-booking-page-cache" ); - return isGloballyEnabled && searchParams.experimentalTeamBookingPageCache === "true"; + return isTeamFeatureEnabled; } export const generateMetadata = async ({ params, searchParams }: PageProps) => { - if (await isCachedTeamBookingEnabled(await searchParams)) { + if (await isCachedTeamBookingEnabled(await params, await searchParams)) { return await generateCachedMetadata({ params, searchParams }); } + const legacyCtx = buildLegacyCtx(await headers(), await cookies(), await params, await searchParams); const props = await getData(legacyCtx); const { booking, isSEOIndexable, eventData, isBrandingHidden } = props; @@ -68,7 +82,7 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => { const getData = withAppDirSsr(getServerSideProps); const ServerPage = async ({ params, searchParams }: PageProps) => { - if (await isCachedTeamBookingEnabled(await searchParams)) { + if (await isCachedTeamBookingEnabled(await params, await searchParams)) { return await CachedTeamBooker({ params, searchParams }); } diff --git a/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/pageWithCachedData.tsx b/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/pageWithCachedData.tsx index 0a15425b05..1594ec72f7 100644 --- a/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/pageWithCachedData.tsx +++ b/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/pageWithCachedData.tsx @@ -41,7 +41,7 @@ const _getTeamMetadataForBooking = (teamData: NonNullable, eventTypeId }; }; -async function _getOrgContext(params: Params) { +export async function getOrgContext(params: Params) { const result = paramsSchema.safeParse({ slug: params?.slug, type: params?.type, @@ -74,7 +74,7 @@ const _getMultipleDurationValue = ( }; export const generateMetadata = async ({ params, searchParams }: PageProps) => { - const { currentOrgDomain, isValidOrgDomain, teamSlug, meetingSlug } = await _getOrgContext(await params); + const { currentOrgDomain, isValidOrgDomain, teamSlug, meetingSlug } = await getOrgContext(await params); const teamData = await getCachedTeamData(teamSlug, currentOrgDomain); if (!teamData) return {}; // should never happen @@ -123,7 +123,7 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => { }; const CachedTeamBooker = async ({ params, searchParams }: PageProps) => { - const { currentOrgDomain, isValidOrgDomain, teamSlug, meetingSlug } = await _getOrgContext(await params); + const { currentOrgDomain, isValidOrgDomain, teamSlug, meetingSlug } = await getOrgContext(await params); const legacyCtx = buildLegacyCtx(await headers(), await cookies(), await params, await searchParams); // Handle org redirects diff --git a/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/queries.ts b/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/queries.ts index 8f4df870a8..825e2c02d3 100644 --- a/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/queries.ts +++ b/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/queries.ts @@ -13,6 +13,7 @@ import { getTeamEventType } from "@calcom/features/eventtypes/lib/getTeamEventTy import { FeaturesRepository } from "@calcom/features/flags/features.repository"; import { NEXTJS_CACHE_TTL } from "@calcom/lib/constants"; import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; +import { TeamRepository } from "@calcom/lib/server/repository/team"; import { UserRepository } from "@calcom/lib/server/repository/user"; import { prisma } from "@calcom/prisma"; import type { SchedulingType } from "@calcom/prisma/enums"; @@ -158,3 +159,14 @@ export async function getCRMData( crmRecordId, }; } + +export async function getTeamId(teamSlug: string, orgSlug: string | null): Promise { + const teamRepo = new TeamRepository(prisma); + const team = await teamRepo.findFirstBySlugAndParentSlug({ + slug: teamSlug, + parentSlug: orgSlug, + select: { id: true }, + }); + + return team?.id ?? null; +}