chore: migrate /team booking pages (#18656)
* wip * remove pages/team/[slug] * fix tests * fix tests 2
This commit is contained in:
1 parent
66b3e735d8
commit
5ccf5e6be8
15 files changed
+173
-125
No files matched your search
+21
-2
@@ -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">;
|
||||
@@ -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">;
|
||||
@@ -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">;
|
||||
@@ -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">;
|
||||
@@ -186,6 +186,7 @@ export const config = {
|
||||
"/availability/:path*",
|
||||
"/booking/:path*",
|
||||
"/routing-forms/:path*",
|
||||
"/team/:path*",
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -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 +0,0 @@
|
||||
export { getServerSideProps, default } from "@pages/team/[slug]";
|
||||
File renamed without changes.
@@ -1 +0,0 @@
|
||||
export { getServerSideProps, default } from "@pages/team/[slug]/[type]";
|
||||
+2
-3
@@ -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(",")
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user