Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: app router - /team, /org, /[user] booking pages (excl. embeds) #18186

Merged
merged 68 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
ce0c847
update env vars
hbjORbj Dec 14, 2024
5535bea
update middleware
hbjORbj Dec 14, 2024
eb98496
remove pages router and move pages to /app
hbjORbj Dec 14, 2024
3c3b568
Merge remote-tracking branch 'origin/main' into chore/app-router-team…
hbjORbj Dec 18, 2024
c4fb7bc
move to /team
hbjORbj Dec 18, 2024
704a667
update imports
hbjORbj Dec 18, 2024
2081334
fix
hbjORbj Dec 18, 2024
d95d2d4
remove pages router and move org pages to /app
hbjORbj Dec 18, 2024
d3c53c1
wip
hbjORbj Dec 18, 2024
d308a82
fix orgSlug/user pages
hbjORbj Dec 18, 2024
756fc52
fix orgSlug/user/type pages
hbjORbj Dec 18, 2024
dd7198d
remove generateMetadata from embed pages
hbjORbj Dec 18, 2024
424d550
Merge remote-tracking branch 'origin/main' into chore/app-router-team…
hbjORbj Dec 18, 2024
284b99c
fix
hbjORbj Dec 18, 2024
c67b23f
remove pages router for /user pages
hbjORbj Dec 18, 2024
ddb0f9b
generateMetadata is not needed in embed pages
hbjORbj Dec 18, 2024
d3e32a6
remove testing setups for app router migration
hbjORbj Dec 18, 2024
068fbed
Merge remote-tracking branch 'origin/main' into chore/app-router-team…
hbjORbj Dec 19, 2024
dfe0977
Merge branch 'main' into chore/app-router-team-pages
hbjORbj Dec 19, 2024
73d5d75
remove future/org
hbjORbj Dec 21, 2024
e35927d
no layout in [user] page
hbjORbj Dec 21, 2024
d6eb699
simplify
hbjORbj Dec 21, 2024
8dec06b
fix
hbjORbj Dec 22, 2024
d7acf67
fix OG image for [user]
hbjORbj Dec 22, 2024
8b6d226
fix OG images for org/user and team/slug
hbjORbj Dec 22, 2024
095d985
Merge branch 'main' into chore/app-router-team-pages
hbjORbj Dec 22, 2024
85d1c77
fix OG images for booking page
hbjORbj Dec 22, 2024
ead55e0
fix all metadata
hbjORbj Dec 22, 2024
29f2b13
use isBrandingHidden
hbjORbj Dec 22, 2024
bf641c2
remove BookerSeo and its usages
hbjORbj Dec 22, 2024
6889882
rename excludeAppNameFromTitle -> hideBranding
hbjORbj Dec 22, 2024
77a0d66
remove logic for meeting type in HeadSeo
hbjORbj Dec 22, 2024
f7dc059
remove BookerSeo instances from team-view and users-public-view
hbjORbj Dec 22, 2024
06c3d8e
create generateMeetingMetadata util and use it to reduce duplicate code
hbjORbj Dec 22, 2024
e821887
remove BookerSeo imports
hbjORbj Dec 22, 2024
3a40e1b
fix spacing
hbjORbj Dec 22, 2024
8427859
remove constructMeetingImage mock from head-seo.test
hbjORbj Dec 22, 2024
11fdcd3
fetch avatarUrl using user id for user page metadata
hbjORbj Dec 22, 2024
c46562c
fix test
hbjORbj Dec 22, 2024
fafd2da
remove unused test cases
hbjORbj Dec 22, 2024
883a0f5
index and follow must be true by default
hbjORbj Dec 23, 2024
c34926e
invert noindex/nofollow flags
hbjORbj Dec 23, 2024
3199342
remove HeadSeo for already migrated pages and refactor prepareMetadata
hbjORbj Dec 23, 2024
45aeb21
fix organization-settings.e2e.ts
hbjORbj Dec 23, 2024
8082048
fix order
hbjORbj Dec 23, 2024
5cd0634
Merge remote-tracking branch 'origin/main' into chore/app-router-team…
hbjORbj Dec 23, 2024
95cd728
enable parallel test execution for dynamic-booking e2e test
hbjORbj Dec 23, 2024
34bc4b2
fix
hbjORbj Dec 23, 2024
373a229
+ could be %2B in app router
hbjORbj Dec 23, 2024
fc82764
refactor handling logic for embeds in app router
hbjORbj Dec 23, 2024
df08b06
fix isEmbed
hbjORbj Dec 26, 2024
48a0066
Merge origin/main
hbjORbj Dec 26, 2024
d5f2276
fix embed-core
hbjORbj Dec 27, 2024
3a3b4d0
remove dead code
hbjORbj Dec 27, 2024
ea08b53
Merge branch 'main' into chore/app-router-team-pages
hbjORbj Dec 27, 2024
26c29ce
move embed pages back to /future
hbjORbj Dec 28, 2024
876ea41
add back embed pages in pages router
hbjORbj Dec 28, 2024
882d097
revert some changes
hbjORbj Dec 28, 2024
43d29cd
fix import type checks
hbjORbj Dec 28, 2024
cbae15a
Merge branch 'main' into chore/app-router-team-pages
hbjORbj Dec 30, 2024
59b53d1
Merge remote-tracking branch 'origin/main' into chore/app-router-team…
hbjORbj Dec 31, 2024
ee5c49e
simplify
hbjORbj Jan 2, 2025
baa6afc
fix
hbjORbj Jan 2, 2025
be31292
feat: Implement generateBookingPageMetadata function for improved SEO…
hariombalhara Jan 2, 2025
ea3445f
fix dirs
hbjORbj Jan 3, 2025
cad082c
Pr-review-fixes-app-router-team-pages (#18450)
hariombalhara Jan 3, 2025
f6113e9
Fix 404 for team page (#18451)
hariombalhara Jan 3, 2025
d685e8d
Remove console log
hariombalhara Jan 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,6 @@ APP_ROUTER_APPS_CATEGORIES_ENABLED=0
# whether we redirect to the future/apps/categories/[category] from /apps/categories/[category] or not
APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED=0
APP_ROUTER_APPS_ENABLED=0
APP_ROUTER_TEAM_ENABLED=0
APP_ROUTER_AUTH_FORGOT_PASSWORD_ENABLED=0
APP_ROUTER_AUTH_LOGIN_ENABLED=0
APP_ROUTER_AUTH_LOGOUT_ENABLED=0
Expand Down
1 change: 0 additions & 1 deletion apps/web/abTest/middlewareFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ const ROUTES: [URLPattern, boolean][] = [
["/auth/error", process.env.APP_ROUTER_AUTH_ERROR_ENABLED === "1"] as const,
["/auth/platform/:path*", process.env.APP_ROUTER_AUTH_PLATFORM_ENABLED === "1"] as const,
["/auth/oauth2/:path*", process.env.APP_ROUTER_AUTH_OAUTH2_ENABLED === "1"] as const,
["/team", process.env.APP_ROUTER_TEAM_ENABLED === "1"] as const,
].map(([pathname, enabled]) => [
new URLPattern({
pathname,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { generateMeetingMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { headers, cookies } from "next/headers";

import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { getOrgFullOrigin, orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { EventRepository } from "@calcom/lib/server/repository/event";

import { buildLegacyCtx } from "@lib/buildLegacyCtx";
Expand All @@ -18,7 +18,7 @@ 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 { booking, user: username, slug: eventSlug, isSEOIndexable, eventData, isBrandingHidden } = props;
const rescheduleUid = booking?.uid;
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(legacyCtx.req, legacyCtx.params?.orgSlug);

Expand All @@ -31,12 +31,35 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => {
});

const profileName = event?.profile?.name ?? "";
const profileImage = event?.profile.image;
const title = event?.title ?? "";

return await _generateMetadata(
const meeting = {
title,
profile: { name: profileName, image: profileImage },
users: [
...(event?.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}`
(t) => `${rescheduleUid ? t("reschedule") : ""} ${title}`,
isBrandingHidden,
getOrgFullOrigin(eventData?.entity.orgSlug ?? null)
);

return {
...metadata,
robots: {
index: !(event?.hidden || !isSEOIndexable),
follow: !(event?.hidden || !isSEOIndexable),
},
};
};
const getData = withAppDirSsr<LegacyPageProps>(getServerSideProps);

Expand Down
55 changes: 55 additions & 0 deletions apps/web/app/[user]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps } from "app/_types";
import { generateMeetingMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { headers, cookies } from "next/headers";

import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
import { UserRepository } from "@calcom/lib/server/repository/user";

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 avatarUrl = await UserRepository.getAvatarUrl(profile.id);
const meeting = {
title: markdownStrippedBio,
profile: { name: `${profile.name}`, image: avatarUrl },
users: [
{
username: `${profile.username ?? ""}`,
name: `${profile.name ?? ""}`,
},
],
};

const metadata = await generateMeetingMetadata(
meeting,
() => profile.name,
() => markdownStrippedBio,
false,
getOrgFullOrigin(entity.orgSlug ?? null)
);

const isOrg = !!profile?.organization;
const allowSEOIndexing =
(!isOrg && profile.allowSEOIndexing) || (isOrg && isOrgSEOIndexable && profile.allowSEOIndexing);

return {
...metadata,
robots: {
index: allowSEOIndexing,
follow: allowSEOIndexing,
},
};
};

const getData = withAppDirSsr<LegacyPageProps>(getServerSideProps);
export default WithLayout({ getData, Page: LegacyPage })<"P">;
22 changes: 21 additions & 1 deletion apps/web/app/_utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import i18next from "i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { headers } from "next/headers";

import { constructGenericImage } from "@calcom/lib/OgImages";
import type { MeetingImageProps } from "@calcom/lib/OgImages";
import { constructGenericImage, 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";
Expand Down Expand Up @@ -103,3 +104,22 @@ export const _generateMetadata = async (
},
};
};

export const generateMeetingMetadata = async (
Copy link
Contributor Author

@hbjORbj hbjORbj Dec 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key difference is const image = SEO_IMG_OGIMG + constructMeetingImage(meeting);. Using constructMeetingImage is important to generate correct OG images.

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],
},
};
};
5 changes: 3 additions & 2 deletions apps/web/app/d/[link]/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) =>
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
const pageProps = await getData(legacyCtx);

const { booking, user: username, slug: eventSlug, isTeamEvent } = pageProps;
const { booking, user: username, slug: eventSlug, isTeamEvent, isBrandingHidden } = pageProps;
const rescheduleUid = booking?.uid;
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(legacyCtx.req);
const org = isValidOrgDomain ? currentOrgDomain : null;
Expand All @@ -34,7 +34,8 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) =>
const title = event?.title ?? "";
return await _generateMetadata(
(t) => `${rescheduleUid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
(t) => `${rescheduleUid ? t("reschedule") : ""} ${title}`
(t) => `${rescheduleUid ? t("reschedule") : ""} ${title}`,
isBrandingHidden
);
};

Expand Down
27 changes: 0 additions & 27 deletions apps/web/app/future/[user]/page.tsx

This file was deleted.

9 changes: 0 additions & 9 deletions apps/web/app/future/org/[orgSlug]/[user]/[type]/page.tsx

This file was deleted.

8 changes: 0 additions & 8 deletions apps/web/app/future/org/[orgSlug]/[user]/embed/page.tsx

This file was deleted.

7 changes: 0 additions & 7 deletions apps/web/app/future/org/[orgSlug]/[user]/page.tsx

This file was deleted.

29 changes: 0 additions & 29 deletions apps/web/app/future/org/[orgSlug]/page.tsx

This file was deleted.

45 changes: 0 additions & 45 deletions apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx

This file was deleted.

29 changes: 0 additions & 29 deletions apps/web/app/future/org/[orgSlug]/team/[slug]/page.tsx

This file was deleted.

28 changes: 0 additions & 28 deletions apps/web/app/future/team/[slug]/page.tsx

This file was deleted.

5 changes: 4 additions & 1 deletion apps/web/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ export const generateMetadata = () => prepareRootMetadata();
const getInitialProps = async (url: string) => {
const { pathname, searchParams } = new URL(url);

const isEmbed = pathname.endsWith("/embed") || (searchParams?.get("embedType") ?? null) !== null;
const isEmbedSnippetGeneratorPath = pathname.startsWith("/event-types");
const isEmbed =
(pathname.endsWith("/embed") || (searchParams?.get("embedType") ?? null) !== null) &&
!isEmbedSnippetGeneratorPath;
const embedColorScheme = searchParams?.get("ui.color-scheme");
Comment on lines +29 to 33
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I synced this file with _document.tsx in pages router


const req = { headers: headers(), cookies: cookies() };
Expand Down
Loading
Loading