diff --git a/apps/web/app/future/booking/[uid]/embed/page.tsx b/apps/web/app/future/booking/[uid]/embed/page.tsx index 4fab8011c66868..18510597d7f191 100644 --- a/apps/web/app/future/booking/[uid]/embed/page.tsx +++ b/apps/web/app/future/booking/[uid]/embed/page.tsx @@ -1,9 +1,11 @@ -import OldPage, { type PageProps, getServerSideProps } from "@pages/booking/[uid]"; +import { type PageProps, getServerSideProps } from "@pages/booking/[uid]"; +import OldPage from "@pages/booking/[uid]/embed"; import { withAppDirSsr } from "app/WithAppDirSsr"; import withEmbedSsrAppDir from "app/WithEmbedSSR"; import { WithLayout } from "app/layoutHOC"; const getData = withAppDirSsr(getServerSideProps); + const getEmbedData = withEmbedSsrAppDir(getData); export default WithLayout({ getLayout: null, getData: getEmbedData, Page: OldPage }); diff --git a/apps/web/app/future/booking/[uid]/page.tsx b/apps/web/app/future/booking/[uid]/page.tsx index b7019972051f79..a8359a43d168aa 100644 --- a/apps/web/app/future/booking/[uid]/page.tsx +++ b/apps/web/app/future/booking/[uid]/page.tsx @@ -1,204 +1,36 @@ -import OldPage from "@pages/booking/[uid]"; +import OldPage, { type PageProps } from "@pages/booking/[uid]"; +import { withAppDirSsr } from "app/WithAppDirSsr"; +import type { Params, SearchParams } from "app/_types"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; -import type { GetServerSidePropsContext } from "next"; -import { notFound } from "next/navigation"; -import { z } from "zod"; - -import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; -import { getBookingWithResponses } from "@calcom/features/bookings/lib/get-booking"; -import { parseRecurringEvent } from "@calcom/lib"; -import { getDefaultEvent } from "@calcom/lib/defaultEvents"; -import { maybeGetBookingUidFromSeat } from "@calcom/lib/server/maybeGetBookingUidFromSeat"; -import prisma from "@calcom/prisma"; -import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; - -import { getRecurringBookings, handleSeatsEventTypeOnBooking, getEventTypesFromDB } from "@lib/booking"; - -import { ssrInit } from "@server/lib/ssr"; - -const stringToBoolean = z - .string() - .optional() - .transform((val) => val === "true"); - -const querySchema = z.object({ - uid: z.string(), - email: z.string().optional(), - eventTypeSlug: z.string().optional(), - cancel: stringToBoolean, - allRemainingBookings: stringToBoolean, - changes: stringToBoolean, - reschedule: stringToBoolean, - isSuccessBookingPage: stringToBoolean, - formerTime: z.string().optional(), - seatReferenceUid: z.string().optional(), -}); - -export const generateMetadata = async () => - await _generateMetadata( - () => "", - () => "" +import { type GetServerSidePropsContext } from "next"; +import { cookies, headers } from "next/headers"; + +import { BookingStatus } from "@calcom/prisma/enums"; + +import { getServerSideProps } from "@lib/booking/[uid]/getServerSideProps"; +import { buildLegacyCtx } from "@lib/buildLegacyCtx"; + +export const generateMetadata = async ({ + params, + searchParams, +}: { + params: Params; + searchParams: SearchParams; +}) => { + const { bookingInfo, eventType, recurringBookings } = await getData( + buildLegacyCtx(headers(), cookies(), params, searchParams) as unknown as GetServerSidePropsContext ); + const needsConfirmation = bookingInfo.status === BookingStatus.PENDING && eventType.requiresConfirmation; -const getData = async (context: GetServerSidePropsContext) => { - const ssr = await ssrInit(context); - const session = await getServerSession(context); - let tz: string | null = null; - let userTimeFormat: number | null = null; - let requiresLoginToUpdate = false; - if (session) { - const user = await ssr.viewer.me.fetch(); - tz = user.timeZone; - userTimeFormat = user.timeFormat; - } - - const parsedQuery = querySchema.safeParse(context.query); - - if (!parsedQuery.success) { - notFound(); - } - - const { uid, eventTypeSlug, seatReferenceUid } = parsedQuery.data; - - const { uid: maybeUid } = await maybeGetBookingUidFromSeat(prisma, uid); - const bookingInfoRaw = await prisma.booking.findFirst({ - where: { - uid: maybeUid, - }, - select: { - title: true, - id: true, - uid: true, - description: true, - customInputs: true, - smsReminderNumber: true, - recurringEventId: true, - startTime: true, - endTime: true, - location: true, - status: true, - metadata: true, - cancellationReason: true, - responses: true, - rejectionReason: true, - user: { - select: { - id: true, - name: true, - email: true, - username: true, - timeZone: true, - }, - }, - attendees: { - select: { - name: true, - email: true, - timeZone: true, - }, - }, - eventTypeId: true, - eventType: { - select: { - eventName: true, - slug: true, - timeZone: true, - }, - }, - seatsReferences: { - select: { - referenceUid: true, - }, - }, - }, - }); - - if (!bookingInfoRaw) { - notFound(); - } - - const eventTypeRaw = !bookingInfoRaw.eventTypeId - ? getDefaultEvent(eventTypeSlug || "") - : await getEventTypesFromDB(bookingInfoRaw.eventTypeId); - if (!eventTypeRaw) { - notFound(); - } - - if (eventTypeRaw.seatsPerTimeSlot && !seatReferenceUid && !session) { - requiresLoginToUpdate = true; - } - - const bookingInfo = getBookingWithResponses(bookingInfoRaw); - // @NOTE: had to do this because Server side cant return [Object objects] - // probably fixable with json.stringify -> json.parse - bookingInfo["startTime"] = (bookingInfo?.startTime as Date)?.toISOString() as unknown as Date; - bookingInfo["endTime"] = (bookingInfo?.endTime as Date)?.toISOString() as unknown as Date; - - eventTypeRaw.users = !!eventTypeRaw.hosts?.length - ? eventTypeRaw.hosts.map((host) => host.user) - : eventTypeRaw.users; - - if (!eventTypeRaw.users.length) { - if (!eventTypeRaw.owner) { - notFound(); - } - - eventTypeRaw.users.push({ - ...eventTypeRaw.owner, - }); - } - - const eventType = { - ...eventTypeRaw, - periodStartDate: eventTypeRaw.periodStartDate?.toString() ?? null, - periodEndDate: eventTypeRaw.periodEndDate?.toString() ?? null, - metadata: EventTypeMetaDataSchema.parse(eventTypeRaw.metadata), - recurringEvent: parseRecurringEvent(eventTypeRaw.recurringEvent), - customInputs: customInputSchema.array().parse(eventTypeRaw.customInputs), - }; - - const profile = { - name: eventType.team?.name || eventType.users[0]?.name || null, - email: eventType.team ? null : eventType.users[0].email || null, - theme: (!eventType.team?.name && eventType.users[0]?.theme) || null, - brandColor: eventType.team ? null : eventType.users[0].brandColor || null, - darkBrandColor: eventType.team ? null : eventType.users[0].darkBrandColor || null, - slug: eventType.team?.slug || eventType.users[0]?.username || null, - }; - - if (bookingInfo !== null && eventType.seatsPerTimeSlot) { - await handleSeatsEventTypeOnBooking(eventType, bookingInfo, seatReferenceUid, session?.user.id); - } - - const payment = await prisma.payment.findFirst({ - where: { - bookingId: bookingInfo.id, - }, - select: { - success: true, - refunded: true, - currency: true, - amount: true, - paymentOption: true, - }, - }); - - return { - themeBasis: eventType.team ? eventType.team.slug : eventType.users[0]?.username, - hideBranding: eventType.team ? eventType.team.hideBranding : eventType.users[0].hideBranding, - profile, - eventType, - recurringBookings: await getRecurringBookings(bookingInfo.recurringEventId), - dehydratedState: ssr.dehydrate(), - dynamicEventName: bookingInfo?.eventType?.eventName || "", - bookingInfo, - paymentStatus: payment, - ...(tz && { tz }), - userTimeFormat, - requiresLoginToUpdate, - }; + return await _generateMetadata( + (t) => + t(`booking_${needsConfirmation ? "submitted" : "confirmed"}${recurringBookings ? "_recurring" : ""}`), + (t) => + t(`booking_${needsConfirmation ? "submitted" : "confirmed"}${recurringBookings ? "_recurring" : ""}`) + ); }; -// @ts-expect-error Argument of type '{ req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'. +const getData = withAppDirSsr(getServerSideProps); + export default WithLayout({ getLayout: null, getData, Page: OldPage }); diff --git a/apps/web/app/future/bookings/[status]/layout.tsx b/apps/web/app/future/bookings/[status]/layout.tsx deleted file mode 100644 index 70fe487437bd43..00000000000000 --- a/apps/web/app/future/bookings/[status]/layout.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { _generateMetadata } from "app/_utils"; -import { WithLayout } from "app/layoutHOC"; -import type { GetServerSidePropsContext } from "next"; -import { notFound } from "next/navigation"; -import { z } from "zod"; - -import { getLayout } from "@calcom/features/MainLayoutAppDir"; -import { APP_NAME } from "@calcom/lib/constants"; - -import { ssgInit } from "@server/lib/ssg"; - -const validStatuses = ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] as const; - -const querySchema = z.object({ - status: z.enum(validStatuses), -}); - -export const generateMetadata = async () => - await _generateMetadata( - (t) => `${APP_NAME} | ${t("bookings")}`, - () => "" - ); - -export const generateStaticParams = async () => { - return validStatuses.map((status) => ({ status })); -}; - -const getData = async (ctx: GetServerSidePropsContext) => { - const parsedParams = querySchema.safeParse(ctx.params); - - if (!parsedParams.success) { - notFound(); - } - - const ssg = await ssgInit(ctx); - - return { - status: parsedParams.data.status, - dehydratedState: ssg.dehydrate(), - }; -}; - -export default WithLayout({ getLayout, getData })<"L">; - -export const dynamic = "force-static"; diff --git a/apps/web/app/future/bookings/[status]/page.tsx b/apps/web/app/future/bookings/[status]/page.tsx index bd24129e5579fa..f821d7a6a1dc15 100644 --- a/apps/web/app/future/bookings/[status]/page.tsx +++ b/apps/web/app/future/bookings/[status]/page.tsx @@ -1 +1,29 @@ -export { default } from "@pages/bookings/[status]"; +import Page from "@pages/bookings/[status]"; +import { withAppDirSsg } from "app/WithAppDirSsg"; +import { _generateMetadata } from "app/_utils"; +import { WithLayout } from "app/layoutHOC"; +import type { InferGetStaticPropsType } from "next"; + +import { getLayout } from "@calcom/features/MainLayoutAppDir"; +import { APP_NAME } from "@calcom/lib/constants"; + +import { getStaticProps } from "@lib/bookings/[status]/getStaticProps"; + +const validStatuses = ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] as const; + +type Y = InferGetStaticPropsType; +const getData = withAppDirSsg(getStaticProps); + +export const generateMetadata = async () => + await _generateMetadata( + (t) => `${APP_NAME} | ${t("bookings")}`, + () => "" + ); + +export const generateStaticParams = async () => { + return validStatuses.map((status) => ({ status })); +}; + +export default WithLayout({ getLayout, getData, Page })<"P">; + +export const dynamic = "force-static"; diff --git a/apps/web/lib/bookings/[status]/getStaticProps.tsx b/apps/web/lib/bookings/[status]/getStaticProps.tsx new file mode 100644 index 00000000000000..b4a8de083bffff --- /dev/null +++ b/apps/web/lib/bookings/[status]/getStaticProps.tsx @@ -0,0 +1,24 @@ +import { type GetStaticProps } from "next"; +import { z } from "zod"; + +import { ssgInit } from "@server/lib/ssg"; + +const validStatuses = ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] as const; + +const querySchema = z.object({ + status: z.enum(validStatuses), +}); + +export const getStaticProps: GetStaticProps = async (ctx) => { + const params = querySchema.safeParse(ctx.params); + const ssg = await ssgInit(ctx); + + if (!params.success) return { notFound: true }; + + return { + props: { + status: params.data.status, + trpcState: ssg.dehydrate(), + }, + }; +}; diff --git a/apps/web/pages/booking/[uid].tsx b/apps/web/pages/booking/[uid].tsx index 0e40412a621686..7613047aaabfb0 100644 --- a/apps/web/pages/booking/[uid].tsx +++ b/apps/web/pages/booking/[uid].tsx @@ -88,9 +88,7 @@ const useBrandColors = ({ useCalcomTheme(brandTheme); }; -type SuccessProps = inferSSRProps; - -export default function Success(props: SuccessProps) { +export default function Success(props: PageProps) { const { t } = useLocale(); const router = useRouter(); const routerQuery = useRouterQuery(); @@ -829,8 +827,8 @@ Success.isBookingPage = true; Success.PageWrapper = PageWrapper; type RecurringBookingsProps = { - eventType: SuccessProps["eventType"]; - recurringBookings: SuccessProps["recurringBookings"]; + eventType: PageProps["eventType"]; + recurringBookings: PageProps["recurringBookings"]; date: dayjs.Dayjs; duration: number | undefined; is24h: boolean; diff --git a/apps/web/pages/bookings/[status].tsx b/apps/web/pages/bookings/[status].tsx index 878d34462087bb..5146f6d1a190a6 100644 --- a/apps/web/pages/bookings/[status].tsx +++ b/apps/web/pages/bookings/[status].tsx @@ -1,7 +1,7 @@ "use client"; import { useAutoAnimate } from "@formkit/auto-animate/react"; -import type { GetStaticPaths, GetStaticProps } from "next"; +import type { GetStaticPaths } from "next"; import { Fragment, useState } from "react"; import React from "react"; import { z } from "zod"; @@ -23,6 +23,7 @@ import type { VerticalTabItemProps, HorizontalTabItemProps } from "@calcom/ui"; import { Alert, Button, EmptyScreen } from "@calcom/ui"; import { Calendar } from "@calcom/ui/components/icon"; +import { getStaticProps } from "@lib/bookings/[status]/getStaticProps"; import { useInViewObserver } from "@lib/hooks/useInViewObserver"; import useMeQuery from "@lib/hooks/useMeQuery"; @@ -30,8 +31,6 @@ import PageWrapper from "@components/PageWrapper"; import BookingListItem from "@components/booking/BookingListItem"; import SkeletonLoader from "@components/booking/SkeletonLoader"; -import { ssgInit } from "@server/lib/ssg"; - type BookingListingStatus = z.infer>["status"]; type BookingOutput = RouterOutputs["viewer"]["bookings"]["get"]["bookings"][0]; @@ -260,20 +259,6 @@ export default function Bookings() { Bookings.PageWrapper = PageWrapper; Bookings.getLayout = getLayout; -export const getStaticProps: GetStaticProps = async (ctx) => { - const params = querySchema.safeParse(ctx.params); - const ssg = await ssgInit(ctx); - - if (!params.success) return { notFound: true }; - - return { - props: { - status: params.data.status, - trpcState: ssg.dehydrate(), - }, - }; -}; - export const getStaticPaths: GetStaticPaths = () => { return { paths: validStatuses.map((status) => ({ @@ -283,3 +268,5 @@ export const getStaticPaths: GetStaticPaths = () => { fallback: "blocking", }; }; + +export { getStaticProps };