chore: migrate /team booking pages (#18656)

* wip

* remove pages/team/[slug]

* fix tests

* fix tests 2
This commit is contained in:
Benny Joo authored and GitHub committed 2025-01-14 23:48:41 +00:00
1 parent 66b3e735d8
commit 5ccf5e6be8
15 files changed
+173 -125

No files matched your search

+21 -2
View File
@@ -3,8 +3,8 @@ import i18next from "i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { headers } from "next/headers";
import type { AppImageProps } from "@calcom/lib/OgImages";
import { constructGenericImage, constructAppImage } from "@calcom/lib/OgImages";
import type { AppImageProps, MeetingImageProps } from "@calcom/lib/OgImages";
import { constructGenericImage, constructAppImage, constructMeetingImage } from "@calcom/lib/OgImages";
import { IS_CALCOM, WEBAPP_URL, APP_NAME, SEO_IMG_OGIMG, CAL_URL } from "@calcom/lib/constants";
import { buildCanonical } from "@calcom/lib/next-seo.config";
import { truncateOnWord } from "@calcom/lib/text";
@@ -125,3 +125,22 @@ export const generateAppMetadata = async (
},
};
};
export const generateMeetingMetadata = async (
meeting: MeetingImageProps,
getTitle: (t: TFunction<string, undefined>) => string,
getDescription: (t: TFunction<string, undefined>) => string,
hideBranding?: boolean,
origin?: string
) => {
const metadata = await _generateMetadataWithoutImage(getTitle, getDescription, hideBranding, origin);
const image = SEO_IMG_OGIMG + constructMeetingImage(meeting);
return {
...metadata,
openGraph: {
...metadata.openGraph,
images: [image],
},
};
};
@@ -1,11 +0,0 @@
import withEmbedSsrAppDir from "app/WithEmbedSSR";
import { WithLayout } from "app/layoutHOC";
import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import type { PageProps } from "~/team/type-view";
import LegacyPage from "~/team/type-view";
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
export default WithLayout({ getLayout: null, getData: getEmbedData, Page: LegacyPage })<"P">;
@@ -1,44 +0,0 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { EventRepository } from "@calcom/lib/server/repository/event";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import LegacyPage, { type PageProps as LegacyPageProps } from "~/team/type-view";
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
const props = await getData(legacyCtx);
const { user: username, slug: eventSlug, booking } = props;
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(legacyCtx.req, legacyCtx.params?.orgSlug);
const event = await EventRepository.getPublicEvent({
username,
eventSlug,
isTeamEvent: true,
org: isValidOrgDomain ? currentOrgDomain : null,
fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true",
});
const profileName = event?.profile?.name ?? "";
const title = event?.title ?? "";
return await _generateMetadata(
(t) => `${booking?.uid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
(t) => `${booking?.uid ? t("reschedule") : ""} ${title}`
);
};
const getData = withAppDirSsr<LegacyPageProps>(getServerSideProps);
export default WithLayout({
Page: LegacyPage,
getData,
getLayout: null,
isBookingPage: true,
})<"P">;
@@ -1,11 +0,0 @@
import withEmbedSsrAppDir from "app/WithEmbedSSR";
import { WithLayout } from "app/layoutHOC";
import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
import type { PageProps } from "~/team/team-view";
import LegacyPage from "~/team/team-view";
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
export default WithLayout({ getLayout: null, getData: getEmbedData, Page: LegacyPage })<"P">;
-28
View File
@@ -1,28 +0,0 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps as _PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
import type { PageProps } from "~/team/team-view";
import LegacyPage from "~/team/team-view";
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams));
return await _generateMetadata(
(t) => props.team.name || t("nameless_team"),
(t) => props.team.name || t("nameless_team")
);
};
const getData = withAppDirSsr<PageProps>(getServerSideProps);
export default WithLayout({
Page: LegacyPage,
getData,
getLayout: null,
})<"P">;
+58
View File
@@ -0,0 +1,58 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps } from "app/_types";
import { generateMeetingMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import LegacyPage from "~/team/type-view";
import type { PageProps as LegacyPageProps } from "~/team/type-view";
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
const props = await getData(legacyCtx);
const { booking, isSEOIndexable, eventData, isBrandingHidden } = props;
const profileName = eventData?.profile?.name ?? "";
const profileImage = eventData?.profile.image;
const title = eventData?.title ?? "";
const meeting = {
title,
profile: { name: profileName, image: profileImage },
users: [
...(eventData?.users || []).map((user) => ({
name: `${user.name}`,
username: `${user.username}`,
})),
],
};
const metadata = await generateMeetingMetadata(
meeting,
(t) => `${booking?.uid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
(t) => `${booking?.uid ? t("reschedule") : ""} ${title}`,
isBrandingHidden,
getOrgFullOrigin(eventData.entity.orgSlug ?? null)
);
return {
...metadata,
robots: {
follow: !(eventData?.hidden || !isSEOIndexable),
index: !(eventData?.hidden || !isSEOIndexable),
},
};
};
const getData = withAppDirSsr<LegacyPageProps>(getServerSideProps);
export default WithLayout({
Page: LegacyPage,
getData,
getLayout: null,
isBookingPage: true,
})<"P">;
+52
View File
@@ -0,0 +1,52 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps as _PageProps } from "app/_types";
import { generateMeetingMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
import { getOrgOrTeamAvatar } from "@calcom/lib/defaultAvatarImage";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
import type { PageProps } from "~/team/team-view";
import LegacyPage from "~/team/team-view";
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
const { team, markdownStrippedBio, isSEOIndexable, currentOrgDomain } = await getData(
buildLegacyCtx(headers(), cookies(), params, searchParams)
);
const meeting = {
title: markdownStrippedBio ?? "",
profile: {
name: `${team.name}`,
image: getOrgOrTeamAvatar(team),
},
};
const metadata = await generateMeetingMetadata(
meeting,
(t) => team.name || t("nameless_team"),
(t) => team.name || t("nameless_team"),
false,
getOrgFullOrigin(currentOrgDomain ?? null)
);
return {
...metadata,
robots: {
follow: isSEOIndexable,
index: isSEOIndexable,
},
};
};
const getData = withAppDirSsr<PageProps>(getServerSideProps);
export default WithLayout({
Page: LegacyPage,
getData,
getLayout: null,
isBookingPage: true,
})<"P">;
+1
View File
@@ -186,6 +186,7 @@ export const config = {
"/availability/:path*",
"/booking/:path*",
"/routing-forms/:path*",
"/team/:path*",
],
};
+26 -22
View File
@@ -4,6 +4,7 @@ import type { EmbedProps } from "app/WithEmbedSSR";
import { useSearchParams } from "next/navigation";
import { Booker } from "@calcom/atoms/monorepo";
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
import type { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
@@ -31,32 +32,35 @@ function Type({
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
isEmbed,
}: PageProps) {
const searchParams = useSearchParams();
return (
<Booker
username={user}
eventSlug={slug}
bookingData={booking}
isInstantMeeting={isInstantMeeting}
hideBranding={isBrandingHidden}
isTeamEvent
entity={{ ...eventData.entity, eventTypeId: eventData?.eventTypeId }}
durationConfig={eventData.metadata?.multipleDuration}
/* TODO: Currently unused, evaluate it is needed-
* Possible alternative approach is to have onDurationChange.
*/
duration={getMultipleDurationValue(
eventData.metadata?.multipleDuration,
searchParams?.get("duration"),
eventData.length
)}
orgBannerUrl={orgBannerUrl}
teamMemberEmail={teamMemberEmail}
crmOwnerRecordType={crmOwnerRecordType}
crmAppSlug={crmAppSlug}
/>
<main className={getBookerWrapperClasses({ isEmbed: !!isEmbed })}>
<Booker
username={user}
eventSlug={slug}
bookingData={booking}
isInstantMeeting={isInstantMeeting}
hideBranding={isBrandingHidden}
isTeamEvent
entity={{ ...eventData.entity, eventTypeId: eventData?.eventTypeId }}
durationConfig={eventData.metadata?.multipleDuration}
/* TODO: Currently unused, evaluate it is needed-
* Possible alternative approach is to have onDurationChange.
*/
duration={getMultipleDurationValue(
eventData.metadata?.multipleDuration,
searchParams?.get("duration"),
eventData.length
)}
orgBannerUrl={orgBannerUrl}
teamMemberEmail={teamMemberEmail}
crmOwnerRecordType={crmOwnerRecordType}
crmAppSlug={crmAppSlug}
/>
</main>
);
}
-1
View File
@@ -1 +0,0 @@
export { getServerSideProps, default } from "@pages/team/[slug]";
File renamed without changes.
@@ -1 +0,0 @@
export { getServerSideProps, default } from "@pages/team/[slug]/[type]";
@@ -1,4 +1,3 @@
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
import PageWrapper from "@components/PageWrapper";
@@ -11,7 +10,7 @@ const Page = (props: PageProps) => {
const { profile, users, hidden, title } = eventData;
return (
<main className={getBookerWrapperClasses({ isEmbed: !!props.isEmbed })}>
<>
<BookerSeo
username={user}
eventSlug={slug}
@@ -33,7 +32,7 @@ const Page = (props: PageProps) => {
isSEOIndexable={isSEOIndexable}
/>
<TypePage {...props} />
</main>
</>
);
};
Page.isBookingPage = true;
@@ -1 +1 @@
export { getServerSideProps, default } from "@pages/team/[slug]";
export { getServerSideProps, default } from "@pages/org/[orgSlug]/index";
@@ -68,7 +68,18 @@ async function verifyRobotsMetaTag({ page, orgSlug, urls, expectedContent }: Ver
const { url } = await goToUrlWithErrorHandling(relativeUrl);
const metaTag = await page.locator('head > meta[name="robots"]');
const metaTagValue = await metaTag.getAttribute("content");
expect(metaTagValue).toEqual(expectedContent);
expect(metaTagValue).not.toBeNull();
expect(
metaTagValue
?.split(",")
.map((s) => s.trim())
.join(",")
).toEqual(
expectedContent
.split(",")
.map((s) => s.trim())
.join(",")
);
}
});
}