import type { GetServerSidePropsContext } from "next"; import { z } from "zod"; import type { LocationObject } from "@calcom/app-store/locations"; import { privacyFilteredLocations } from "@calcom/app-store/locations"; import getBooking from "@calcom/features/bookings/lib/get-booking"; import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking"; import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields"; import { parseRecurringEvent } from "@calcom/lib"; import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import prisma from "@calcom/prisma"; import { customInputSchema, eventTypeBookingFields, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import { asStringOrNull, asStringOrThrow } from "@lib/asStringOrNull"; import type { inferSSRProps } from "@lib/types/inferSSRProps"; import PageWrapper from "@components/PageWrapper"; import BookingPage from "@components/booking/pages/BookingPage"; import { ssrInit } from "@server/lib/ssr"; export type TeamBookingPageProps = inferSSRProps; export default function TeamBookingPage(props: TeamBookingPageProps) { return ; } TeamBookingPage.isBookingPage = true; TeamBookingPage.PageWrapper = PageWrapper; const querySchema = z.object({ rescheduleUid: z.string().optional(), bookingUid: z.string().optional(), }); export async function getServerSideProps(context: GetServerSidePropsContext) { const ssr = await ssrInit(context); const eventTypeId = parseInt(asStringOrThrow(context.query.type)); const recurringEventCountQuery = asStringOrNull(context.query.count); if (typeof eventTypeId !== "number" || eventTypeId % 1 !== 0) { return { notFound: true, } as const; } const eventTypeRaw = await prisma.eventType.findUnique({ where: { id: eventTypeId, }, select: { id: true, title: true, slug: true, description: true, length: true, locations: true, customInputs: true, periodType: true, periodDays: true, periodStartDate: true, periodEndDate: true, periodCountCalendarDays: true, recurringEvent: true, requiresConfirmation: true, disableGuests: true, price: true, currency: true, metadata: true, seatsPerTimeSlot: true, schedulingType: true, bookingFields: true, successRedirectUrl: true, workflows: { include: { workflow: { include: { steps: true, }, }, }, }, team: { select: { slug: true, name: true, logo: true, theme: true, brandColor: true, darkBrandColor: true, }, }, users: { select: { id: true, username: true, avatar: true, name: true, }, }, }, }); if (!eventTypeRaw) return { notFound: true }; const eventType = { ...eventTypeRaw, //TODO: Use zodSchema to verify it instead of using Type Assertion locations: privacyFilteredLocations((eventTypeRaw.locations || []) as LocationObject[]), recurringEvent: parseRecurringEvent(eventTypeRaw.recurringEvent), bookingFields: eventTypeBookingFields.parse(eventTypeRaw.bookingFields || []), }; const eventTypeObject = [eventType].map((e) => { return { ...e, metadata: EventTypeMetaDataSchema.parse(e.metadata || {}), bookingFields: getBookingFieldsWithSystemFields(eventType), periodStartDate: e.periodStartDate?.toString() ?? null, periodEndDate: e.periodEndDate?.toString() ?? null, customInputs: customInputSchema.array().parse(e.customInputs || []), users: eventType.users.map((u) => ({ id: u.id, name: u.name, username: u.username, avatar: u.avatar, image: u.avatar, slug: u.username, })), descriptionAsSafeHTML: markdownToSafeHTML(eventType.description), }; })[0]; let booking: GetBookingType | null = null; const { rescheduleUid, bookingUid } = querySchema.parse(context.query); if (rescheduleUid || bookingUid) { booking = await getBooking(prisma, rescheduleUid || bookingUid || ""); } // Checking if number of recurring event ocurrances is valid against event type configuration const recurringEventCount = (eventType.recurringEvent?.count && recurringEventCountQuery && (parseInt(recurringEventCountQuery) <= eventType.recurringEvent.count ? parseInt(recurringEventCountQuery) : eventType.recurringEvent.count)) || null; return { props: { trpcState: ssr.dehydrate(), profile: { ...eventTypeObject.team, // FIXME: This slug is used as username on success page which is wrong. This is correctly set as username for user booking. slug: "team/" + eventTypeObject.slug, image: eventTypeObject.team?.logo || null, eventName: null, }, themeBasis: eventTypeObject.team?.slug, eventType: eventTypeObject, recurringEventCount, booking, currentSlotBooking: null, isDynamicGroupBooking: false, hasHashedBookingLink: false, hashedLink: null, isEmbed: typeof context.query.embed === "string", }, }; }