diff --git a/apps/web/app/AppDirSSRHOC.tsx b/apps/web/app/AppDirSSRHOC.tsx index 87b419ca49..5a06c74787 100644 --- a/apps/web/app/AppDirSSRHOC.tsx +++ b/apps/web/app/AppDirSSRHOC.tsx @@ -2,7 +2,8 @@ import type { GetServerSideProps, GetServerSidePropsContext } from "next"; import { notFound, redirect } from "next/navigation"; export const withAppDir = - (getServerSideProps: GetServerSideProps) => async (context: GetServerSidePropsContext) => { + >(getServerSideProps: GetServerSideProps) => + async (context: GetServerSidePropsContext): Promise => { const ssrResponse = await getServerSideProps(context); if ("redirect" in ssrResponse) { @@ -13,5 +14,9 @@ export const withAppDir = notFound(); } - return ssrResponse.props; + return { + ...ssrResponse.props, + // includes dehydratedState required for future page trpcPropvider + ...("trpcState" in ssrResponse.props && { dehydratedState: ssrResponse.props.trpcState }), + }; }; diff --git a/apps/web/app/future/apps/categories/[category]/page.tsx b/apps/web/app/future/apps/categories/[category]/page.tsx index 6f3d54e434..bdf85125ff 100644 --- a/apps/web/app/future/apps/categories/[category]/page.tsx +++ b/apps/web/app/future/apps/categories/[category]/page.tsx @@ -2,6 +2,7 @@ import CategoryPage from "@pages/apps/categories/[category]"; import { Prisma } from "@prisma/client"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; +import { type GetServerSidePropsContext } from "next"; import { notFound } from "next/navigation"; import z from "zod"; @@ -38,8 +39,8 @@ const querySchema = z.object({ category: z.nativeEnum(AppCategories), }); -const getPageProps = async ({ params }: { params: Record }) => { - const p = querySchema.safeParse(params); +const getPageProps = async (context: GetServerSidePropsContext) => { + const p = querySchema.safeParse(context.params); if (!p.success) { return notFound(); @@ -66,6 +67,5 @@ const getPageProps = async ({ params }: { params: Record; +export default WithLayout({ getData: getPageProps, Page: CategoryPage, getLayout: null })<"P">; export const dynamic = "force-static"; diff --git a/apps/web/app/future/apps/categories/page.tsx b/apps/web/app/future/apps/categories/page.tsx index 0ef5547ee4..7df8bf3770 100644 --- a/apps/web/app/future/apps/categories/page.tsx +++ b/apps/web/app/future/apps/categories/page.tsx @@ -1,7 +1,7 @@ import LegacyPage from "@pages/apps/categories/index"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; -import type { GetServerSidePropsContext } from "next"; +import { type GetServerSidePropsContext } from "next"; import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; diff --git a/apps/web/app/future/apps/page.tsx b/apps/web/app/future/apps/page.tsx index cdccef7bdf..d53e2252ae 100644 --- a/apps/web/app/future/apps/page.tsx +++ b/apps/web/app/future/apps/page.tsx @@ -1,7 +1,7 @@ import AppsPage from "@pages/apps"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; -import type { GetServerSidePropsContext } from "next"; +import { type GetServerSidePropsContext } from "next"; import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry"; import { getLayout } from "@calcom/features/MainLayoutAppDir"; diff --git a/apps/web/app/future/bookings/[status]/layout.tsx b/apps/web/app/future/bookings/[status]/layout.tsx index 70fe487437..ff7907bf39 100644 --- a/apps/web/app/future/bookings/[status]/layout.tsx +++ b/apps/web/app/future/bookings/[status]/layout.tsx @@ -1,6 +1,6 @@ import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; -import type { GetServerSidePropsContext } from "next"; +import { type GetServerSidePropsContext } from "next"; import { notFound } from "next/navigation"; import { z } from "zod"; diff --git a/apps/web/app/future/getting-started/[[...step]]/page.tsx b/apps/web/app/future/getting-started/[[...step]]/page.tsx index 1665eca319..b8ad1b6c84 100644 --- a/apps/web/app/future/getting-started/[[...step]]/page.tsx +++ b/apps/web/app/future/getting-started/[[...step]]/page.tsx @@ -1,7 +1,6 @@ import LegacyPage from "@pages/getting-started/[[...step]]"; import { WithLayout } from "app/layoutHOC"; -import type { GetServerSidePropsContext } from "next"; -import { cookies, headers } from "next/headers"; +import { type GetServerSidePropsContext } from "next"; import { redirect } from "next/navigation"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; @@ -10,10 +9,7 @@ import prisma from "@calcom/prisma"; import { ssrInit } from "@server/lib/ssr"; const getData = async (ctx: GetServerSidePropsContext) => { - const req = { headers: headers(), cookies: cookies() }; - - //@ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest - const session = await getServerSession({ req }); + const session = await getServerSession({ req: ctx.req }); if (!session?.user?.id) { return redirect("/auth/login"); diff --git a/apps/web/app/future/team/[slug]/[type]/embed/page.tsx b/apps/web/app/future/team/[slug]/[type]/embed/page.tsx new file mode 100644 index 0000000000..edaf30bea2 --- /dev/null +++ b/apps/web/app/future/team/[slug]/[type]/embed/page.tsx @@ -0,0 +1,18 @@ +import { getServerSideProps } from "@pages/team/[slug]/[type]"; +import { withAppDir } from "app/AppDirSSRHOC"; +import type { PageProps } from "app/_types"; +import type { GetServerSidePropsContext } from "next"; +import { cookies, headers } from "next/headers"; + +import { buildLegacyCtx } from "@lib/buildLegacyCtx"; +import withEmbedSsr from "@lib/withEmbedSsr"; + +const Page = async ({ params }: PageProps) => { + const legacyCtx = buildLegacyCtx(headers(), cookies(), params); + + await withAppDir(withEmbedSsr(getServerSideProps))(legacyCtx as unknown as GetServerSidePropsContext); + + return null; +}; + +export default Page; diff --git a/apps/web/app/future/team/[slug]/[type]/page.tsx b/apps/web/app/future/team/[slug]/[type]/page.tsx new file mode 100644 index 0000000000..b7dc19427b --- /dev/null +++ b/apps/web/app/future/team/[slug]/[type]/page.tsx @@ -0,0 +1,21 @@ +import LegacyPage, { + type PageProps, + getServerSideProps as _getServerSideProps, +} from "@pages/team/[slug]/[type]"; +import { withAppDir } from "app/AppDirSSRHOC"; +import { _generateMetadata } from "app/_utils"; +import { WithLayout } from "app/layoutHOC"; + +export const generateMetadata = async () => + await _generateMetadata( + () => "", + () => "" + ); +const getData = withAppDir(_getServerSideProps); + +export default WithLayout({ + Page: LegacyPage, + getData, + getLayout: null, + isBookingPage: true, +})<"P">; diff --git a/apps/web/app/future/team/[slug]/embed/page.tsx b/apps/web/app/future/team/[slug]/embed/page.tsx new file mode 100644 index 0000000000..a0116a7dcf --- /dev/null +++ b/apps/web/app/future/team/[slug]/embed/page.tsx @@ -0,0 +1,18 @@ +import { getServerSideProps } from "@pages/team/[slug]"; +import { withAppDir } from "app/AppDirSSRHOC"; +import type { PageProps } from "app/_types"; +import type { GetServerSidePropsContext } from "next"; +import { cookies, headers } from "next/headers"; + +import { buildLegacyCtx } from "@lib/buildLegacyCtx"; +import withEmbedSsr from "@lib/withEmbedSsr"; + +const Page = async ({ params }: PageProps) => { + const legacyCtx = buildLegacyCtx(headers(), cookies(), params); + + await withAppDir(withEmbedSsr(getServerSideProps))(legacyCtx as unknown as GetServerSidePropsContext); + + return null; +}; + +export default Page; diff --git a/apps/web/app/future/team/[slug]/page.tsx b/apps/web/app/future/team/[slug]/page.tsx new file mode 100644 index 0000000000..98a21531cc --- /dev/null +++ b/apps/web/app/future/team/[slug]/page.tsx @@ -0,0 +1,29 @@ +import LegacyPage, { type PageProps, getServerSideProps as _getServerSideProps } from "@pages/team/[slug]"; +import { withAppDir } from "app/AppDirSSRHOC"; +import { _generateMetadata } from "app/_utils"; +import { WithLayout } from "app/layoutHOC"; +import { type GetServerSidePropsContext } from "next"; +import { cookies, headers } from "next/headers"; + +import { buildLegacyCtx } from "@lib/buildLegacyCtx"; + +export const generateMetadata = async ({ params }: { params: Record }) => { + const props = await getData( + buildLegacyCtx(headers(), cookies(), params) as unknown as GetServerSidePropsContext + ); + const teamName = props.team.name || "Nameless Team"; + + return await _generateMetadata( + () => teamName, + () => teamName + ); +}; + +const getData = withAppDir(_getServerSideProps); + +export default WithLayout({ + Page: LegacyPage, + getData, + getLayout: null, + isBookingPage: true, +})<"P">; diff --git a/apps/web/app/future/teams/page.tsx b/apps/web/app/future/teams/page.tsx index 6453ac8e01..93e3b360ab 100644 --- a/apps/web/app/future/teams/page.tsx +++ b/apps/web/app/future/teams/page.tsx @@ -1,7 +1,7 @@ import OldPage from "@pages/teams/index"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; -import type { GetServerSidePropsContext } from "next"; +import { type GetServerSidePropsContext } from "next"; import { redirect } from "next/navigation"; import { getLayout } from "@calcom/features/MainLayoutAppDir"; diff --git a/apps/web/app/future/video/[uid]/page.tsx b/apps/web/app/future/video/[uid]/page.tsx index 7625bd0125..824c18baec 100644 --- a/apps/web/app/future/video/[uid]/page.tsx +++ b/apps/web/app/future/video/[uid]/page.tsx @@ -2,7 +2,7 @@ import OldPage from "@pages/video/[uid]"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import MarkdownIt from "markdown-it"; -import type { GetServerSidePropsContext } from "next"; +import { type GetServerSidePropsContext } from "next"; import { redirect } from "next/navigation"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; diff --git a/apps/web/app/layoutHOC.tsx b/apps/web/app/layoutHOC.tsx index fab74cb2d5..14543014d8 100644 --- a/apps/web/app/layoutHOC.tsx +++ b/apps/web/app/layoutHOC.tsx @@ -10,9 +10,15 @@ type WithLayoutParams> = { getLayout: ((page: React.ReactElement) => React.ReactNode) | null; Page?: (props: T) => React.ReactElement | null; getData?: (arg: GetServerSidePropsContext) => Promise; + isBookingPage?: boolean; }; -export function WithLayout>({ getLayout, getData, Page }: WithLayoutParams) { +export function WithLayout>({ + getLayout, + getData, + Page, + isBookingPage, +}: WithLayoutParams) { return async

(p: P extends "P" ? PageProps : LayoutProps) => { const h = headers(); const nonce = h.get("x-nonce") ?? undefined; @@ -23,7 +29,13 @@ export function WithLayout>({ getLayout, getData, const children = "children" in p ? p.children : null; return ( - + {Page ? : children} ); diff --git a/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx b/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx new file mode 100644 index 0000000000..169e275c37 --- /dev/null +++ b/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx @@ -0,0 +1,102 @@ +import type { GetServerSidePropsContext } from "next"; +import { z } from "zod"; + +import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; +import { getBookingForReschedule, getMultipleDurationValue } from "@calcom/features/bookings/lib/get-booking"; +import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking"; +import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains"; +import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains"; +import slugify from "@calcom/lib/slugify"; +import prisma from "@calcom/prisma"; +import { RedirectType } from "@calcom/prisma/client"; + +import { getTemporaryOrgRedirect } from "@lib/getTemporaryOrgRedirect"; + +const paramsSchema = z.object({ + type: z.string().transform((s) => slugify(s)), + slug: z.string().transform((s) => slugify(s)), +}); + +// Booker page fetches a tiny bit of data server side: +// 1. Check if team exists, to show 404 +// 2. If rescheduling, get the booking details +export const getServerSideProps = async (context: GetServerSidePropsContext) => { + const session = await getServerSession(context); + const { slug: teamSlug, type: meetingSlug } = paramsSchema.parse(context.params); + const { rescheduleUid, duration: queryDuration, isInstantMeeting: queryIsInstantMeeting } = context.query; + const { ssrInit } = await import("@server/lib/ssr"); + const ssr = await ssrInit(context); + const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug); + const isOrgContext = currentOrgDomain && isValidOrgDomain; + + if (!isOrgContext) { + const redirect = await getTemporaryOrgRedirect({ + slug: teamSlug, + redirectType: RedirectType.Team, + eventTypeSlug: meetingSlug, + currentQuery: context.query, + }); + + if (redirect) { + return redirect; + } + } + + const team = await prisma.team.findFirst({ + where: { + ...getSlugOrRequestedSlug(teamSlug), + parent: isValidOrgDomain && currentOrgDomain ? getSlugOrRequestedSlug(currentOrgDomain) : null, + }, + select: { + id: true, + hideBranding: true, + }, + }); + + if (!team) { + return { + notFound: true, + } as const; + } + + let booking: GetBookingType | null = null; + if (rescheduleUid) { + booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id); + } + + const org = isValidOrgDomain ? currentOrgDomain : null; + // We use this to both prefetch the query on the server, + // as well as to check if the event exist, so we c an show a 404 otherwise. + const eventData = await ssr.viewer.public.event.fetch({ + username: teamSlug, + eventSlug: meetingSlug, + isTeamEvent: true, + org, + }); + + if (!eventData) { + return { + notFound: true, + } as const; + } + + return { + props: { + entity: eventData.entity, + duration: getMultipleDurationValue( + eventData.metadata?.multipleDuration, + queryDuration, + eventData.length + ), + booking, + away: false, + user: teamSlug, + teamId: team.id, + slug: meetingSlug, + trpcState: ssr.dehydrate(), + isBrandingHidden: team?.hideBranding, + isInstantMeeting: eventData.isInstantEvent && queryIsInstantMeeting ? true : false, + themeBasis: null, + }, + }; +}; diff --git a/apps/web/lib/team/[slug]/getServerSideProps.tsx b/apps/web/lib/team/[slug]/getServerSideProps.tsx new file mode 100644 index 0000000000..b6b535eeab --- /dev/null +++ b/apps/web/lib/team/[slug]/getServerSideProps.tsx @@ -0,0 +1,137 @@ +import type { GetServerSidePropsContext } from "next"; + +import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains"; +import { getFeatureFlagMap } from "@calcom/features/flags/server/utils"; +import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client"; +import logger from "@calcom/lib/logger"; +import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; +import { getTeamWithMembers } from "@calcom/lib/server/queries/teams"; +import slugify from "@calcom/lib/slugify"; +import { stripMarkdown } from "@calcom/lib/stripMarkdown"; +import prisma from "@calcom/prisma"; +import { RedirectType } from "@calcom/prisma/client"; +import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; + +import { getTemporaryOrgRedirect } from "@lib/getTemporaryOrgRedirect"; + +import { ssrInit } from "@server/lib/ssr"; + +const log = logger.getSubLogger({ prefix: ["team/[slug]"] }); + +export const getServerSideProps = async (context: GetServerSidePropsContext) => { + const slug = Array.isArray(context.query?.slug) ? context.query.slug.pop() : context.query.slug; + const { isValidOrgDomain, currentOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug); + const isOrgContext = isValidOrgDomain && currentOrgDomain; + + // Provided by Rewrite from next.config.js + const isOrgProfile = context.query?.isOrgProfile === "1"; + const flags = await getFeatureFlagMap(prisma); + const isOrganizationFeatureEnabled = flags["organizations"]; + + log.debug("getServerSideProps", { + isOrgProfile, + isOrganizationFeatureEnabled, + isValidOrgDomain, + currentOrgDomain, + }); + + const team = await getTeamWithMembers({ + slug: slugify(slug ?? ""), + orgSlug: currentOrgDomain, + isTeamView: true, + isOrgView: isValidOrgDomain && isOrgProfile, + }); + + if (!isOrgContext && slug) { + const redirect = await getTemporaryOrgRedirect({ + slug: slug, + redirectType: RedirectType.Team, + eventTypeSlug: null, + currentQuery: context.query, + }); + + if (redirect) { + return redirect; + } + } + + const ssr = await ssrInit(context); + const metadata = teamMetadataSchema.parse(team?.metadata ?? {}); + + // Taking care of sub-teams and orgs + if ( + (!isValidOrgDomain && team?.parent) || + (!isValidOrgDomain && !!metadata?.isOrganization) || + !isOrganizationFeatureEnabled + ) { + return { notFound: true } as const; + } + + if (!team || (team.parent && !team.parent.slug)) { + const unpublishedTeam = await prisma.team.findFirst({ + where: { + ...(team?.parent + ? { id: team.parent.id } + : { + metadata: { + path: ["requestedSlug"], + equals: slug, + }, + }), + }, + }); + + if (!unpublishedTeam) return { notFound: true } as const; + + return { + props: { + isUnpublished: true, + team: { ...unpublishedTeam, createdAt: null }, + trpcState: ssr.dehydrate(), + }, + } as const; + } + + team.eventTypes = + team.eventTypes?.map((type) => ({ + ...type, + users: type.users.map((user) => ({ + ...user, + avatar: `/${user.username}/avatar.png`, + })), + descriptionAsSafeHTML: markdownToSafeHTML(type.description), + })) ?? null; + + const safeBio = markdownToSafeHTML(team.bio) || ""; + + const members = !team.isPrivate + ? team.members.map((member) => { + return { + name: member.name, + id: member.id, + bio: member.bio, + subteams: member.subteams, + username: member.username, + accepted: member.accepted, + organizationId: member.organizationId, + safeBio: markdownToSafeHTML(member.bio || ""), + bookerUrl: getBookerBaseUrlSync(member.organization?.slug || ""), + }; + }) + : []; + + const markdownStrippedBio = stripMarkdown(team?.bio || ""); + + const { inviteToken: _inviteToken, ...serializableTeam } = team; + + return { + props: { + team: { ...serializableTeam, safeBio, members, metadata }, + themeBasis: serializableTeam.slug, + trpcState: ssr.dehydrate(), + markdownStrippedBio, + isValidOrgDomain, + currentOrgDomain, + }, + } as const; +}; diff --git a/apps/web/pages/team/[slug].tsx b/apps/web/pages/team/[slug].tsx index a5cdaca7fa..678303d029 100644 --- a/apps/web/pages/team/[slug].tsx +++ b/apps/web/pages/team/[slug].tsx @@ -1,3 +1,5 @@ +"use client"; + // This route is reachable by // 1. /team/[slug] // 2. / (when on org domain e.g. http://calcom.cal.com/. This is through a rewrite from next.config.js) @@ -5,45 +7,32 @@ // 1. org/[orgSlug]/team/[slug] // 2. org/[orgSlug]/[user]/[type] import classNames from "classnames"; -import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { useEffect } from "react"; import { sdkActionManager, useIsEmbed } from "@calcom/embed-core/embed-iframe"; -import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains"; import EventTypeDescription from "@calcom/features/eventtypes/components/EventTypeDescription"; -import { getFeatureFlagMap } from "@calcom/features/flags/server/utils"; import { WEBAPP_URL } from "@calcom/lib/constants"; -import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery"; import useTheme from "@calcom/lib/hooks/useTheme"; -import logger from "@calcom/lib/logger"; -import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; -import { getTeamWithMembers } from "@calcom/lib/server/queries/teams"; -import slugify from "@calcom/lib/slugify"; -import { stripMarkdown } from "@calcom/lib/stripMarkdown"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; -import prisma from "@calcom/prisma"; -import { RedirectType } from "@calcom/prisma/client"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; import { Avatar, Button, HeadSeo, UnpublishedEntity } from "@calcom/ui"; import { ArrowRight } from "@calcom/ui/components/icon"; import { useToggleQuery } from "@lib/hooks/useToggleQuery"; +import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps"; import type { inferSSRProps } from "@lib/types/inferSSRProps"; import PageWrapper from "@components/PageWrapper"; import Team from "@components/team/screens/Team"; import { UserAvatarGroup } from "@components/ui/avatar/UserAvatarGroup"; -import { ssrInit } from "@server/lib/ssr"; - -import { getTemporaryOrgRedirect } from "../../lib/getTemporaryOrgRedirect"; +export { getServerSideProps }; export type PageProps = inferSSRProps; -const log = logger.getSubLogger({ prefix: ["team/[slug]"] }); function TeamPage({ team, isUnpublished, @@ -268,124 +257,6 @@ function TeamPage({ ); } -export const getServerSideProps = async (context: GetServerSidePropsContext) => { - const slug = Array.isArray(context.query?.slug) ? context.query.slug.pop() : context.query.slug; - const { isValidOrgDomain, currentOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug); - const isOrgContext = isValidOrgDomain && currentOrgDomain; - - // Provided by Rewrite from next.config.js - const isOrgProfile = context.query?.isOrgProfile === "1"; - const flags = await getFeatureFlagMap(prisma); - const isOrganizationFeatureEnabled = flags["organizations"]; - - log.debug("getServerSideProps", { - isOrgProfile, - isOrganizationFeatureEnabled, - isValidOrgDomain, - currentOrgDomain, - }); - - const team = await getTeamWithMembers({ - slug: slugify(slug ?? ""), - orgSlug: currentOrgDomain, - isTeamView: true, - isOrgView: isValidOrgDomain && isOrgProfile, - }); - - if (!isOrgContext && slug) { - const redirect = await getTemporaryOrgRedirect({ - slug: slug, - redirectType: RedirectType.Team, - eventTypeSlug: null, - currentQuery: context.query, - }); - - if (redirect) { - return redirect; - } - } - - const ssr = await ssrInit(context); - const metadata = teamMetadataSchema.parse(team?.metadata ?? {}); - - // Taking care of sub-teams and orgs - if ( - (!isValidOrgDomain && team?.parent) || - (!isValidOrgDomain && !!metadata?.isOrganization) || - !isOrganizationFeatureEnabled - ) { - return { notFound: true } as const; - } - - if (!team || (team.parent && !team.parent.slug)) { - const unpublishedTeam = await prisma.team.findFirst({ - where: { - ...(team?.parent - ? { id: team.parent.id } - : { - metadata: { - path: ["requestedSlug"], - equals: slug, - }, - }), - }, - }); - - if (!unpublishedTeam) return { notFound: true } as const; - - return { - props: { - isUnpublished: true, - team: { ...unpublishedTeam, createdAt: null }, - trpcState: ssr.dehydrate(), - }, - } as const; - } - - team.eventTypes = - team.eventTypes?.map((type) => ({ - ...type, - users: type.users.map((user) => ({ - ...user, - avatar: `/${user.username}/avatar.png`, - })), - descriptionAsSafeHTML: markdownToSafeHTML(type.description), - })) ?? null; - - const safeBio = markdownToSafeHTML(team.bio) || ""; - - const members = !team.isPrivate - ? team.members.map((member) => { - return { - name: member.name, - id: member.id, - bio: member.bio, - subteams: member.subteams, - username: member.username, - accepted: member.accepted, - organizationId: member.organizationId, - safeBio: markdownToSafeHTML(member.bio || ""), - bookerUrl: getBookerBaseUrlSync(member.organization?.slug || ""), - }; - }) - : []; - - const markdownStrippedBio = stripMarkdown(team?.bio || ""); - - const { inviteToken: _inviteToken, ...serializableTeam } = team; - - return { - props: { - team: { ...serializableTeam, safeBio, members, metadata }, - themeBasis: serializableTeam.slug, - trpcState: ssr.dehydrate(), - markdownStrippedBio, - isValidOrgDomain, - currentOrgDomain, - }, - } as const; -}; - TeamPage.isBookingPage = true; TeamPage.PageWrapper = PageWrapper; diff --git a/apps/web/pages/team/[slug]/[type].tsx b/apps/web/pages/team/[slug]/[type].tsx index 781f17d4b8..933dd5e695 100644 --- a/apps/web/pages/team/[slug]/[type].tsx +++ b/apps/web/pages/team/[slug]/[type].tsx @@ -1,27 +1,19 @@ -import type { GetServerSidePropsContext } from "next"; -import { z } from "zod"; +"use client"; import { Booker } from "@calcom/atoms"; -import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses"; import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo"; -import { getBookingForReschedule, getMultipleDurationValue } from "@calcom/features/bookings/lib/get-booking"; -import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking"; -import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains"; -import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains"; -import slugify from "@calcom/lib/slugify"; -import prisma from "@calcom/prisma"; -import { RedirectType } from "@calcom/prisma/client"; +import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps"; import type { inferSSRProps } from "@lib/types/inferSSRProps"; import type { EmbedProps } from "@lib/withEmbedSsr"; import PageWrapper from "@components/PageWrapper"; -import { getTemporaryOrgRedirect } from "../../../lib/getTemporaryOrgRedirect"; - export type PageProps = inferSSRProps & EmbedProps; +export { getServerSideProps }; + export default function Type({ slug, user, @@ -61,92 +53,3 @@ export default function Type({ Type.PageWrapper = PageWrapper; Type.isBookingPage = true; - -const paramsSchema = z.object({ - type: z.string().transform((s) => slugify(s)), - slug: z.string().transform((s) => slugify(s)), -}); - -// Booker page fetches a tiny bit of data server side: -// 1. Check if team exists, to show 404 -// 2. If rescheduling, get the booking details -export const getServerSideProps = async (context: GetServerSidePropsContext) => { - const session = await getServerSession(context); - const { slug: teamSlug, type: meetingSlug } = paramsSchema.parse(context.params); - const { rescheduleUid, duration: queryDuration, isInstantMeeting: queryIsInstantMeeting } = context.query; - const { ssrInit } = await import("@server/lib/ssr"); - const ssr = await ssrInit(context); - const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug); - const isOrgContext = currentOrgDomain && isValidOrgDomain; - - if (!isOrgContext) { - const redirect = await getTemporaryOrgRedirect({ - slug: teamSlug, - redirectType: RedirectType.Team, - eventTypeSlug: meetingSlug, - currentQuery: context.query, - }); - - if (redirect) { - return redirect; - } - } - - const team = await prisma.team.findFirst({ - where: { - ...getSlugOrRequestedSlug(teamSlug), - parent: isValidOrgDomain && currentOrgDomain ? getSlugOrRequestedSlug(currentOrgDomain) : null, - }, - select: { - id: true, - hideBranding: true, - }, - }); - - if (!team) { - return { - notFound: true, - } as const; - } - - let booking: GetBookingType | null = null; - if (rescheduleUid) { - booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id); - } - - const org = isValidOrgDomain ? currentOrgDomain : null; - // We use this to both prefetch the query on the server, - // as well as to check if the event exist, so we c an show a 404 otherwise. - const eventData = await ssr.viewer.public.event.fetch({ - username: teamSlug, - eventSlug: meetingSlug, - isTeamEvent: true, - org, - }); - - if (!eventData) { - return { - notFound: true, - } as const; - } - - return { - props: { - entity: eventData.entity, - duration: getMultipleDurationValue( - eventData.metadata?.multipleDuration, - queryDuration, - eventData.length - ), - booking, - away: false, - user: teamSlug, - teamId: team.id, - slug: meetingSlug, - trpcState: ssr.dehydrate(), - isBrandingHidden: team?.hideBranding, - isInstantMeeting: eventData.isInstantEvent && queryIsInstantMeeting ? true : false, - themeBasis: null, - }, - }; -}; diff --git a/apps/web/pages/team/[slug]/[type]/embed.tsx b/apps/web/pages/team/[slug]/[type]/embed.tsx index 5cfd1726ca..9b0fb7a7a8 100644 --- a/apps/web/pages/team/[slug]/[type]/embed.tsx +++ b/apps/web/pages/team/[slug]/[type]/embed.tsx @@ -1,3 +1,5 @@ +"use client"; + import withEmbedSsr from "@lib/withEmbedSsr"; import { getServerSideProps as _getServerSideProps } from "../[type]"; diff --git a/apps/web/pages/team/[slug]/embed.tsx b/apps/web/pages/team/[slug]/embed.tsx index d220355cc9..fa255bc512 100644 --- a/apps/web/pages/team/[slug]/embed.tsx +++ b/apps/web/pages/team/[slug]/embed.tsx @@ -1,3 +1,5 @@ +"use client"; + import withEmbedSsr from "@lib/withEmbedSsr"; import { getServerSideProps as _getServerSideProps } from "../../team/[slug]";