diff --git a/apps/web/components/booking/BookingListItem.tsx b/apps/web/components/booking/BookingListItem.tsx index 48692cf6e8..dac6a6d7a1 100644 --- a/apps/web/components/booking/BookingListItem.tsx +++ b/apps/web/components/booking/BookingListItem.tsx @@ -185,26 +185,13 @@ function BookingListItem(booking: BookingItemProps) { .concat(booking.recurringInfo?.bookings[BookingStatus.PENDING]) .sort((date1: Date, date2: Date) => date1.getTime() - date2.getTime()); - const location = booking.location || ""; - const onClickTableData = () => { router.push({ pathname: "/success", query: { - date: booking.startTime, - // TODO: Booking when fetched should have id 0 already(for Dynamic Events). - type: booking.eventType.id || 0, - eventSlug: booking.eventType.slug, - username: user?.username || "", - name: booking.attendees[0] ? booking.attendees[0].name : undefined, - email: booking.attendees[0] ? booking.attendees[0].email : undefined, - location: location, - eventName: booking.eventType.eventName || "", - bookingId: booking.id, - recur: booking.recurringEventId, - reschedule: isConfirmed, + uid: booking.uid, listingStatus: booking.listingStatus, - status: booking.status, + email: booking.attendees[0] ? booking.attendees[0].email : undefined, }, }); }; diff --git a/apps/web/components/booking/pages/BookingPage.tsx b/apps/web/components/booking/pages/BookingPage.tsx index 944786dc38..72bee51acc 100644 --- a/apps/web/components/booking/pages/BookingPage.tsx +++ b/apps/web/components/booking/pages/BookingPage.tsx @@ -122,7 +122,7 @@ const BookingPage = ({ const mutation = useMutation(createBooking, { onSuccess: async (responseData) => { - const { id, paymentUid } = responseData; + const { uid, paymentUid } = responseData; if (paymentUid) { return await router.push( createPaymentLink({ @@ -138,17 +138,10 @@ const BookingPage = ({ return router.push({ pathname: "/success", query: { - date, - type: eventType.id, - eventSlug: eventType.slug, - username: profile.slug, - reschedule: !!rescheduleUid, - name: bookingForm.getValues("name"), - email: bookingForm.getValues("email"), - location: responseData.location, - eventName: profile.eventName || "", - bookingId: id, + uid, isSuccessBookingPage: true, + email: bookingForm.getValues("email"), + eventTypeSlug: eventType.slug, }, }); }, @@ -156,31 +149,14 @@ const BookingPage = ({ const recurringMutation = useMutation(createRecurringBooking, { onSuccess: async (responseData = []) => { - const { attendees = [], id, recurringEventId } = responseData[0] || {}; - const location = (function humanReadableLocation(location) { - if (!location) { - return; - } - if (location.includes("integration")) { - return t("web_conferencing_details_to_follow"); - } - return location; - })(responseData[0].location); + const { uid } = responseData[0] || {}; return router.push({ pathname: "/success", query: { - date, - type: eventType.id, - eventSlug: eventType.slug, - recur: recurringEventId, - username: profile.slug, - reschedule: !!rescheduleUid, - name: attendees[0].name, - email: attendees[0].email, - location, - eventName: profile.eventName || "", - bookingId: id, + uid, + email: bookingForm.getValues("email"), + eventTypeSlug: eventType.slug, }, }); }, diff --git a/apps/web/pages/success.tsx b/apps/web/pages/success.tsx index 47858b6190..4156a061dc 100644 --- a/apps/web/pages/success.tsx +++ b/apps/web/pages/success.tsx @@ -1,4 +1,4 @@ -import { Prisma } from "@prisma/client"; +import { BookingStatus } from "@prisma/client"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible"; import classNames from "classnames"; import { createEvent } from "ics"; @@ -33,6 +33,7 @@ import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calco import { getIs24hClockFromLocalStorage, isBrowserLocale24h } from "@calcom/lib/timeFormat"; import { localStorage } from "@calcom/lib/webstorage"; import prisma, { baseUserSelect } from "@calcom/prisma"; +import { Prisma } from "@calcom/prisma/client"; import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import Button from "@calcom/ui/Button"; import { Icon } from "@calcom/ui/Icon"; @@ -144,28 +145,26 @@ type SuccessProps = inferSSRProps; export default function Success(props: SuccessProps) { const { t } = useLocale(); const router = useRouter(); - const { - location: _location, - name, - email, - reschedule, - listingStatus, - status, - isSuccessBookingPage, - } = router.query; - const location: ReturnType = Array.isArray(_location) - ? _location[0] || "" - : _location || ""; + const { listingStatus, isSuccessBookingPage } = router.query; + + const location: ReturnType = Array.isArray(props.bookingInfo.location) + ? props.bookingInfo.location[0] || "" + : props.bookingInfo.location || ""; if (!location) { // Can't use logger.error because it throws error on client. stdout isn't available to it. console.error(`No location found `); } + const name = props.bookingInfo?.user?.name; + const email = props.bookingInfo?.user?.email; + const status = props.bookingInfo?.status; + const reschedule = props.bookingInfo.status === BookingStatus.ACCEPTED; + const [is24h, setIs24h] = useState(isBrowserLocale24h()); const { data: session } = useSession(); - const [date, setDate] = useState(dayjs.utc(asStringOrThrow(router.query.date))); + const [date, setDate] = useState(dayjs.utc(props.bookingInfo.startTime)); const { eventType, bookingInfo } = props; const isBackgroundTransparent = useIsBackgroundTransparent(); @@ -189,7 +188,7 @@ export default function Success(props: SuccessProps) { const giphyImage = giphyAppData?.thankYouPage; const eventName = getEventName(eventNameObject, true); - const needsConfirmation = eventType.requiresConfirmation && reschedule != "true"; + const needsConfirmation = eventType.requiresConfirmation && reschedule != true; const isCancelled = status === "CANCELLED" || status === "REJECTED"; const telemetry = useTelemetry(); useEffect(() => { @@ -596,7 +595,7 @@ export default function Success(props: SuccessProps) { @@ -740,6 +739,8 @@ const getEventTypesFromDB = async (id: number) => { metadata: true, seatsPerTimeSlot: true, seatsShowAttendees: true, + periodStartDate: true, + periodEndDate: true, }, }); @@ -756,24 +757,10 @@ const getEventTypesFromDB = async (id: number) => { }; }; -const strToNumber = z.string().transform((val, ctx) => { - const parsed = parseInt(val); - if (isNaN(parsed)) ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Not a number" }); - return parsed; -}); - const schema = z.object({ - type: strToNumber, - date: z.string().optional(), - username: z.string().optional(), - reschedule: z.string().optional(), - name: z.string().optional(), + uid: z.string(), email: z.string().optional(), - recur: z.string().optional(), - location: z.string().optional(), - eventSlug: z.string().default("15min"), - eventName: z.string().default(""), - bookingId: strToNumber, + eventTypeSlug: z.string().optional(), }); const handleSeatsEventTypeOnBooking = ( @@ -804,18 +791,60 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { const ssr = await ssrInit(context); const parsedQuery = schema.safeParse(context.query); if (!parsedQuery.success) return { notFound: true }; - const { - type: eventTypeId, - recur: recurringEventIdQuery, - eventSlug: eventTypeSlug, - eventName: dynamicEventName, - bookingId, - username, - name, - email, - } = parsedQuery.data; + const { uid, email, eventTypeSlug } = parsedQuery.data; - const eventTypeRaw = !eventTypeId ? getDefaultEvent(eventTypeSlug) : await getEventTypesFromDB(eventTypeId); + const bookingInfo = await prisma.booking.findFirst({ + where: { + uid, + }, + select: { + title: true, + id: true, + uid: true, + description: true, + customInputs: true, + smsReminderNumber: true, + recurringEventId: true, + startTime: true, + location: true, + status: true, + user: { + select: { + id: true, + name: true, + email: true, + username: true, + }, + }, + attendees: { + select: { + name: true, + email: true, + }, + }, + eventTypeId: true, + eventType: { + select: { + eventName: true, + slug: true, + }, + }, + }, + }); + + if (!bookingInfo) { + return { + notFound: true, + }; + } + + // @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; + + const eventTypeRaw = !bookingInfo.eventTypeId + ? getDefaultEvent(eventTypeSlug || "") + : await getEventTypesFromDB(bookingInfo.eventTypeId); if (!eventTypeRaw) { return { notFound: true, @@ -843,6 +872,8 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { const eventType = { ...eventTypeRaw, + periodStartDate: eventTypeRaw.periodStartDate?.toString() ?? null, + periodEndDate: eventTypeRaw.periodEndDate?.toString() ?? null, metadata: EventTypeMetaDataSchema.parse(eventTypeRaw.metadata), recurringEvent: parseRecurringEvent(eventTypeRaw.recurringEvent), }; @@ -856,59 +887,16 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { slug: eventType.team?.slug || eventType.users[0]?.username || null, }; - const where: Prisma.BookingWhereInput = { - id: bookingId, - attendees: { some: { email, name } }, - }; - // Dynamic Event uses EventType from @calcom/lib/defaultEvents(a fake EventType) which doesn't have a real user/team/eventTypeId - // So, you can't look them up in DB. - if (!eventType.isDynamic) { - // A Team Event doesn't have a correct user query param as of now. It is equal to team/{eventSlug} which is not a user, so you can't look it up in DB. - if (!eventType.team) { - // username being equal to profile.slug isn't applicable for Team or Dynamic Events. - where.user = { username }; - } - where.eventTypeId = eventType.id; - } else { - // username being equal to eventSlug for Dynamic Event Booking, it can't be used for user lookup. So, just use eventTypeId which would always be null for Dynamic Event Bookings - where.eventTypeId = null; - } - - const bookingInfo = await prisma.booking.findFirst({ - where, - select: { - title: true, - id: true, - uid: true, - description: true, - customInputs: true, - smsReminderNumber: true, - user: { - select: { - id: true, - name: true, - email: true, - }, - }, - attendees: { - select: { - name: true, - email: true, - }, - }, - }, - }); - if (bookingInfo !== null && email) { handleSeatsEventTypeOnBooking(eventType, bookingInfo, email); } let recurringBookings = null; - if (recurringEventIdQuery) { + if (bookingInfo.recurringEventId) { // We need to get the dates for the bookings to be able to show them in the UI recurringBookings = await prisma.booking.findMany({ where: { - recurringEventId: recurringEventIdQuery, + recurringEventId: bookingInfo.recurringEventId, }, select: { startTime: true, @@ -923,7 +911,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { eventType, recurringBookings: recurringBookings ? recurringBookings.map((obj) => obj.startTime.toString()) : null, trpcState: ssr.dehydrate(), - dynamicEventName, + dynamicEventName: bookingInfo?.eventType?.eventName || "", bookingInfo, }, }; diff --git a/apps/web/playwright/booking-pages.e2e.ts b/apps/web/playwright/booking-pages.e2e.ts index a20318e3e5..c4ce528c86 100644 --- a/apps/web/playwright/booking-pages.e2e.ts +++ b/apps/web/playwright/booking-pages.e2e.ts @@ -92,7 +92,7 @@ test.describe("pro user", () => { await page.locator('[data-testid="confirm-reschedule-button"]').click(); await page.waitForNavigation({ url(url) { - return url.pathname === "/success" && url.searchParams.get("reschedule") === "true"; + return url.pathname === "/success"; }, }); }); diff --git a/apps/web/playwright/dynamic-booking-pages.e2e.ts b/apps/web/playwright/dynamic-booking-pages.e2e.ts index 5a63b406f1..0fae82a8f7 100644 --- a/apps/web/playwright/dynamic-booking-pages.e2e.ts +++ b/apps/web/playwright/dynamic-booking-pages.e2e.ts @@ -37,7 +37,7 @@ test("dynamic booking", async ({ page, users }) => { await page.locator('[data-testid="confirm-reschedule-button"]').click(); await page.waitForNavigation({ url(url) { - return url.pathname === "/success" && url.searchParams.get("reschedule") === "true"; + return url.pathname === "/success"; }, }); await expect(page.locator("[data-testid=success-page]")).toBeVisible(); diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index e2ee7e1eb9..0d039ff4be 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -510,6 +510,7 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) { uid: reqBody.bookingUid, }, select: { + uid: true, id: true, attendees: true, userId: true, diff --git a/packages/features/ee/payments/components/Payment.tsx b/packages/features/ee/payments/components/Payment.tsx index 72c44f5dfd..4f0f0ed27f 100644 --- a/packages/features/ee/payments/components/Payment.tsx +++ b/packages/features/ee/payments/components/Payment.tsx @@ -36,6 +36,7 @@ type Props = { user: { username: string | null }; location?: string | null; bookingId: number; + bookingUid: string; }; type States = @@ -47,7 +48,6 @@ type States = export default function PaymentComponent(props: Props) { const { t, i18n } = useLocale(); const router = useRouter(); - const { email, name, date } = router.query; const [state, setState] = useState({ status: "idle" }); const stripe = useStripe(); const elements = useElements(); @@ -83,12 +83,7 @@ export default function PaymentComponent(props: Props) { }); } else { const params: { [k: string]: any } = { - date, - type: props.eventType.id, - username: props.user.username, - email, - name, - bookingId: props.bookingId, + uid: props.bookingUid, }; if (props.location) { diff --git a/packages/features/ee/payments/components/PaymentPage.tsx b/packages/features/ee/payments/components/PaymentPage.tsx index 75db3e9e39..119bd61890 100644 --- a/packages/features/ee/payments/components/PaymentPage.tsx +++ b/packages/features/ee/payments/components/PaymentPage.tsx @@ -136,6 +136,7 @@ const PaymentPage: FC = (props) => { user={props.user} location={props.booking.location} bookingId={props.booking.id} + bookingUid={props.booking.uid} /> )} diff --git a/packages/features/ee/payments/pages/payment.tsx b/packages/features/ee/payments/pages/payment.tsx index d9f45ad0d8..03a5c819b8 100644 --- a/packages/features/ee/payments/pages/payment.tsx +++ b/packages/features/ee/payments/pages/payment.tsx @@ -31,6 +31,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => booking: { select: { id: true, + uid: true, description: true, title: true, startTime: true,