Skip to content

Commit bcc433b

Browse files
authored
fix: reschedule flow is broken for seated booking (calcom#23987)
* fix: reschedule flow is broken for seated booking * Update BookingListItem.tsx * Update * tweak
1 parent 2ba571d commit bcc433b

5 files changed

Lines changed: 134 additions & 8 deletions

File tree

apps/web/components/booking/BookingListItem.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ function BookingListItem(booking: BookingItemProps) {
185185
const isTabUnconfirmed = booking.listingStatus === "unconfirmed";
186186
const isBookingFromRoutingForm = isBookingReroutable(parsedBooking);
187187

188+
const userSeat = booking.seatsReferences.find((seat) => !!userEmail && seat.attendee?.email === userEmail);
189+
190+
const isAttendee = !!userSeat;
191+
188192
const paymentAppData = getPaymentAppData(booking.eventType);
189193

190194
const location = booking.location as ReturnType<typeof getEventLocationValue>;
@@ -223,10 +227,7 @@ function BookingListItem(booking: BookingItemProps) {
223227
};
224228

225229
const getSeatReferenceUid = () => {
226-
if (!booking.seatsReferences[0]) {
227-
return undefined;
228-
}
229-
return booking.seatsReferences[0].referenceUid;
230+
return userSeat?.referenceUid;
230231
};
231232

232233
const actionContext: BookingActionContext = {
@@ -250,6 +251,7 @@ function BookingListItem(booking: BookingItemProps) {
250251
booking.location === "integrations:daily" ||
251252
(typeof booking.location === "string" && booking.location.trim() === ""),
252253
showPendingPayment: paymentAppData.enabled && booking.payment.length && !booking.paid,
254+
isAttendee,
253255
cardCharged,
254256
attendeeList,
255257
getSeatReferenceUid,

apps/web/components/booking/bookingActions.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface BookingActionContext {
2121
isDisabledRescheduling: boolean;
2222
isCalVideoLocation: boolean;
2323
showPendingPayment: boolean;
24+
isAttendee: boolean;
2425
cardCharged: boolean;
2526
attendeeList: Array<{
2627
name: string;
@@ -102,6 +103,7 @@ export function getEditEventActions(context: BookingActionContext): ActionType[]
102103
isDisabledRescheduling,
103104
isBookingFromRoutingForm,
104105
getSeatReferenceUid,
106+
isAttendee,
105107
t,
106108
} = context;
107109

@@ -111,7 +113,7 @@ export function getEditEventActions(context: BookingActionContext): ActionType[]
111113
icon: "clock",
112114
label: t("reschedule_booking"),
113115
href: `/reschedule/${booking.uid}${
114-
booking.seatsReferences.length ? `?seatReferenceUid=${getSeatReferenceUid()}` : ""
116+
booking.seatsReferences.length && isAttendee ? `?seatReferenceUid=${getSeatReferenceUid()}` : ""
115117
}`,
116118
disabled:
117119
(isBookingInPast && !booking.eventType.allowReschedulingPastBookings) || isDisabledRescheduling,

apps/web/lib/reschedule/[uid]/getServerSideProps.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { z } from "zod";
55

66
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
77
import { determineReschedulePreventionRedirect } from "@calcom/features/bookings/lib/reschedule/determineReschedulePreventionRedirect";
8-
import { buildEventUrlFromBooking } from "@calcom/lib/bookings/buildEventUrlFromBooking";
98
import { getDefaultEvent } from "@calcom/features/eventtypes/lib/defaultEvents";
9+
import { buildEventUrlFromBooking } from "@calcom/lib/bookings/buildEventUrlFromBooking";
1010
import { maybeGetBookingUidFromSeat } from "@calcom/lib/server/maybeGetBookingUidFromSeat";
1111
import { UserRepository } from "@calcom/lib/server/repository/user";
1212
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
@@ -39,7 +39,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
3939
uid,
4040
seatReferenceUid: maybeSeatReferenceUid,
4141
bookingSeat,
42-
} = await maybeGetBookingUidFromSeat(prisma, bookingUid);
42+
} = await maybeGetBookingUidFromSeat(prisma, seatReferenceUid ? seatReferenceUid : bookingUid);
4343

4444
const booking = await prisma.booking.findUnique({
4545
where: {

apps/web/playwright/booking-seats.e2e.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,5 +458,128 @@ test.describe("Reschedule for booking with seats", () => {
458458
await expect(page.locator('[data-testid="confirm-reschedule-button"]')).toHaveCount(1);
459459
});
460460

461+
test("Host reschedule from /upcoming page should have rescheduleUid parameter set to bookingUid", async ({
462+
page,
463+
users,
464+
bookings,
465+
}) => {
466+
const { user, booking } = await createUserWithSeatedEventAndAttendees({ users, bookings }, [
467+
{ name: "John First", email: "first+seats@cal.com", timeZone: "Europe/Berlin" },
468+
{ name: "Jane Second", email: "second+seats@cal.com", timeZone: "Europe/Berlin" },
469+
]);
470+
await user.apiLogin();
471+
472+
const bookingAttendees = await prisma.attendee.findMany({
473+
where: { bookingId: booking.id },
474+
select: {
475+
id: true,
476+
name: true,
477+
email: true,
478+
},
479+
});
480+
481+
const bookingSeats = bookingAttendees.map((attendee) => ({
482+
bookingId: booking.id,
483+
attendeeId: attendee.id,
484+
referenceUid: uuidv4(),
485+
data: {
486+
responses: {
487+
name: attendee.name,
488+
email: attendee.email,
489+
},
490+
},
491+
}));
492+
493+
await prisma.bookingSeat.createMany({
494+
data: bookingSeats,
495+
});
496+
497+
await page.goto("/bookings/upcoming");
498+
await page.waitForSelector('[data-testid="bookings"]');
499+
500+
await page.locator('[data-testid="booking-actions-dropdown"]').nth(0).click();
501+
await page.locator('[data-testid="reschedule"]').click();
502+
503+
await page.waitForURL((url) => {
504+
const rescheduleUid = url.searchParams.get("rescheduleUid");
505+
return !!rescheduleUid && rescheduleUid === booking.uid;
506+
});
507+
});
508+
509+
test("Second attendee reschedule from /upcoming page should use correct seatReferenceUid and show attendee info", async ({
510+
page,
511+
users,
512+
bookings,
513+
}) => {
514+
const { user, booking } = await createUserWithSeatedEventAndAttendees({ users, bookings }, [
515+
{ name: "John First", email: "first+seats@cal.com", timeZone: "Europe/Berlin" },
516+
{ name: "Jane Second", email: "second+seats@cal.com", timeZone: "Europe/Berlin" },
517+
]);
518+
519+
const bookingAttendees = await prisma.attendee.findMany({
520+
where: { bookingId: booking.id },
521+
select: {
522+
id: true,
523+
name: true,
524+
email: true,
525+
},
526+
});
527+
528+
const bookingSeats = bookingAttendees.map((attendee) => ({
529+
bookingId: booking.id,
530+
attendeeId: attendee.id,
531+
referenceUid: uuidv4(),
532+
data: {
533+
responses: {
534+
name: attendee.name,
535+
email: attendee.email,
536+
},
537+
},
538+
}));
539+
540+
await prisma.bookingSeat.createMany({
541+
data: bookingSeats,
542+
});
543+
544+
const references = await prisma.bookingSeat.findMany({
545+
where: { bookingId: booking.id },
546+
orderBy: { id: "asc" },
547+
});
548+
549+
const secondUser = await users.create({
550+
name: "Jane Second",
551+
email: "second+seats@cal.com",
552+
});
553+
await secondUser.apiLogin();
554+
555+
await page.goto("/bookings/upcoming");
556+
await page.waitForSelector('[data-testid="bookings"]');
557+
558+
await page.locator('[data-testid="booking-actions-dropdown"]').nth(0).click();
559+
await page.locator('[data-testid="reschedule"]').click();
560+
561+
await page.waitForURL((url) => {
562+
const rescheduleUid = url.searchParams.get("rescheduleUid");
563+
return !!rescheduleUid && rescheduleUid === references[1].referenceUid;
564+
});
565+
566+
await selectFirstAvailableTimeSlotNextMonth(page);
567+
568+
const nameElement = page.locator("input[name=name]");
569+
const name = await nameElement.inputValue();
570+
expect(name).toBe("Jane Second");
571+
572+
const emailElement = page.locator("input[name=email]");
573+
const email = await emailElement.inputValue();
574+
expect(email).toBe("second+seats@cal.com");
575+
576+
// Complete the reschedule
577+
await confirmReschedule(page);
578+
579+
// Verify successful reschedule
580+
await page.waitForURL(/\/booking\/.*/);
581+
await expect(page).toHaveURL(/\/booking\/.*/);
582+
});
583+
461584
// @TODO: force 404 when rescheduleUid is not found
462585
});

packages/trpc/server/routers/viewer/bookings/get.handler.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,6 @@ export async function getBookings({
578578
.selectFrom("Attendee")
579579
.select(["Attendee.email"])
580580
.whereRef("BookingSeat.attendeeId", "=", "Attendee.id")
581-
.where("Attendee.email", "=", user.email)
582581
).as("attendee"),
583582
])
584583
.whereRef("BookingSeat.bookingId", "=", "Booking.id")

0 commit comments

Comments
 (0)