chore: migrate user booking pages to App Router (#19069)
* add user pages to config.matcher in middleware * add app router user booking pages * remove HeadSeo from users-public-view component * remove pages router user booking pages * fix type check * fix embed * fix test * chore: remove BookerSEO from users-type-public-view * fix
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import type { PageProps } from "app/_types";
|
||||
import { generateMeetingMetadata } from "app/_utils";
|
||||
import { headers, cookies } from "next/headers";
|
||||
|
||||
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { getServerSideProps } from "@server/lib/[user]/[type]/getServerSideProps";
|
||||
|
||||
import type { PageProps as LegacyPageProps } from "~/users/views/users-type-public-view";
|
||||
import LegacyPage from "~/users/views/users-type-public-view";
|
||||
|
||||
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
|
||||
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
|
||||
const props = await getData(legacyCtx);
|
||||
|
||||
const { booking, isSEOIndexable = true, eventData, isBrandingHidden } = props;
|
||||
const rescheduleUid = booking?.uid;
|
||||
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) => `${rescheduleUid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
|
||||
(t) => `${rescheduleUid ? 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);
|
||||
|
||||
const ServerPage = async ({ params, searchParams }: PageProps) => {
|
||||
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
|
||||
const props = await getData(legacyCtx);
|
||||
|
||||
return <LegacyPage {...props} />;
|
||||
};
|
||||
|
||||
export default ServerPage;
|
||||
@@ -0,0 +1,52 @@
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import type { PageProps } from "app/_types";
|
||||
import { generateMeetingMetadata } from "app/_utils";
|
||||
import { headers, cookies } from "next/headers";
|
||||
|
||||
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { getServerSideProps } from "@server/lib/[user]/getServerSideProps";
|
||||
|
||||
import type { PageProps as LegacyPageProps } from "~/users/views/users-public-view";
|
||||
import LegacyPage from "~/users/views/users-public-view";
|
||||
|
||||
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
|
||||
const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams));
|
||||
|
||||
const { profile, markdownStrippedBio, isOrgSEOIndexable, entity } = props;
|
||||
const isOrg = !!profile?.organization;
|
||||
const allowSEOIndexing =
|
||||
(!isOrg && profile.allowSEOIndexing) || (isOrg && isOrgSEOIndexable && profile.allowSEOIndexing);
|
||||
|
||||
const meeting = {
|
||||
title: markdownStrippedBio,
|
||||
profile: { name: `${profile.name}`, image: profile.image },
|
||||
users: [{ username: `${profile.username}`, name: `${profile.name}` }],
|
||||
};
|
||||
const metadata = await generateMeetingMetadata(
|
||||
meeting,
|
||||
() => profile.name,
|
||||
() => markdownStrippedBio,
|
||||
false,
|
||||
getOrgFullOrigin(entity.orgSlug ?? null)
|
||||
);
|
||||
|
||||
return {
|
||||
...metadata,
|
||||
robots: {
|
||||
follow: allowSEOIndexing,
|
||||
index: allowSEOIndexing,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const getData = withAppDirSsr<LegacyPageProps>(getServerSideProps);
|
||||
const ServerPage = async ({ params, searchParams }: PageProps) => {
|
||||
const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams));
|
||||
|
||||
return <LegacyPage {...props} />;
|
||||
};
|
||||
|
||||
export default ServerPage;
|
||||
@@ -1,47 +0,0 @@
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import type { PageProps } from "app/_types";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import { headers, cookies } 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 "@server/lib/[user]/[type]/getServerSideProps";
|
||||
|
||||
import type { PageProps as LegacyPageProps } from "~/users/views/users-type-public-view";
|
||||
import LegacyPage from "~/users/views/users-type-public-view";
|
||||
|
||||
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
|
||||
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
|
||||
const props = await getData(legacyCtx);
|
||||
|
||||
const { booking, user: username, slug: eventSlug } = props;
|
||||
const rescheduleUid = booking?.uid;
|
||||
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(legacyCtx.req, legacyCtx.params?.orgSlug);
|
||||
|
||||
const event = await EventRepository.getPublicEvent({
|
||||
username,
|
||||
eventSlug,
|
||||
isTeamEvent: false,
|
||||
org: isValidOrgDomain ? currentOrgDomain : null,
|
||||
fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true",
|
||||
});
|
||||
|
||||
const profileName = event?.profile?.name ?? "";
|
||||
const title = event?.title ?? "";
|
||||
|
||||
return await _generateMetadata(
|
||||
(t) => `${rescheduleUid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
|
||||
(t) => `${rescheduleUid ? t("reschedule") : ""} ${title}`
|
||||
);
|
||||
};
|
||||
const getData = withAppDirSsr<LegacyPageProps>(getServerSideProps);
|
||||
|
||||
export default WithLayout({
|
||||
getData,
|
||||
Page: LegacyPage,
|
||||
getLayout: null,
|
||||
})<"P">;
|
||||
@@ -1,25 +0,0 @@
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import type { PageProps } from "app/_types";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import { headers, cookies } from "next/headers";
|
||||
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { getServerSideProps } from "@server/lib/[user]/getServerSideProps";
|
||||
|
||||
import type { PageProps as LegacyPageProps } from "~/users/views/users-public-view";
|
||||
import LegacyPage from "~/users/views/users-public-view";
|
||||
|
||||
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
|
||||
const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams));
|
||||
|
||||
const { profile, markdownStrippedBio } = props;
|
||||
return await _generateMetadata(
|
||||
() => profile.name,
|
||||
() => markdownStrippedBio
|
||||
);
|
||||
};
|
||||
|
||||
const getData = withAppDirSsr<LegacyPageProps>(getServerSideProps);
|
||||
export default WithLayout({ getData, Page: LegacyPage })<"P">;
|
||||
@@ -21,7 +21,7 @@ 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 { booking, isSEOIndexable = true, eventData, isBrandingHidden } = props;
|
||||
const rescheduleUid = booking?.uid;
|
||||
|
||||
const profileName = eventData?.profile?.name ?? "";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getServerSideProps as GSSUserPage } from "@pages/[user]";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
|
||||
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
@@ -6,6 +5,8 @@ import prisma from "@calcom/prisma";
|
||||
|
||||
import { getServerSideProps as GSSTeamPage } from "@lib/team/[slug]/getServerSideProps";
|
||||
|
||||
import { getServerSideProps as GSSUserPage } from "@server/lib/[user]/getServerSideProps";
|
||||
|
||||
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
|
||||
const team = await prisma.team.findFirst({
|
||||
where: {
|
||||
|
||||
@@ -183,6 +183,8 @@ export const config = {
|
||||
"/routing-forms/:path*",
|
||||
"/team/:path*",
|
||||
"/org/:path*",
|
||||
"/:user/:type/",
|
||||
"/:user/",
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { describe, it, vi } from "vitest";
|
||||
|
||||
import { getOrgFullOrigin } from "@calcom/ee/organizations/lib/orgDomains";
|
||||
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
||||
@@ -71,33 +71,6 @@ describe("UserPage Component", () => {
|
||||
});
|
||||
|
||||
render(<UserPage {...mockData.props} />);
|
||||
|
||||
const expectedDescription = mockData.props.markdownStrippedBio;
|
||||
const expectedTitle = expectedDescription;
|
||||
expect(HeadSeo).toHaveBeenCalledWith(
|
||||
{
|
||||
origin: `${mockData.props.entity.orgSlug}.cal.local`,
|
||||
title: `${mockData.props.profile.name}`,
|
||||
description: expectedDescription,
|
||||
meeting: {
|
||||
profile: {
|
||||
name: mockData.props.profile.name,
|
||||
image: mockData.props.users[0].avatarUrl,
|
||||
},
|
||||
title: expectedTitle,
|
||||
users: [
|
||||
{
|
||||
name: mockData.props.users[0].name,
|
||||
username: mockData.props.users[0].username,
|
||||
},
|
||||
],
|
||||
},
|
||||
nextSeoProps: {
|
||||
nofollow: !mockData.props.profile.allowSEOIndexing,
|
||||
noindex: !mockData.props.profile.allowSEOIndexing,
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
expect(HeadSeo).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,18 +11,17 @@ import {
|
||||
useEmbedStyles,
|
||||
useIsEmbed,
|
||||
} from "@calcom/embed-core/embed-iframe";
|
||||
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { EventTypeDescriptionLazy as EventTypeDescription } from "@calcom/features/eventtypes/components";
|
||||
import EmptyPage from "@calcom/features/eventtypes/components/EmptyPage";
|
||||
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
||||
import useTheme from "@calcom/lib/hooks/useTheme";
|
||||
import { HeadSeo, Icon, UnpublishedEntity, UserAvatar } from "@calcom/ui";
|
||||
import { Icon, UnpublishedEntity, UserAvatar } from "@calcom/ui";
|
||||
|
||||
import type { getServerSideProps } from "@server/lib/[user]/getServerSideProps";
|
||||
|
||||
export type PageProps = InferGetServerSidePropsType<typeof getServerSideProps>;
|
||||
export function UserPage(props: PageProps) {
|
||||
const { users, profile, eventTypes, markdownStrippedBio, entity, isOrgSEOIndexable } = props;
|
||||
const { users, profile, eventTypes, entity } = props;
|
||||
|
||||
const [user] = users; //To be used when we only have a single user, not dynamic group
|
||||
useTheme(profile.theme);
|
||||
@@ -60,29 +59,8 @@ export function UserPage(props: PageProps) {
|
||||
const isEventListEmpty = eventTypes.length === 0;
|
||||
const isOrg = !!user?.profile?.organization;
|
||||
|
||||
const allowSEOIndexing = isOrg
|
||||
? isOrgSEOIndexable
|
||||
? profile.allowSEOIndexing
|
||||
: false
|
||||
: profile.allowSEOIndexing;
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeadSeo
|
||||
origin={getOrgFullOrigin(entity.orgSlug ?? null)}
|
||||
title={profile.name}
|
||||
description={markdownStrippedBio}
|
||||
meeting={{
|
||||
title: markdownStrippedBio,
|
||||
profile: { name: `${profile.name}`, image: user.avatarUrl || null },
|
||||
users: [{ username: `${user.username}`, name: `${user.name}` }],
|
||||
}}
|
||||
nextSeoProps={{
|
||||
noindex: !allowSEOIndexing,
|
||||
nofollow: !allowSEOIndexing,
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className={classNames(shouldAlignCentrally ? "mx-auto" : "", isEmbed ? "max-w-3xl" : "")}>
|
||||
<main
|
||||
className={classNames(
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useSearchParams } from "next/navigation";
|
||||
|
||||
import { Booker } from "@calcom/atoms/monorepo";
|
||||
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
|
||||
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
|
||||
|
||||
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
||||
@@ -23,40 +22,11 @@ export const getMultipleDurationValue = (
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
function Type({
|
||||
slug,
|
||||
user,
|
||||
isEmbed,
|
||||
booking,
|
||||
isBrandingHidden,
|
||||
isSEOIndexable,
|
||||
rescheduleUid,
|
||||
eventData,
|
||||
orgBannerUrl,
|
||||
}: PageProps) {
|
||||
function Type({ slug, user, isEmbed, booking, isBrandingHidden, eventData, orgBannerUrl }: PageProps) {
|
||||
const searchParams = useSearchParams();
|
||||
const { profile, users, hidden, title } = eventData;
|
||||
|
||||
return (
|
||||
<main className={getBookerWrapperClasses({ isEmbed: !!isEmbed })}>
|
||||
<BookerSeo
|
||||
username={user}
|
||||
eventSlug={slug}
|
||||
rescheduleUid={rescheduleUid ?? undefined}
|
||||
hideBranding={isBrandingHidden}
|
||||
isSEOIndexable={isSEOIndexable ?? true}
|
||||
eventData={
|
||||
profile && users && title && hidden !== undefined
|
||||
? {
|
||||
profile,
|
||||
users,
|
||||
title,
|
||||
hidden,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
entity={eventData.entity}
|
||||
bookingData={booking}
|
||||
/>
|
||||
<Booker
|
||||
username={user}
|
||||
eventSlug={slug}
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
import withEmbedSsr from "@lib/withEmbedSsr";
|
||||
|
||||
import PageWrapper from "@components/PageWrapper";
|
||||
|
||||
import { getServerSideProps as _getServerSideProps } from "@server/lib/[user]/[type]/getServerSideProps";
|
||||
|
||||
import type { PageProps } from "~/users/views/users-type-public-view";
|
||||
import TypePage from "~/users/views/users-type-public-view";
|
||||
|
||||
export const getServerSideProps = withEmbedSsr(_getServerSideProps);
|
||||
|
||||
export { default } from "./index";
|
||||
const Type = (props: PageProps) => <TypePage {...props} />;
|
||||
|
||||
Type.PageWrapper = PageWrapper;
|
||||
|
||||
export default Type;
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import PageWrapper from "@components/PageWrapper";
|
||||
|
||||
import type { PageProps } from "~/users/views/users-type-public-view";
|
||||
import TypePage from "~/users/views/users-type-public-view";
|
||||
|
||||
export { getServerSideProps } from "@server/lib/[user]/[type]/getServerSideProps";
|
||||
|
||||
const Type = (props: PageProps) => <TypePage {...props} />;
|
||||
|
||||
Type.PageWrapper = PageWrapper;
|
||||
|
||||
export default Type;
|
||||
@@ -1,6 +1,15 @@
|
||||
import withEmbedSsr from "@lib/withEmbedSsr";
|
||||
|
||||
import PageWrapper from "@components/PageWrapper";
|
||||
|
||||
import { getServerSideProps as _getServerSideProps } from "@server/lib/[user]/getServerSideProps";
|
||||
|
||||
export { default } from "./index";
|
||||
import User, { type PageProps } from "~/users/views/users-public-view";
|
||||
|
||||
export const getServerSideProps = withEmbedSsr(_getServerSideProps);
|
||||
|
||||
const UserPage = (props: PageProps) => <User {...props} />;
|
||||
|
||||
UserPage.PageWrapper = PageWrapper;
|
||||
|
||||
export default UserPage;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import PageWrapper from "@components/PageWrapper";
|
||||
|
||||
import User, { type PageProps } from "~/users/views/users-public-view";
|
||||
|
||||
export { getServerSideProps } from "@server/lib/[user]/getServerSideProps";
|
||||
|
||||
const UserPage = (props: PageProps) => <User {...props} />;
|
||||
|
||||
UserPage.PageWrapper = PageWrapper;
|
||||
|
||||
export default UserPage;
|
||||
Reference in New Issue
Block a user