fix: reschedule flow is broken for seated booking (#23987)

* fix: reschedule flow is broken for seated booking

* Update BookingListItem.tsx

* Update

* tweak
This commit is contained in:
Anik Dhabal Babu authored and GitHub committed 2025-09-23 22:05:08 +01:00
1 parent 2ba571d230
commit bcc433ba9a
5 files changed
+134 -8

No files matched your search

@@ -185,6 +185,10 @@ function BookingListItem(booking: BookingItemProps) {
const isTabUnconfirmed = booking.listingStatus === "unconfirmed";
const isBookingFromRoutingForm = isBookingReroutable(parsedBooking);
const userSeat = booking.seatsReferences.find((seat) => !!userEmail && seat.attendee?.email === userEmail);
const isAttendee = !!userSeat;
const paymentAppData = getPaymentAppData(booking.eventType);
const location = booking.location as ReturnType<typeof getEventLocationValue>;
@@ -223,10 +227,7 @@ function BookingListItem(booking: BookingItemProps) {
};
const getSeatReferenceUid = () => {
if (!booking.seatsReferences[0]) {
return undefined;
}
return booking.seatsReferences[0].referenceUid;
return userSeat?.referenceUid;
};
const actionContext: BookingActionContext = {
@@ -250,6 +251,7 @@ function BookingListItem(booking: BookingItemProps) {
booking.location === "integrations:daily" ||
(typeof booking.location === "string" && booking.location.trim() === ""),
showPendingPayment: paymentAppData.enabled && booking.payment.length && !booking.paid,
isAttendee,
cardCharged,
attendeeList,
getSeatReferenceUid,
@@ -21,6 +21,7 @@ export interface BookingActionContext {
isDisabledRescheduling: boolean;
isCalVideoLocation: boolean;
showPendingPayment: boolean;
isAttendee: boolean;
cardCharged: boolean;
attendeeList: Array<{
name: string;
@@ -102,6 +103,7 @@ export function getEditEventActions(context: BookingActionContext): ActionType[]
isDisabledRescheduling,
isBookingFromRoutingForm,
getSeatReferenceUid,
isAttendee,
t,
} = context;
@@ -111,7 +113,7 @@ export function getEditEventActions(context: BookingActionContext): ActionType[]
icon: "clock",
label: t("reschedule_booking"),
href: `/reschedule/${booking.uid}${
booking.seatsReferences.length ? `?seatReferenceUid=${getSeatReferenceUid()}` : ""
booking.seatsReferences.length && isAttendee ? `?seatReferenceUid=${getSeatReferenceUid()}` : ""
}`,
disabled:
(isBookingInPast && !booking.eventType.allowReschedulingPastBookings) || isDisabledRescheduling,
@@ -5,8 +5,8 @@ import { z } from "zod";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { determineReschedulePreventionRedirect } from "@calcom/features/bookings/lib/reschedule/determineReschedulePreventionRedirect";
import { buildEventUrlFromBooking } from "@calcom/lib/bookings/buildEventUrlFromBooking";
import { getDefaultEvent } from "@calcom/features/eventtypes/lib/defaultEvents";
import { buildEventUrlFromBooking } from "@calcom/lib/bookings/buildEventUrlFromBooking";
import { maybeGetBookingUidFromSeat } from "@calcom/lib/server/maybeGetBookingUidFromSeat";
import { UserRepository } from "@calcom/lib/server/repository/user";
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
@@ -39,7 +39,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
uid,
seatReferenceUid: maybeSeatReferenceUid,
bookingSeat,
} = await maybeGetBookingUidFromSeat(prisma, bookingUid);
} = await maybeGetBookingUidFromSeat(prisma, seatReferenceUid ? seatReferenceUid : bookingUid);
const booking = await prisma.booking.findUnique({
where: {
+123
View File
@@ -458,5 +458,128 @@ test.describe("Reschedule for booking with seats", () => {
await expect(page.locator('[data-testid="confirm-reschedule-button"]')).toHaveCount(1);
});
test("Host reschedule from /upcoming page should have rescheduleUid parameter set to bookingUid", async ({
page,
users,
bookings,
}) => {
const { user, booking } = await createUserWithSeatedEventAndAttendees({ users, bookings }, [
{ name: "John First", email: "first+seats@cal.com", timeZone: "Europe/Berlin" },
{ name: "Jane Second", email: "second+seats@cal.com", timeZone: "Europe/Berlin" },
]);
await user.apiLogin();
const bookingAttendees = await prisma.attendee.findMany({
where: { bookingId: booking.id },
select: {
id: true,
name: true,
email: true,
},
});
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,
});
await page.goto("/bookings/upcoming");
await page.waitForSelector('[data-testid="bookings"]');
await page.locator('[data-testid="booking-actions-dropdown"]').nth(0).click();
await page.locator('[data-testid="reschedule"]').click();
await page.waitForURL((url) => {
const rescheduleUid = url.searchParams.get("rescheduleUid");
return !!rescheduleUid && rescheduleUid === booking.uid;
});
});
test("Second attendee reschedule from /upcoming page should use correct seatReferenceUid and show attendee info", async ({
page,
users,
bookings,
}) => {
const { user, booking } = await createUserWithSeatedEventAndAttendees({ users, bookings }, [
{ name: "John First", email: "first+seats@cal.com", timeZone: "Europe/Berlin" },
{ name: "Jane Second", email: "second+seats@cal.com", timeZone: "Europe/Berlin" },
]);
const bookingAttendees = await prisma.attendee.findMany({
where: { bookingId: booking.id },
select: {
id: true,
name: true,
email: true,
},
});
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,
});
const references = await prisma.bookingSeat.findMany({
where: { bookingId: booking.id },
orderBy: { id: "asc" },
});
const secondUser = await users.create({
name: "Jane Second",
email: "second+seats@cal.com",
});
await secondUser.apiLogin();
await page.goto("/bookings/upcoming");
await page.waitForSelector('[data-testid="bookings"]');
await page.locator('[data-testid="booking-actions-dropdown"]').nth(0).click();
await page.locator('[data-testid="reschedule"]').click();
await page.waitForURL((url) => {
const rescheduleUid = url.searchParams.get("rescheduleUid");
return !!rescheduleUid && rescheduleUid === references[1].referenceUid;
});
await selectFirstAvailableTimeSlotNextMonth(page);
const nameElement = page.locator("input[name=name]");
const name = await nameElement.inputValue();
expect(name).toBe("Jane Second");
const emailElement = page.locator("input[name=email]");
const email = await emailElement.inputValue();
expect(email).toBe("second+seats@cal.com");
// Complete the reschedule
await confirmReschedule(page);
// Verify successful reschedule
await page.waitForURL(/\/booking\/.*/);
await expect(page).toHaveURL(/\/booking\/.*/);
});
// @TODO: force 404 when rescheduleUid is not found
});
@@ -578,7 +578,6 @@ export async function getBookings({
.selectFrom("Attendee")
.select(["Attendee.email"])
.whereRef("BookingSeat.attendeeId", "=", "Attendee.id")
.where("Attendee.email", "=", user.email)
).as("attendee"),
])
.whereRef("BookingSeat.bookingId", "=", "Booking.id")