Files
calendar/apps/web/app/generateBookingPageMetadata.ts
T
2f9c8cb441 chore: app router - /team, /org, /[user] booking pages (excl. embeds) (#18186)
* update env vars

* update middleware

* remove pages router and move pages to /app

* move to /team

* update imports

* fix

* remove pages router and move org pages to /app

* wip

* fix orgSlug/user pages

* fix orgSlug/user/type pages

* remove generateMetadata from embed pages

* fix

* remove pages router for /user pages

* generateMetadata is not needed in embed pages

* remove future/org

* no layout in [user] page

* simplify

* fix

* fix OG image for [user]

* fix OG images for org/user and team/slug

* fix OG images for booking page

* fix all metadata

* use isBrandingHidden

* remove BookerSeo and its usages

* rename excludeAppNameFromTitle -> hideBranding

* remove logic for meeting type in HeadSeo

* remove BookerSeo instances from team-view and users-public-view

* create generateMeetingMetadata util and use it to reduce duplicate code

* remove BookerSeo imports

* fix spacing

* remove constructMeetingImage mock from head-seo.test

* fetch avatarUrl using user id for user page metadata

* fix test

* remove unused test cases

* index and follow must be true by default

* invert noindex/nofollow flags

* remove HeadSeo for already migrated pages and refactor prepareMetadata

* fix organization-settings.e2e.ts

* fix order

* enable parallel test execution for dynamic-booking e2e test

* fix

* + could be %2B in app router

* refactor handling logic for embeds in app router

* fix isEmbed

* fix embed-core

* remove dead code

* move embed pages back to /future

* add back embed pages in pages router

* revert some changes

* fix import type checks

* simplify

* fix

* feat: Implement generateBookingPageMetadata function for improved SEO and metadata handling across user and event pages (#18440)

- Added a new utility function `generateBookingPageMetadata` to streamline the generation of metadata for booking and user profile pages.
- Updated multiple page components to utilize the new function, enhancing SEO indexing and metadata consistency.
- Removed redundant code related to previous metadata generation methods, improving code clarity and maintainability.

* fix dirs

* Pr-review-fixes-app-router-team-pages (#18450)

* Remove unnecessary getPublicEvent call

* Remove unused variable

* Fix 404 for team page (#18451)

* Remove console log

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2025-01-03 22:26:30 +00:00

152 lines
3.9 KiB
TypeScript

import type { TFunction } from "next-i18next";
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
import { generateMeetingMetadata } from "./_utils";
type MetadataPayload = {
title: (t: TFunction) => string;
description: (t: TFunction) => string;
meeting: {
title: string;
profile: { name: string; image: string };
users?: { name: string; username: string }[];
};
robots: {
index: boolean;
follow: boolean;
};
};
type UserProfilePageProfile = {
name: string | null;
username: string | null;
image: string | null;
markdownStrippedBio: string | null | undefined;
};
type TeamProfilePageProfile = {
teamName: string | null;
image: string | null;
markdownStrippedBio: string | null | undefined;
};
type EventBookingPageProfile = { name: string; image: string };
type BaseMetadataArgs = {
hideBranding: boolean;
orgSlug: string | null;
isSEOIndexable: boolean;
isReschedule?: boolean;
};
type TeamProfilePageArgs = BaseMetadataArgs & {
profile: TeamProfilePageProfile;
event: null;
};
type UserProfilePageArgs = BaseMetadataArgs & {
profile: UserProfilePageProfile;
event: null;
};
type EventBookingPageArgs = BaseMetadataArgs & {
profile: EventBookingPageProfile;
event: {
title: string;
hidden: boolean;
users: { name: string | null; username: string | null }[];
};
};
export const generateEventBookingPageMetadata = async (props: EventBookingPageArgs) => {
const { profile, isReschedule, hideBranding, orgSlug, isSEOIndexable, event } = props;
const fullOrigin = getOrgFullOrigin(orgSlug);
const payload: MetadataPayload = {
title: (t) => `${isReschedule ? t("reschedule") : ""} ${event.title} | ${profile.name}`,
description: (t) => `${isReschedule ? t("reschedule") : ""} ${event.title}`,
meeting: {
title: event.title,
profile: { name: profile.name, image: profile.image },
users: event.users.map((user) => ({
name: `${user.name}`,
username: `${user.username}`,
})),
},
robots: {
index: !(event.hidden || !isSEOIndexable),
follow: !(event.hidden || !isSEOIndexable),
},
};
return {
...(await generateMeetingMetadata(
payload.meeting,
payload.title,
payload.description,
hideBranding,
fullOrigin
)),
robots: payload.robots,
};
};
export const generateUserProfilePageMetadata = async (props: UserProfilePageArgs) => {
const { profile, hideBranding, orgSlug, isSEOIndexable } = props;
const fullOrigin = getOrgFullOrigin(orgSlug);
const payload: MetadataPayload = {
title: () => profile.name ?? "",
description: () => profile.markdownStrippedBio ?? "",
meeting: {
title: profile.markdownStrippedBio ?? "",
profile: { name: profile.name ?? "", image: profile.image ?? "" },
users: [{ name: profile.name ?? "", username: profile.username ?? "" }],
},
robots: {
index: isSEOIndexable,
follow: isSEOIndexable,
},
};
return {
...(await generateMeetingMetadata(
payload.meeting,
payload.title,
payload.description,
hideBranding,
fullOrigin
)),
robots: payload.robots,
};
};
export const generateTeamProfilePageMetadata = async (props: TeamProfilePageArgs) => {
const { profile, hideBranding, orgSlug, isSEOIndexable } = props;
const fullOrigin = getOrgFullOrigin(orgSlug);
const payload: MetadataPayload = {
title: (t) => profile.teamName || t("nameless_team"),
description: () => profile.markdownStrippedBio ?? "",
meeting: {
title: profile.markdownStrippedBio ?? "",
profile: { name: `${profile.teamName}`, image: profile.image ?? "" },
},
robots: {
index: isSEOIndexable,
follow: isSEOIndexable,
},
};
return {
...(await generateMeetingMetadata(
payload.meeting,
payload.title,
payload.description,
hideBranding,
fullOrigin
)),
robots: payload.robots,
};
};