diff --git a/apps/web/app/future/org/[orgSlug]/[user]/[type]/embed/page.tsx b/apps/web/app/future/org/[orgSlug]/[user]/[type]/embed/page.tsx new file mode 100644 index 0000000000..381ba7d88f --- /dev/null +++ b/apps/web/app/future/org/[orgSlug]/[user]/[type]/embed/page.tsx @@ -0,0 +1,9 @@ +import Page from "@pages/org/[orgSlug]/[user]/[type]/embed"; +import withEmbedSsrAppDir from "app/WithEmbedSSR"; +import { WithLayout } from "app/layoutHOC"; + +import { getData } from "../page"; + +const getEmbedData = withEmbedSsrAppDir(getData); + +export default WithLayout({ getLayout: null, getData: getEmbedData, isBookingPage: true, Page }); diff --git a/apps/web/app/future/org/[orgSlug]/[user]/[type]/page.tsx b/apps/web/app/future/org/[orgSlug]/[user]/[type]/page.tsx new file mode 100644 index 0000000000..e26772b7a2 --- /dev/null +++ b/apps/web/app/future/org/[orgSlug]/[user]/[type]/page.tsx @@ -0,0 +1,9 @@ +import Page, { type PageProps } from "@pages/org/[orgSlug]/[user]/[type]"; +import { withAppDir } from "app/AppDirSSRHOC"; +import { WithLayout } from "app/layoutHOC"; + +import { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps"; + +export const getData = withAppDir(getServerSideProps); + +export default WithLayout({ getLayout: null, getData, isBookingPage: true, Page }); diff --git a/apps/web/app/future/org/[orgSlug]/[user]/embed/page.tsx b/apps/web/app/future/org/[orgSlug]/[user]/embed/page.tsx new file mode 100644 index 0000000000..53ffe3aef2 --- /dev/null +++ b/apps/web/app/future/org/[orgSlug]/[user]/embed/page.tsx @@ -0,0 +1,9 @@ +import Page from "@pages/org/[orgSlug]/[user]/embed"; +import withEmbedSsrAppDir from "app/WithEmbedSSR"; +import { WithLayout } from "app/layoutHOC"; + +import { getData } from "../page"; + +const getEmbedData = withEmbedSsrAppDir(getData); + +export default WithLayout({ getLayout: null, getData: getEmbedData, isBookingPage: true, Page }); diff --git a/apps/web/app/future/org/[orgSlug]/[user]/page.tsx b/apps/web/app/future/org/[orgSlug]/[user]/page.tsx new file mode 100644 index 0000000000..dc2e19496c --- /dev/null +++ b/apps/web/app/future/org/[orgSlug]/[user]/page.tsx @@ -0,0 +1,7 @@ +import Page, { getServerSideProps, type PageProps } from "@pages/org/[orgSlug]/[user]"; +import { withAppDir } from "app/AppDirSSRHOC"; +import { WithLayout } from "app/layoutHOC"; + +export const getData = withAppDir(getServerSideProps); + +export default WithLayout({ getLayout: null, getData, isBookingPage: true, Page }); 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/[user]/[type]/getServerSideProps.tsx b/apps/web/lib/[user]/[type]/getServerSideProps.tsx new file mode 100644 index 0000000000..79dedb942b --- /dev/null +++ b/apps/web/lib/[user]/[type]/getServerSideProps.tsx @@ -0,0 +1,213 @@ +import { type GetServerSidePropsContext } from "next"; +import { z } from "zod"; + +import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; +import { handleTypeRedirection } from "@calcom/features/booking-redirect/handle-type"; +import { getBookingForReschedule, getBookingForSeatedEvent } from "@calcom/features/bookings/lib/get-booking"; +import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking"; +import { orgDomainConfig, userOrgQuery } from "@calcom/features/ee/organizations/lib/orgDomains"; +import { getUsernameList } from "@calcom/lib/defaultEvents"; +import slugify from "@calcom/lib/slugify"; +import prisma from "@calcom/prisma"; +import { RedirectType } from "@calcom/prisma/client"; + +import { getTemporaryOrgRedirect } from "@lib/getTemporaryOrgRedirect"; + +async function getDynamicGroupPageProps(context: GetServerSidePropsContext) { + const session = await getServerSession(context); + const { user: usernames, type: slug } = paramsSchema.parse(context.params); + const { rescheduleUid, bookingUid } = context.query; + + const { ssrInit } = await import("@server/lib/ssr"); + const ssr = await ssrInit(context); + const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug); + const org = isValidOrgDomain ? currentOrgDomain : null; + if (!org) { + const redirect = await getTemporaryOrgRedirect({ + slugs: usernames, + redirectType: RedirectType.User, + eventTypeSlug: slug, + currentQuery: context.query, + }); + + if (redirect) { + return redirect; + } + } + + const users = await prisma.user.findMany({ + where: { + username: { + in: usernames, + }, + organization: isValidOrgDomain + ? { + slug: currentOrgDomain, + } + : null, + }, + select: { + allowDynamicBooking: true, + }, + }); + + if (!users.length) { + return { + notFound: true, + } as const; + } + + let booking: GetBookingType | null = null; + if (rescheduleUid) { + booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id); + } else if (bookingUid) { + booking = await getBookingForSeatedEvent(`${bookingUid}`); + } + + // 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: usernames.join("+"), + eventSlug: slug, + org, + }); + + if (!eventData) { + return { + notFound: true, + } as const; + } + + return { + props: { + eventData: { + entity: eventData.entity, + length: eventData.length, + metadata: { + ...eventData.metadata, + multipleDuration: [15, 30, 60], + }, + }, + booking, + user: usernames.join("+"), + slug, + away: false, + trpcState: ssr.dehydrate(), + isBrandingHidden: false, + isSEOIndexable: true, + themeBasis: null, + bookingUid: bookingUid ? `${bookingUid}` : null, + rescheduleUid: rescheduleUid ? `${rescheduleUid}` : null, + }, + }; +} + +async function getUserPageProps(context: GetServerSidePropsContext) { + const session = await getServerSession(context); + const { user: usernames, type: slug } = paramsSchema.parse(context.params); + const username = usernames[0]; + const { rescheduleUid, bookingUid } = context.query; + const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug); + let outOfOffice = false; + const isOrgContext = currentOrgDomain && isValidOrgDomain; + if (!isOrgContext) { + const redirect = await getTemporaryOrgRedirect({ + slugs: usernames, + redirectType: RedirectType.User, + eventTypeSlug: slug, + currentQuery: context.query, + }); + + if (redirect) { + return redirect; + } + } + + const { ssrInit } = await import("@server/lib/ssr"); + const ssr = await ssrInit(context); + const user = await prisma.user.findFirst({ + where: { + username, + organization: userOrgQuery(context.req, context.params?.orgSlug), + }, + select: { + id: true, + hideBranding: true, + allowSEOIndexing: true, + }, + }); + + if (!user) { + return { + notFound: true, + } as const; + } + // If user is found, quickly verify bookingRedirects + const result = await handleTypeRedirection({ + userId: user.id, + username, + slug, + }); + if (result && result.outOfOffice) { + outOfOffice = true; + } + if (result && result.redirect?.destination) { + return result; + } + + let booking: GetBookingType | null = null; + if (rescheduleUid) { + booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id); + } else if (bookingUid) { + booking = await getBookingForSeatedEvent(`${bookingUid}`); + } + + 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 can show a 404 otherwise. + const eventData = await ssr.viewer.public.event.fetch({ + username, + eventSlug: slug, + org, + }); + + if (!eventData) { + return { + notFound: true, + } as const; + } + + return { + props: { + booking, + eventData: { + entity: eventData.entity, + length: eventData.length, + metadata: eventData.metadata, + }, + away: outOfOffice, + user: username, + slug, + trpcState: ssr.dehydrate(), + isBrandingHidden: user?.hideBranding, + isSEOIndexable: user?.allowSEOIndexing, + themeBasis: username, + bookingUid: bookingUid ? `${bookingUid}` : null, + rescheduleUid: rescheduleUid ? `${rescheduleUid}` : null, + }, + }; +} + +const paramsSchema = z.object({ + type: z.string().transform((s) => slugify(s)), + user: z.string().transform((s) => getUsernameList(s)), +}); + +// Booker page fetches a tiny bit of data server side, to determine early +// whether the page should show an away state or dynamic booking not allowed. +export const getServerSideProps = async (context: GetServerSidePropsContext) => { + const { user } = paramsSchema.parse(context.params); + const isDynamicGroup = user.length > 1; + + return isDynamicGroup ? await getDynamicGroupPageProps(context) : await getUserPageProps(context); +}; diff --git a/apps/web/lib/org/[orgSlug]/[user]/[type]/getServerSideProps.tsx b/apps/web/lib/org/[orgSlug]/[user]/[type]/getServerSideProps.tsx new file mode 100644 index 0000000000..98a5ab0cef --- /dev/null +++ b/apps/web/lib/org/[orgSlug]/[user]/[type]/getServerSideProps.tsx @@ -0,0 +1,58 @@ +import type { GetServerSidePropsContext } from "next"; +import z from "zod"; + +import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains"; +import slugify from "@calcom/lib/slugify"; +import prisma from "@calcom/prisma"; + +import { getServerSideProps as GSSUserTypePage } from "@lib/[user]/[type]/getServerSideProps"; +import { getServerSideProps as GSSTeamTypePage } from "@lib/team/[slug]/[type]/getServerSideProps"; + +const paramsSchema = z.object({ + orgSlug: z.string().transform((s) => slugify(s)), + user: z.string(), + type: z.string().transform((s) => slugify(s)), +}); + +export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { + const { user: teamOrUserSlugOrDynamicGroup, orgSlug, type } = paramsSchema.parse(ctx.params); + const team = await prisma.team.findFirst({ + where: { + slug: slugify(teamOrUserSlugOrDynamicGroup), + parentId: { + not: null, + }, + parent: getSlugOrRequestedSlug(orgSlug), + }, + select: { + id: true, + }, + }); + + if (team) { + const params = { slug: teamOrUserSlugOrDynamicGroup, type }; + return GSSTeamTypePage({ + ...ctx, + params: { + ...ctx.params, + ...params, + }, + query: { + ...ctx.query, + ...params, + }, + }); + } + const params = { user: teamOrUserSlugOrDynamicGroup, type }; + return GSSUserTypePage({ + ...ctx, + params: { + ...ctx.params, + ...params, + }, + query: { + ...ctx.query, + ...params, + }, + }); +}; diff --git a/apps/web/lib/org/[orgSlug]/[user]/getServerSideProps.tsx b/apps/web/lib/org/[orgSlug]/[user]/getServerSideProps.tsx new file mode 100644 index 0000000000..4a729042bb --- /dev/null +++ b/apps/web/lib/org/[orgSlug]/[user]/getServerSideProps.tsx @@ -0,0 +1,25 @@ +import { getServerSideProps as GSSUserPage } from "@pages/[user]"; +import { getServerSideProps as GSSTeamPage } from "@pages/team/[slug]"; +import type { GetServerSidePropsContext } from "next"; + +import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains"; +import prisma from "@calcom/prisma"; + +export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { + const team = await prisma.team.findFirst({ + where: { + slug: ctx.query.user as string, + parentId: { + not: null, + }, + parent: getSlugOrRequestedSlug(ctx.query.orgSlug as string), + }, + select: { + id: true, + }, + }); + if (team) { + return GSSTeamPage({ ...ctx, query: { slug: ctx.query.user } }); + } + return GSSUserPage({ ...ctx, query: { user: ctx.query.user, redirect: ctx.query.redirect } }); +}; 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..d6d79eda3a --- /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({ + slugs: 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/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index db7312154f..5d669e1e57 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -1,28 +1,19 @@ -import type { GetServerSidePropsContext } from "next"; +"use client"; + import { useSearchParams } from "next/navigation"; -import { z } from "zod"; import { Booker } from "@calcom/atoms"; -import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; -import { handleTypeRedirection } from "@calcom/features/booking-redirect/handle-type"; import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses"; import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo"; -import { getBookingForReschedule, getBookingForSeatedEvent } from "@calcom/features/bookings/lib/get-booking"; -import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking"; -import { orgDomainConfig, userOrgQuery } from "@calcom/features/ee/organizations/lib/orgDomains"; -import { getUsernameList } from "@calcom/lib/defaultEvents"; -import slugify from "@calcom/lib/slugify"; -import prisma from "@calcom/prisma"; -import { RedirectType } from "@calcom/prisma/client"; +import { getServerSideProps } from "@lib/[user]/[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 type PageProps = Omit, "trpcState"> & EmbedProps; +export { getServerSideProps }; export const getMultipleDurationValue = ( multipleDurationConfig: number[] | undefined, @@ -81,202 +72,3 @@ export default function Type({ Type.isBookingPage = true; Type.PageWrapper = PageWrapper; - -async function getDynamicGroupPageProps(context: GetServerSidePropsContext) { - const session = await getServerSession(context); - const { user: usernames, type: slug } = paramsSchema.parse(context.params); - const { rescheduleUid, bookingUid } = context.query; - - const { ssrInit } = await import("@server/lib/ssr"); - const ssr = await ssrInit(context); - const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug); - const org = isValidOrgDomain ? currentOrgDomain : null; - if (!org) { - const redirect = await getTemporaryOrgRedirect({ - slugs: usernames, - redirectType: RedirectType.User, - eventTypeSlug: slug, - currentQuery: context.query, - }); - - if (redirect) { - return redirect; - } - } - - const users = await prisma.user.findMany({ - where: { - username: { - in: usernames, - }, - organization: isValidOrgDomain - ? { - slug: currentOrgDomain, - } - : null, - }, - select: { - allowDynamicBooking: true, - }, - }); - - if (!users.length) { - return { - notFound: true, - } as const; - } - - let booking: GetBookingType | null = null; - if (rescheduleUid) { - booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id); - } else if (bookingUid) { - booking = await getBookingForSeatedEvent(`${bookingUid}`); - } - - // 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: usernames.join("+"), - eventSlug: slug, - org, - }); - - if (!eventData) { - return { - notFound: true, - } as const; - } - - return { - props: { - eventData: { - entity: eventData.entity, - length: eventData.length, - metadata: { - ...eventData.metadata, - multipleDuration: [15, 30, 60], - }, - }, - booking, - user: usernames.join("+"), - slug, - away: false, - trpcState: ssr.dehydrate(), - isBrandingHidden: false, - isSEOIndexable: true, - themeBasis: null, - bookingUid: bookingUid ? `${bookingUid}` : null, - rescheduleUid: rescheduleUid ? `${rescheduleUid}` : null, - }, - }; -} - -async function getUserPageProps(context: GetServerSidePropsContext) { - const session = await getServerSession(context); - const { user: usernames, type: slug } = paramsSchema.parse(context.params); - const username = usernames[0]; - const { rescheduleUid, bookingUid } = context.query; - const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug); - let outOfOffice = false; - const isOrgContext = currentOrgDomain && isValidOrgDomain; - if (!isOrgContext) { - const redirect = await getTemporaryOrgRedirect({ - slugs: usernames, - redirectType: RedirectType.User, - eventTypeSlug: slug, - currentQuery: context.query, - }); - - if (redirect) { - return redirect; - } - } - - const { ssrInit } = await import("@server/lib/ssr"); - const ssr = await ssrInit(context); - const user = await prisma.user.findFirst({ - where: { - username, - organization: userOrgQuery(context.req, context.params?.orgSlug), - }, - select: { - id: true, - hideBranding: true, - allowSEOIndexing: true, - }, - }); - - if (!user) { - return { - notFound: true, - } as const; - } - // If user is found, quickly verify bookingRedirects - const result = await handleTypeRedirection({ - userId: user.id, - username, - slug, - }); - if (result && result.outOfOffice) { - outOfOffice = true; - } - if (result && result.redirect?.destination) { - return result; - } - - let booking: GetBookingType | null = null; - if (rescheduleUid) { - booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id); - } else if (bookingUid) { - booking = await getBookingForSeatedEvent(`${bookingUid}`); - } - - 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 can show a 404 otherwise. - const eventData = await ssr.viewer.public.event.fetch({ - username, - eventSlug: slug, - org, - }); - - if (!eventData) { - return { - notFound: true, - } as const; - } - - return { - props: { - booking, - eventData: { - entity: eventData.entity, - length: eventData.length, - metadata: eventData.metadata, - }, - away: outOfOffice, - user: username, - slug, - trpcState: ssr.dehydrate(), - isBrandingHidden: user?.hideBranding, - isSEOIndexable: user?.allowSEOIndexing, - themeBasis: username, - bookingUid: bookingUid ? `${bookingUid}` : null, - rescheduleUid: rescheduleUid ? `${rescheduleUid}` : null, - }, - }; -} - -const paramsSchema = z.object({ - type: z.string().transform((s) => slugify(s)), - user: z.string().transform((s) => getUsernameList(s)), -}); - -// Booker page fetches a tiny bit of data server side, to determine early -// whether the page should show an away state or dynamic booking not allowed. -export const getServerSideProps = async (context: GetServerSidePropsContext) => { - const { user } = paramsSchema.parse(context.params); - const isDynamicGroup = user.length > 1; - - return isDynamicGroup ? await getDynamicGroupPageProps(context) : await getUserPageProps(context); -}; diff --git a/apps/web/pages/org/[orgSlug]/[user]/[type].tsx b/apps/web/pages/org/[orgSlug]/[user]/[type].tsx index abf833ec14..dcd5a9f3da 100644 --- a/apps/web/pages/org/[orgSlug]/[user]/[type].tsx +++ b/apps/web/pages/org/[orgSlug]/[user]/[type].tsx @@ -1,69 +1,17 @@ -import type { GetServerSidePropsContext } from "next"; -import z from "zod"; - -import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains"; -import slugify from "@calcom/lib/slugify"; -import prisma from "@calcom/prisma"; +"use client"; import PageWrapper from "@components/PageWrapper"; import type { PageProps as UserTypePageProps } from "../../../[user]/[type]"; -import UserTypePage, { getServerSideProps as GSSUserTypePage } from "../../../[user]/[type]"; +import UserTypePage from "../../../[user]/[type]"; import type { PageProps as TeamTypePageProps } from "../../../team/[slug]/[type]"; -import TeamTypePage, { getServerSideProps as GSSTeamTypePage } from "../../../team/[slug]/[type]"; +import TeamTypePage from "../../../team/[slug]/[type]"; -const paramsSchema = z.object({ - orgSlug: z.string().transform((s) => slugify(s)), - user: z.string(), - type: z.string().transform((s) => slugify(s)), -}); +export { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps"; -export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { - const { user: teamOrUserSlugOrDynamicGroup, orgSlug, type } = paramsSchema.parse(ctx.params); - const team = await prisma.team.findFirst({ - where: { - slug: slugify(teamOrUserSlugOrDynamicGroup), - parentId: { - not: null, - }, - parent: getSlugOrRequestedSlug(orgSlug), - }, - select: { - id: true, - }, - }); +export type PageProps = UserTypePageProps | TeamTypePageProps; - if (team) { - const params = { slug: teamOrUserSlugOrDynamicGroup, type }; - return GSSTeamTypePage({ - ...ctx, - params: { - ...ctx.params, - ...params, - }, - query: { - ...ctx.query, - ...params, - }, - }); - } - const params = { user: teamOrUserSlugOrDynamicGroup, type }; - return GSSUserTypePage({ - ...ctx, - params: { - ...ctx.params, - ...params, - }, - query: { - ...ctx.query, - ...params, - }, - }); -}; - -type Props = UserTypePageProps | TeamTypePageProps; - -export default function Page(props: Props) { +export default function Page(props: PageProps) { if ((props as TeamTypePageProps)?.teamId) return ; return ; } diff --git a/apps/web/pages/org/[orgSlug]/[user]/[type]/embed.tsx b/apps/web/pages/org/[orgSlug]/[user]/[type]/embed.tsx index 5cfd1726ca..9b0fb7a7a8 100644 --- a/apps/web/pages/org/[orgSlug]/[user]/[type]/embed.tsx +++ b/apps/web/pages/org/[orgSlug]/[user]/[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/org/[orgSlug]/[user]/embed.tsx b/apps/web/pages/org/[orgSlug]/[user]/embed.tsx index 5c62c6117e..6317514b2d 100644 --- a/apps/web/pages/org/[orgSlug]/[user]/embed.tsx +++ b/apps/web/pages/org/[orgSlug]/[user]/embed.tsx @@ -1,3 +1,5 @@ +"use client"; + import withEmbedSsr from "@lib/withEmbedSsr"; import { getServerSideProps as _getServerSideProps } from "../[user]"; diff --git a/apps/web/pages/org/[orgSlug]/[user]/index.tsx b/apps/web/pages/org/[orgSlug]/[user]/index.tsx index df0d6d6b97..fc2943a4bd 100644 --- a/apps/web/pages/org/[orgSlug]/[user]/index.tsx +++ b/apps/web/pages/org/[orgSlug]/[user]/index.tsx @@ -1,37 +1,17 @@ -import type { GetServerSidePropsContext } from "next"; - -import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains"; -import prisma from "@calcom/prisma"; +"use client"; import PageWrapper from "@components/PageWrapper"; import type { UserPageProps } from "../../../[user]"; -import UserPage, { getServerSideProps as GSSUserPage } from "../../../[user]"; +import UserPage from "../../../[user]"; import type { PageProps as TeamPageProps } from "../../../team/[slug]"; -import TeamPage, { getServerSideProps as GSSTeamPage } from "../../../team/[slug]"; +import TeamPage from "../../../team/[slug]"; -export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { - const team = await prisma.team.findFirst({ - where: { - slug: ctx.query.user as string, - parentId: { - not: null, - }, - parent: getSlugOrRequestedSlug(ctx.query.orgSlug as string), - }, - select: { - id: true, - }, - }); - if (team) { - return GSSTeamPage({ ...ctx, query: { slug: ctx.query.user } }); - } - return GSSUserPage({ ...ctx, query: { user: ctx.query.user, redirect: ctx.query.redirect } }); -}; +export { getServerSideProps } from "@lib/org/[orgSlug]/[user]/getServerSideProps"; -type Props = UserPageProps | TeamPageProps; +export type PageProps = UserPageProps | TeamPageProps; -export default function Page(props: Props) { +export default function Page(props: PageProps) { if ((props as TeamPageProps)?.team) return ; return ; } diff --git a/apps/web/pages/team/[slug]/[type].tsx b/apps/web/pages/team/[slug]/[type].tsx index 0ae240b95b..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({ - slugs: 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, - }, - }; -};