fix: seated event custom questions (#12779)
* fix: seated event custom questions * updating tests & code refactor * addition of prisma custom json schema and type fixes * attendeeName parsing refactor * response parsing fix * response parsing fix --------- Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Co-authored-by: Keith Williams <keithwillcode@gmail.com>
This commit is contained in:
14 files changed
+186
-87
No files matched your search
+33
-15
@@ -1,4 +1,5 @@
|
||||
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
|
||||
import { bookingResponsesDbSchema } from "@calcom/features/bookings/lib/getBookingResponsesSchema";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type { Prisma } from "@calcom/prisma/client";
|
||||
import { BookingStatus } from "@calcom/prisma/enums";
|
||||
@@ -115,21 +116,23 @@ export const handleSeatsEventTypeOnBooking = async (
|
||||
}>
|
||||
>,
|
||||
seatReferenceUid?: string,
|
||||
userId?: number
|
||||
isHost?: boolean
|
||||
) => {
|
||||
if (eventType?.seatsPerTimeSlot !== null) {
|
||||
// @TODO: right now bookings with seats doesn't save every description that its entered by every user
|
||||
delete bookingInfo.description;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// @TODO: If handling teams, we need to do more check ups for this.
|
||||
if (bookingInfo?.user?.id === userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eventType.seatsShowAttendees) {
|
||||
const seatAttendee = await prisma.bookingSeat.findFirst({
|
||||
bookingInfo["responses"] = {};
|
||||
type seatAttendee = {
|
||||
attendee: {
|
||||
email: string;
|
||||
name: string;
|
||||
};
|
||||
id: number;
|
||||
data: Prisma.JsonValue;
|
||||
bookingId: number;
|
||||
attendeeId: number;
|
||||
referenceUid: string;
|
||||
} | null;
|
||||
let seatAttendee: seatAttendee = null;
|
||||
if (seatReferenceUid) {
|
||||
seatAttendee = await prisma.bookingSeat.findFirst({
|
||||
where: {
|
||||
referenceUid: seatReferenceUid,
|
||||
},
|
||||
@@ -142,16 +145,31 @@ export const handleSeatsEventTypeOnBooking = async (
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
if (seatAttendee) {
|
||||
const seatAttendeeData = seatAttendee.data as unknown as {
|
||||
description?: string;
|
||||
responses: Prisma.JsonValue;
|
||||
};
|
||||
bookingInfo["description"] = seatAttendeeData.description ?? null;
|
||||
bookingInfo["responses"] = bookingResponsesDbSchema.parse(seatAttendeeData.responses ?? {});
|
||||
}
|
||||
|
||||
if (!eventType.seatsShowAttendees && !isHost) {
|
||||
if (seatAttendee) {
|
||||
const attendee = bookingInfo?.attendees?.find((a) => {
|
||||
return a.email === seatAttendee.attendee?.email;
|
||||
return a.email === seatAttendee?.attendee?.email;
|
||||
});
|
||||
bookingInfo["attendees"] = attendee ? [attendee] : [];
|
||||
} else {
|
||||
bookingInfo["attendees"] = [];
|
||||
}
|
||||
}
|
||||
|
||||
// // @TODO: If handling teams, we need to do more check ups for this.
|
||||
// if (bookingInfo?.user?.id === userId) {
|
||||
// return;
|
||||
// }
|
||||
return bookingInfo;
|
||||
};
|
||||
|
||||
|
||||
@@ -56,12 +56,15 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
const parsedQuery = querySchema.safeParse(context.query);
|
||||
|
||||
if (!parsedQuery.success) return { notFound: true } as const;
|
||||
const { uid, eventTypeSlug, seatReferenceUid } = parsedQuery.data;
|
||||
const { eventTypeSlug } = parsedQuery.data;
|
||||
let { uid, seatReferenceUid } = parsedQuery.data;
|
||||
|
||||
const { uid: maybeUid } = await maybeGetBookingUidFromSeat(prisma, uid);
|
||||
const maybeBookingUidFromSeat = await maybeGetBookingUidFromSeat(prisma, uid);
|
||||
if (maybeBookingUidFromSeat.uid) uid = maybeBookingUidFromSeat.uid;
|
||||
if (maybeBookingUidFromSeat.seatReferenceUid) seatReferenceUid = maybeBookingUidFromSeat.seatReferenceUid;
|
||||
const bookingInfoRaw = await prisma.booking.findFirst({
|
||||
where: {
|
||||
uid: maybeUid,
|
||||
uid: uid,
|
||||
},
|
||||
select: {
|
||||
title: true,
|
||||
@@ -168,7 +171,12 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
};
|
||||
|
||||
if (bookingInfo !== null && eventType.seatsPerTimeSlot) {
|
||||
await handleSeatsEventTypeOnBooking(eventType, bookingInfo, seatReferenceUid, session?.user.id);
|
||||
await handleSeatsEventTypeOnBooking(
|
||||
eventType,
|
||||
bookingInfo,
|
||||
seatReferenceUid,
|
||||
session?.user.id === eventType.userId
|
||||
);
|
||||
}
|
||||
|
||||
const payment = await prisma.payment.findFirst({
|
||||
|
||||
@@ -14,6 +14,7 @@ import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
|
||||
import type { getEventLocationValue } from "@calcom/app-store/locations";
|
||||
import { getSuccessPageLocationMessage, guessEventLocationType } from "@calcom/app-store/locations";
|
||||
import { getEventTypeAppData } from "@calcom/app-store/utils";
|
||||
import type { nameObjectSchema } from "@calcom/core/event";
|
||||
import { getEventName } from "@calcom/core/event";
|
||||
import type { ConfigType } from "@calcom/dayjs";
|
||||
import dayjs from "@calcom/dayjs";
|
||||
@@ -90,6 +91,7 @@ export default function Success(props: PageProps) {
|
||||
const routerQuery = useRouterQuery();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useCompatSearchParams();
|
||||
const { eventType, bookingInfo, requiresLoginToUpdate } = props;
|
||||
const {
|
||||
allRemainingBookings,
|
||||
isSuccessBookingPage,
|
||||
@@ -99,35 +101,28 @@ export default function Success(props: PageProps) {
|
||||
seatReferenceUid,
|
||||
} = querySchema.parse(routerQuery);
|
||||
|
||||
const attendeeTimeZone = props?.bookingInfo?.attendees.find(
|
||||
(attendee) => attendee.email === email
|
||||
)?.timeZone;
|
||||
const attendeeTimeZone = bookingInfo?.attendees.find((attendee) => attendee.email === email)?.timeZone;
|
||||
|
||||
const tz = props.tz ? props.tz : isSuccessBookingPage && attendeeTimeZone ? attendeeTimeZone : timeZone();
|
||||
|
||||
const location = props.bookingInfo.location as ReturnType<typeof getEventLocationValue>;
|
||||
const location = bookingInfo.location as ReturnType<typeof getEventLocationValue>;
|
||||
let rescheduleLocation: string | undefined;
|
||||
if (
|
||||
typeof props.bookingInfo.responses.location === "object" &&
|
||||
"optionValue" in props.bookingInfo.responses.location
|
||||
typeof bookingInfo.responses?.location === "object" &&
|
||||
"optionValue" in bookingInfo.responses.location
|
||||
) {
|
||||
rescheduleLocation = props.bookingInfo.responses.location.optionValue;
|
||||
rescheduleLocation = bookingInfo.responses.location.optionValue;
|
||||
}
|
||||
|
||||
const locationVideoCallUrl: string | undefined = bookingMetadataSchema.parse(
|
||||
props?.bookingInfo?.metadata || {}
|
||||
bookingInfo?.metadata || {}
|
||||
)?.videoCallUrl;
|
||||
|
||||
const status = props.bookingInfo?.status;
|
||||
const reschedule = props.bookingInfo.status === BookingStatus.ACCEPTED;
|
||||
const cancellationReason = props.bookingInfo.cancellationReason || props.bookingInfo.rejectionReason;
|
||||
const status = bookingInfo?.status;
|
||||
const reschedule = bookingInfo.status === BookingStatus.ACCEPTED;
|
||||
const cancellationReason = bookingInfo.cancellationReason || bookingInfo.rejectionReason;
|
||||
|
||||
const attendeeName =
|
||||
typeof props?.bookingInfo?.attendees?.[0]?.name === "string"
|
||||
? props?.bookingInfo?.attendees?.[0]?.name
|
||||
: "Nameless";
|
||||
|
||||
const attendees = props?.bookingInfo?.attendees;
|
||||
const attendees = bookingInfo?.attendees;
|
||||
|
||||
const isGmail = !!attendees.find((attendee) => attendee.email.includes("gmail.com"));
|
||||
|
||||
@@ -136,15 +131,14 @@ export default function Success(props: PageProps) {
|
||||
);
|
||||
const { data: session } = useSession();
|
||||
|
||||
const [date, setDate] = useState(dayjs.utc(props.bookingInfo.startTime));
|
||||
const { eventType, bookingInfo } = props;
|
||||
const [date, setDate] = useState(dayjs.utc(bookingInfo.startTime));
|
||||
|
||||
const isBackgroundTransparent = useIsBackgroundTransparent();
|
||||
const isEmbed = useIsEmbed();
|
||||
const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left";
|
||||
const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed;
|
||||
const [calculatedDuration, setCalculatedDuration] = useState<number | undefined>(undefined);
|
||||
const { requiresLoginToUpdate } = props;
|
||||
|
||||
function setIsCancellationMode(value: boolean) {
|
||||
const _searchParams = new URLSearchParams(searchParams ?? undefined);
|
||||
|
||||
@@ -159,13 +153,13 @@ export default function Success(props: PageProps) {
|
||||
router.replace(`${pathname}?${_searchParams.toString()}`);
|
||||
}
|
||||
|
||||
let evtName = props.eventType.eventName;
|
||||
let evtName = eventType.eventName;
|
||||
if (eventType.isDynamic && bookingInfo.responses?.title) {
|
||||
evtName = bookingInfo.responses.title as string;
|
||||
}
|
||||
const eventNameObject = {
|
||||
attendeeName,
|
||||
eventType: props.eventType.title,
|
||||
attendeeName: bookingInfo.responses.name as z.infer<typeof nameObjectSchema> | string,
|
||||
eventType: eventType.title,
|
||||
eventName: evtName,
|
||||
host: props.profile.name || "Nameless",
|
||||
location: location,
|
||||
@@ -223,9 +217,7 @@ export default function Success(props: PageProps) {
|
||||
}, [eventType, needsConfirmation]);
|
||||
|
||||
useEffect(() => {
|
||||
setCalculatedDuration(
|
||||
dayjs(props.bookingInfo.endTime).diff(dayjs(props.bookingInfo.startTime), "minutes")
|
||||
);
|
||||
setCalculatedDuration(dayjs(bookingInfo.endTime).diff(dayjs(bookingInfo.startTime), "minutes"));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
@@ -245,7 +237,7 @@ export default function Success(props: PageProps) {
|
||||
],
|
||||
startInputType: "utc",
|
||||
title: eventName,
|
||||
description: props.eventType.description ? props.eventType.description : undefined,
|
||||
description: eventType.description ? eventType.description : undefined,
|
||||
/** formatted to required type of description ^ */
|
||||
duration: {
|
||||
minutes: calculatedDuration,
|
||||
@@ -424,7 +416,7 @@ export default function Success(props: PageProps) {
|
||||
{reschedule && !!formerTime && (
|
||||
<p className="line-through">
|
||||
<RecurringBookings
|
||||
eventType={props.eventType}
|
||||
eventType={eventType}
|
||||
duration={calculatedDuration}
|
||||
recurringBookings={props.recurringBookings}
|
||||
allRemainingBookings={allRemainingBookings}
|
||||
@@ -436,7 +428,7 @@ export default function Success(props: PageProps) {
|
||||
</p>
|
||||
)}
|
||||
<RecurringBookings
|
||||
eventType={props.eventType}
|
||||
eventType={eventType}
|
||||
duration={calculatedDuration}
|
||||
recurringBookings={props.recurringBookings}
|
||||
allRemainingBookings={allRemainingBookings}
|
||||
@@ -638,15 +630,15 @@ export default function Success(props: PageProps) {
|
||||
.add(calculatedDuration, "minute")
|
||||
.utc()
|
||||
.format("YYYYMMDDTHHmmss[Z]")}&text=${eventName}&details=${
|
||||
props.eventType.description
|
||||
eventType.description
|
||||
}${
|
||||
typeof locationVideoCallUrl === "string"
|
||||
? `&location=${encodeURIComponent(locationVideoCallUrl)}`
|
||||
: ""
|
||||
}${
|
||||
props.eventType.recurringEvent
|
||||
eventType.recurringEvent
|
||||
? `&recur=${encodeURIComponent(
|
||||
new RRule(props.eventType.recurringEvent).toString()
|
||||
new RRule(eventType.recurringEvent).toString()
|
||||
)}`
|
||||
: ""
|
||||
}`}
|
||||
@@ -664,7 +656,7 @@ export default function Success(props: PageProps) {
|
||||
href={
|
||||
encodeURI(
|
||||
`https://outlook.live.com/calendar/0/deeplink/compose?body=${
|
||||
props.eventType.description
|
||||
eventType.description
|
||||
}&enddt=${date
|
||||
.add(calculatedDuration, "minute")
|
||||
.utc()
|
||||
@@ -691,7 +683,7 @@ export default function Success(props: PageProps) {
|
||||
href={
|
||||
encodeURI(
|
||||
`https://outlook.office.com/calendar/0/deeplink/compose?body=${
|
||||
props.eventType.description
|
||||
eventType.description
|
||||
}&enddt=${date
|
||||
.add(calculatedDuration, "minute")
|
||||
.utc()
|
||||
@@ -717,7 +709,7 @@ export default function Success(props: PageProps) {
|
||||
<Link
|
||||
href={`data:text/calendar,${eventLink()}`}
|
||||
className="border-subtle text-default mx-2 h-10 w-10 rounded-sm border px-3 py-2"
|
||||
download={`${props.eventType.title}.ics`}>
|
||||
download={`${eventType.title}.ics`}>
|
||||
<svg
|
||||
version="1.1"
|
||||
fill="currentColor"
|
||||
|
||||
@@ -38,14 +38,22 @@ test.describe("Booking with Seats", () => {
|
||||
where: { bookingId: booking.id },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
});
|
||||
|
||||
const bookingSeats = [
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[0].id, referenceUid: uuidv4() },
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[1].id, referenceUid: uuidv4() },
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[2].id, referenceUid: uuidv4() },
|
||||
];
|
||||
const bookingSeats = bookingAttendees.map((attendee) => ({
|
||||
bookingId: booking.id,
|
||||
attendeeId: attendee.id,
|
||||
referenceUid: uuidv4(),
|
||||
data: {
|
||||
responses: {
|
||||
name: attendee.name,
|
||||
email: attendee.email,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
await prisma.bookingSeat.createMany({
|
||||
data: bookingSeats,
|
||||
@@ -122,13 +130,22 @@ test.describe("Reschedule for booking with seats", () => {
|
||||
where: { bookingId: booking.id },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
});
|
||||
|
||||
const bookingSeats = [
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[0].id, referenceUid: uuidv4() },
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[1].id, referenceUid: uuidv4() },
|
||||
];
|
||||
const bookingSeats = bookingAttendees.map((attendee) => ({
|
||||
bookingId: booking.id,
|
||||
attendeeId: attendee.id,
|
||||
referenceUid: uuidv4(),
|
||||
data: {
|
||||
responses: {
|
||||
name: attendee.name,
|
||||
email: attendee.email,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
await prisma.bookingSeat.createMany({
|
||||
data: bookingSeats,
|
||||
@@ -181,13 +198,22 @@ test.describe("Reschedule for booking with seats", () => {
|
||||
where: { bookingId: booking.id },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
});
|
||||
|
||||
const bookingSeats = [
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[0].id, referenceUid: uuidv4() },
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[1].id, referenceUid: uuidv4() },
|
||||
];
|
||||
const bookingSeats = bookingAttendees.map((attendee) => ({
|
||||
bookingId: booking.id,
|
||||
attendeeId: attendee.id,
|
||||
referenceUid: uuidv4(),
|
||||
data: {
|
||||
responses: {
|
||||
name: attendee.name,
|
||||
email: attendee.email,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
await prisma.bookingSeat.createMany({
|
||||
data: bookingSeats,
|
||||
@@ -248,13 +274,22 @@ test.describe("Reschedule for booking with seats", () => {
|
||||
where: { bookingId: booking.id },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
});
|
||||
|
||||
const bookingSeats = [
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[0].id, referenceUid: uuidv4() },
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[1].id, referenceUid: uuidv4() },
|
||||
];
|
||||
const bookingSeats = bookingAttendees.map((attendee) => ({
|
||||
bookingId: booking.id,
|
||||
attendeeId: attendee.id,
|
||||
referenceUid: uuidv4(),
|
||||
data: {
|
||||
responses: {
|
||||
name: attendee.name,
|
||||
email: attendee.email,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
await prisma.bookingSeat.createMany({
|
||||
data: bookingSeats,
|
||||
@@ -350,7 +385,7 @@ test.describe("Reschedule for booking with seats", () => {
|
||||
// data displayed in form should be user owner
|
||||
const nameElement = await page.locator("input[name=name]");
|
||||
const name = await nameElement.inputValue();
|
||||
expect(name).toBe(user.username);
|
||||
expect(name).toBe(user.name);
|
||||
|
||||
//same for email
|
||||
const emailElement = await page.locator("input[name=email]");
|
||||
|
||||
@@ -293,6 +293,7 @@ export const createUserWithLimits = ({
|
||||
async function createUserWithSeatedEvent(users: Fixtures["users"]) {
|
||||
const slug = "seats";
|
||||
const user = await users.create({
|
||||
name: "Seated event user",
|
||||
eventTypes: [
|
||||
{
|
||||
title: "Seated event",
|
||||
|
||||
@@ -478,6 +478,7 @@ test.describe("Webhook tests", async () => {
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -485,6 +486,12 @@ test.describe("Webhook tests", async () => {
|
||||
bookingId: booking.id,
|
||||
attendeeId: attendee.id,
|
||||
referenceUid: uuidv4(),
|
||||
data: {
|
||||
responses: {
|
||||
name: attendee.name,
|
||||
email: attendee.email,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
await prisma.bookingSeat.createMany({
|
||||
|
||||
+21
-6
@@ -1,10 +1,23 @@
|
||||
import type { TFunction } from "next-i18next";
|
||||
import z from "zod";
|
||||
|
||||
import { guessEventLocationType } from "@calcom/app-store/locations";
|
||||
import type { Prisma } from "@calcom/prisma/client";
|
||||
|
||||
export const nameObjectSchema = z.object({
|
||||
firstName: z.string(),
|
||||
lastName: z.string().optional(),
|
||||
});
|
||||
|
||||
function parseName(name: z.infer<typeof nameObjectSchema> | string | undefined) {
|
||||
if (typeof name === "string") return name;
|
||||
else if (typeof name === "object" && nameObjectSchema.parse(name))
|
||||
return `${name.firstName} ${name.lastName}`.trim();
|
||||
else return "Nameless";
|
||||
}
|
||||
|
||||
export type EventNameObjectType = {
|
||||
attendeeName: string;
|
||||
attendeeName: z.infer<typeof nameObjectSchema> | string;
|
||||
eventType: string;
|
||||
eventName?: string | null;
|
||||
teamName?: string | null;
|
||||
@@ -15,11 +28,13 @@ export type EventNameObjectType = {
|
||||
};
|
||||
|
||||
export function getEventName(eventNameObj: EventNameObjectType, forAttendeeView = false) {
|
||||
const attendeeName = parseName(eventNameObj.attendeeName);
|
||||
|
||||
if (!eventNameObj.eventName)
|
||||
return eventNameObj.t("event_between_users", {
|
||||
eventName: eventNameObj.eventType,
|
||||
host: eventNameObj.teamName || eventNameObj.host,
|
||||
attendeeName: eventNameObj.attendeeName,
|
||||
attendeeName,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
@@ -40,12 +55,12 @@ export function getEventName(eventNameObj: EventNameObjectType, forAttendeeView
|
||||
let dynamicEventName = eventName
|
||||
// Need this for compatibility with older event names
|
||||
.replaceAll("{Event type title}", eventNameObj.eventType)
|
||||
.replaceAll("{Scheduler}", eventNameObj.attendeeName)
|
||||
.replaceAll("{Scheduler}", attendeeName)
|
||||
.replaceAll("{Organiser}", eventNameObj.host)
|
||||
.replaceAll("{USER}", eventNameObj.attendeeName)
|
||||
.replaceAll("{ATTENDEE}", eventNameObj.attendeeName)
|
||||
.replaceAll("{USER}", attendeeName)
|
||||
.replaceAll("{ATTENDEE}", attendeeName)
|
||||
.replaceAll("{HOST}", eventNameObj.host)
|
||||
.replaceAll("{HOST/ATTENDEE}", forAttendeeView ? eventNameObj.host : eventNameObj.attendeeName);
|
||||
.replaceAll("{HOST/ATTENDEE}", forAttendeeView ? eventNameObj.host : attendeeName);
|
||||
|
||||
const customInputvariables = dynamicEventName.match(/\{(.+?)}/g)?.map((variable) => {
|
||||
return variable.replace("{", "").replace("}", "");
|
||||
|
||||
@@ -45,7 +45,7 @@ function getResponsesFromOldBooking(
|
||||
};
|
||||
}
|
||||
|
||||
async function getBooking(prisma: PrismaClient, uid: string) {
|
||||
async function getBooking(prisma: PrismaClient, uid: string, isSeatedEvent?: boolean) {
|
||||
const rawBooking = await prisma.booking.findFirst({
|
||||
where: {
|
||||
uid,
|
||||
@@ -79,7 +79,7 @@ async function getBooking(prisma: PrismaClient, uid: string) {
|
||||
return rawBooking;
|
||||
}
|
||||
|
||||
const booking = getBookingWithResponses(rawBooking);
|
||||
const booking = getBookingWithResponses(rawBooking, isSeatedEvent);
|
||||
|
||||
if (booking) {
|
||||
// @NOTE: had to do this because Server side cant return [Object objects]
|
||||
@@ -99,11 +99,14 @@ export const getBookingWithResponses = <
|
||||
};
|
||||
}>
|
||||
>(
|
||||
booking: T
|
||||
booking: T,
|
||||
isSeatedEvent?: boolean
|
||||
) => {
|
||||
return {
|
||||
...booking,
|
||||
responses: bookingResponsesDbSchema.parse(booking.responses || getResponsesFromOldBooking(booking)),
|
||||
responses: isSeatedEvent
|
||||
? bookingResponsesDbSchema.parse(booking.responses || {})
|
||||
: bookingResponsesDbSchema.parse(booking.responses || getResponsesFromOldBooking(booking)),
|
||||
} as Omit<T, "responses"> & { responses: z.infer<typeof bookingResponsesDbSchema> };
|
||||
};
|
||||
|
||||
@@ -140,6 +143,7 @@ export const getBookingForReschedule = async (uid: string, userId?: number) => {
|
||||
// If no booking is found via the uid, it's probably a booking seat
|
||||
// that its being rescheduled, which we query next.
|
||||
let attendeeEmail: string | null = null;
|
||||
let bookingSeatData: { description?: string; responses: Prisma.JsonValue } | null = null;
|
||||
if (!theBooking) {
|
||||
const bookingSeat = await prisma.bookingSeat.findFirst({
|
||||
where: {
|
||||
@@ -153,6 +157,7 @@ export const getBookingForReschedule = async (uid: string, userId?: number) => {
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
data: true,
|
||||
booking: {
|
||||
select: {
|
||||
uid: true,
|
||||
@@ -161,6 +166,7 @@ export const getBookingForReschedule = async (uid: string, userId?: number) => {
|
||||
},
|
||||
});
|
||||
if (bookingSeat) {
|
||||
bookingSeatData = bookingSeat.data as unknown as { description?: string; responses: Prisma.JsonValue };
|
||||
bookingSeatReferenceUid = bookingSeat.id;
|
||||
rescheduleUid = bookingSeat.booking.uid;
|
||||
attendeeEmail = bookingSeat.attendee.email;
|
||||
@@ -185,10 +191,14 @@ export const getBookingForReschedule = async (uid: string, userId?: number) => {
|
||||
// and we return null here.
|
||||
if (!theBooking && !rescheduleUid) return null;
|
||||
|
||||
const booking = await getBooking(prisma, rescheduleUid || uid);
|
||||
const booking = await getBooking(prisma, rescheduleUid || uid, bookingSeatReferenceUid ? true : false);
|
||||
|
||||
if (!booking) return null;
|
||||
|
||||
if (bookingSeatReferenceUid) {
|
||||
booking["description"] = bookingSeatData?.description ?? null;
|
||||
booking["responses"] = bookingResponsesDbSchema.parse(bookingSeatData?.responses ?? {});
|
||||
}
|
||||
return {
|
||||
...booking,
|
||||
attendees: rescheduleUid
|
||||
|
||||
@@ -727,11 +727,11 @@ async function createBooking({
|
||||
|
||||
const newBookingData: Prisma.BookingCreateInput = {
|
||||
uid,
|
||||
responses: responses === null ? Prisma.JsonNull : responses,
|
||||
responses: responses === null || evt.seatsPerTimeSlot ? Prisma.JsonNull : responses,
|
||||
title: evt.title,
|
||||
startTime: dayjs.utc(evt.startTime).toDate(),
|
||||
endTime: dayjs.utc(evt.endTime).toDate(),
|
||||
description: evt.additionalNotes,
|
||||
description: evt.seatsPerTimeSlot ? null : evt.additionalNotes,
|
||||
customInputs: isPrismaObjOrUndefined(evt.customInputs),
|
||||
status: isConfirmedByDefault ? BookingStatus.ACCEPTED : BookingStatus.PENDING,
|
||||
location: evt.location,
|
||||
@@ -1573,6 +1573,7 @@ async function handler(
|
||||
reqBodyMetadata: reqBody.metadata,
|
||||
subscriberOptions,
|
||||
eventTrigger,
|
||||
responses,
|
||||
});
|
||||
if (newBooking) {
|
||||
req.statusCode = 201;
|
||||
@@ -1658,7 +1659,8 @@ async function handler(
|
||||
data: {
|
||||
referenceUid: uniqueAttendeeId,
|
||||
data: {
|
||||
description: evt.additionalNotes,
|
||||
description: additionalNotes,
|
||||
responses,
|
||||
},
|
||||
booking: {
|
||||
connect: {
|
||||
|
||||
@@ -35,6 +35,7 @@ const createNewSeat = async (
|
||||
organizerUser,
|
||||
fullName,
|
||||
bookerEmail,
|
||||
responses,
|
||||
} = rescheduleSeatedBookingObject;
|
||||
let { evt } = rescheduleSeatedBookingObject;
|
||||
let resultBooking: HandleSeatsResultBooking;
|
||||
@@ -83,6 +84,7 @@ const createNewSeat = async (
|
||||
referenceUid: attendeeUniqueId,
|
||||
data: {
|
||||
description: additionalNotes,
|
||||
responses,
|
||||
},
|
||||
booking: {
|
||||
connect: {
|
||||
|
||||
@@ -28,8 +28,9 @@ const handleSeats = async (newSeatedBookingObject: NewSeatedBookingObject) => {
|
||||
eventTypeId,
|
||||
subscriberOptions,
|
||||
eventTrigger,
|
||||
evt,
|
||||
} = newSeatedBookingObject;
|
||||
const { evt } = newSeatedBookingObject;
|
||||
|
||||
const loggerWithEventDetails = createLoggerWithEventDetails(eventType.id, reqBodyUser, eventType.slug);
|
||||
|
||||
let resultBooking: HandleSeatsResultBooking = null;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import type z from "zod";
|
||||
|
||||
import type { AppsStatus } from "@calcom/types/Calendar";
|
||||
|
||||
@@ -35,6 +36,7 @@ export type NewSeatedBookingObject = {
|
||||
reqBodyMetadata: ReqBodyMetadata;
|
||||
subscriberOptions: GetSubscriberOptions;
|
||||
eventTrigger: WebhookTriggerEvents;
|
||||
responses: z.infer<ReturnType<typeof getBookingDataSchema>>["responses"] | null;
|
||||
};
|
||||
|
||||
export type RescheduleSeatedBookingObject = NewSeatedBookingObject & { rescheduleUid: string };
|
||||
|
||||
@@ -902,6 +902,7 @@ model BookingSeat {
|
||||
booking Booking @relation(fields: [bookingId], references: [id], onDelete: Cascade)
|
||||
attendeeId Int @unique
|
||||
attendee Attendee @relation(fields: [attendeeId], references: [id], onDelete: Cascade)
|
||||
/// @zod.custom(imports.bookingSeatDataSchema)
|
||||
data Json?
|
||||
|
||||
@@index([bookingId])
|
||||
|
||||
@@ -662,3 +662,8 @@ export type ZVerifyCodeInputSchema = z.infer<typeof ZVerifyCodeInputSchema>;
|
||||
export const coerceToDate = z.coerce.date();
|
||||
export const getStringAsNumberRequiredSchema = (t: TFunction) =>
|
||||
z.string().min(1, t("error_required_field")).pipe(z.coerce.number());
|
||||
|
||||
export const bookingSeatDataSchema = z.object({
|
||||
description: z.string().optional(),
|
||||
responses: bookingResponses,
|
||||
});
|
||||
Reference in new issue
Block a user