Fix/seats-cancel-links (#8394)
* Fix cancel links * Use searchParams API to build cancelLink * Fix cancel showSeats * Added test for owner cancel attendee list --------- Co-authored-by: Alex van Andel <me@alexvanandel.com>pull/8446/head
parent
5ce341093b
commit
afe1f5b72f
|
@ -891,16 +891,31 @@ const getEventTypesFromDB = async (id: number) => {
|
|||
};
|
||||
};
|
||||
|
||||
const handleSeatsEventTypeOnBooking = (
|
||||
const handleSeatsEventTypeOnBooking = async (
|
||||
eventType: {
|
||||
seatsPerTimeSlot?: number | null;
|
||||
seatsShowAttendees: boolean | null;
|
||||
[x: string | number | symbol]: unknown;
|
||||
},
|
||||
bookingInfo: Partial<
|
||||
Prisma.BookingGetPayload<{ include: { attendees: { select: { name: true; email: true } } } }>
|
||||
Prisma.BookingGetPayload<{
|
||||
include: {
|
||||
attendees: { select: { name: true; email: true } };
|
||||
seatsReferences: { select: { referenceUid: true } };
|
||||
user: {
|
||||
select: {
|
||||
id: true;
|
||||
name: true;
|
||||
email: true;
|
||||
username: true;
|
||||
timeZone: true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}>
|
||||
>,
|
||||
email: string
|
||||
seatReferenceUid?: string,
|
||||
userId?: number
|
||||
) => {
|
||||
if (eventType?.seatsPerTimeSlot !== null) {
|
||||
// @TODO: right now bookings with seats doesn't save every description that its entered by every user
|
||||
|
@ -908,12 +923,34 @@ const handleSeatsEventTypeOnBooking = (
|
|||
} 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 attendee = bookingInfo?.attendees?.find((a) => {
|
||||
return a.email === email;
|
||||
const seatAttendee = await prisma.bookingSeat.findFirst({
|
||||
where: {
|
||||
referenceUid: seatReferenceUid,
|
||||
},
|
||||
include: {
|
||||
attendee: {
|
||||
select: {
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
bookingInfo["attendees"] = attendee ? [attendee] : [];
|
||||
if (seatAttendee) {
|
||||
const attendee = bookingInfo?.attendees?.find((a) => {
|
||||
return a.email === seatAttendee.attendee?.email;
|
||||
});
|
||||
bookingInfo["attendees"] = attendee ? [attendee] : [];
|
||||
} else {
|
||||
bookingInfo["attendees"] = [];
|
||||
}
|
||||
}
|
||||
return bookingInfo;
|
||||
};
|
||||
|
@ -931,7 +968,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||
const parsedQuery = querySchema.safeParse(context.query);
|
||||
|
||||
if (!parsedQuery.success) return { notFound: true };
|
||||
const { uid, email, eventTypeSlug, cancel, isSuccessBookingPage } = parsedQuery.data;
|
||||
const { uid, eventTypeSlug, seatReferenceUid } = parsedQuery.data;
|
||||
|
||||
const bookingInfoRaw = await prisma.booking.findFirst({
|
||||
where: {
|
||||
|
@ -1037,8 +1074,8 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||
slug: eventType.team?.slug || eventType.users[0]?.username || null,
|
||||
};
|
||||
|
||||
if (bookingInfo !== null && email && eventType.seatsPerTimeSlot) {
|
||||
handleSeatsEventTypeOnBooking(eventType, bookingInfo, email);
|
||||
if (bookingInfo !== null && eventType.seatsPerTimeSlot) {
|
||||
await handleSeatsEventTypeOnBooking(eventType, bookingInfo, seatReferenceUid, session?.user.id);
|
||||
}
|
||||
|
||||
const payment = await prisma.payment.findFirst({
|
||||
|
|
|
@ -313,5 +313,145 @@ test.describe("Booking with Seats", () => {
|
|||
// Validate that the number of seats its 10
|
||||
expect(await page.locator("text=9 / 10 Seats available").count()).toEqual(0);
|
||||
});
|
||||
|
||||
test("Should cancel with seats but event should be still accesible and with one less attendee/seat", 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.login();
|
||||
|
||||
const bookingAttendees = await prisma.attendee.findMany({
|
||||
where: { bookingId: booking.id },
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
const bookingSeats = [
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[0].id, referenceUid: uuidv4() },
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[1].id, referenceUid: uuidv4() },
|
||||
];
|
||||
|
||||
await prisma.bookingSeat.createMany({
|
||||
data: bookingSeats,
|
||||
});
|
||||
|
||||
// Now we cancel the booking as the first attendee
|
||||
// booking/${bookingUid}?cancel=true&allRemainingBookings=false&seatReferenceUid={bookingSeat.referenceUid}
|
||||
await page.goto(
|
||||
`/booking/${booking.uid}?cancel=true&allRemainingBookings=false&seatReferenceUid=${bookingSeats[0].referenceUid}`
|
||||
);
|
||||
|
||||
await page.locator('[data-testid="cancel"]').click();
|
||||
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
await expect(page).toHaveURL(/.*booking/);
|
||||
|
||||
await page.goto(
|
||||
`/booking/${booking.uid}?cancel=true&allRemainingBookings=false&seatReferenceUid=${bookingSeats[1].referenceUid}`
|
||||
);
|
||||
|
||||
// Page should not be 404
|
||||
await page.locator('[data-testid="cancel"]').click();
|
||||
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
await expect(page).toHaveURL(/.*booking/);
|
||||
});
|
||||
|
||||
test("Should book with seats and hide attendees info from showAttendees true", 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.login();
|
||||
const bookingWithEventType = await prisma.booking.findFirst({
|
||||
where: { uid: booking.uid },
|
||||
select: {
|
||||
id: true,
|
||||
eventTypeId: true,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.eventType.update({
|
||||
data: {
|
||||
seatsShowAttendees: false,
|
||||
},
|
||||
where: {
|
||||
id: bookingWithEventType?.eventTypeId || -1,
|
||||
},
|
||||
});
|
||||
|
||||
const bookingAttendees = await prisma.attendee.findMany({
|
||||
where: { bookingId: booking.id },
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
const bookingSeats = [
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[0].id, referenceUid: uuidv4() },
|
||||
{ bookingId: booking.id, attendeeId: bookingAttendees[1].id, referenceUid: uuidv4() },
|
||||
];
|
||||
|
||||
await prisma.bookingSeat.createMany({
|
||||
data: bookingSeats,
|
||||
});
|
||||
|
||||
// Go to cancel page and see that attendees are listed and myself as I'm owner of the booking
|
||||
await page.goto(`/booking/${booking.uid}?cancel=true&allRemainingBookings=false`);
|
||||
|
||||
const foundFirstAttendeeAsOwner = await page.locator('p[data-testid="attendee-first+seats@cal.com"]');
|
||||
await expect(foundFirstAttendeeAsOwner).toHaveCount(1);
|
||||
const foundSecondAttendeeAsOwner = await page.locator('p[data-testid="attendee-second+seats@cal.com"]');
|
||||
await expect(foundSecondAttendeeAsOwner).toHaveCount(1);
|
||||
await page.pause();
|
||||
await page.goto("auth/logout");
|
||||
|
||||
// Now we cancel the booking as the first attendee
|
||||
// booking/${bookingUid}?cancel=true&allRemainingBookings=false&seatReferenceUid={bookingSeat.referenceUid}
|
||||
await page.goto(
|
||||
`/booking/${booking.uid}?cancel=true&allRemainingBookings=false&seatReferenceUid=${bookingSeats[0].referenceUid}`
|
||||
);
|
||||
|
||||
// No attendees should be displayed only the one that it's cancelling
|
||||
const notFoundSecondAttendee = await page.locator('p[data-testid="attendee-second+seats@cal.com"]');
|
||||
|
||||
await expect(notFoundSecondAttendee).toHaveCount(0);
|
||||
const foundFirstAttendee = await page.locator('p[data-testid="attendee-first+seats@cal.com"]');
|
||||
await expect(foundFirstAttendee).toHaveCount(1);
|
||||
|
||||
await prisma.eventType.update({
|
||||
data: {
|
||||
seatsShowAttendees: true,
|
||||
},
|
||||
where: {
|
||||
id: bookingWithEventType?.eventTypeId || -1,
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto(
|
||||
`/booking/${booking.uid}?cancel=true&allRemainingBookings=false&seatReferenceUid=${bookingSeats[1].referenceUid}`
|
||||
);
|
||||
|
||||
// Now attendees should be displayed
|
||||
const foundSecondAttendee = await page.locator('p[data-testid="attendee-second+seats@cal.com"]');
|
||||
|
||||
await expect(foundSecondAttendee).toHaveCount(1);
|
||||
const foundFirstAttendeeAgain = await page
|
||||
.locator('p[data-testid="attendee-first+seats@cal.com"]')
|
||||
.first();
|
||||
await expect(foundFirstAttendeeAgain).toHaveCount(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1443,6 +1443,7 @@ async function handler(
|
|||
* deep cloning evt to avoid this
|
||||
*/
|
||||
const copyEvent = cloneDeep(evt);
|
||||
copyEvent.uid = booking.uid;
|
||||
await sendScheduledSeatsEmails(copyEvent, invitee[0], newSeat, !!eventType.seatsShowAttendees);
|
||||
|
||||
const credentials = await refreshCredentials(organizerUser.credentials);
|
||||
|
|
|
@ -150,7 +150,7 @@ export const getUid = (calEvent: CalendarEvent): string => {
|
|||
};
|
||||
|
||||
const getSeatReferenceId = (calEvent: CalendarEvent): string => {
|
||||
return calEvent.attendeeSeatId ? `seatReferenceUid=${calEvent.attendeeSeatId}` : "";
|
||||
return calEvent.attendeeSeatId ? calEvent.attendeeSeatId : "";
|
||||
};
|
||||
|
||||
export const getManageLink = (calEvent: CalendarEvent) => {
|
||||
|
@ -161,12 +161,12 @@ ${WEBAPP_URL + "/booking/" + getUid(calEvent) + "?changes=true"}
|
|||
};
|
||||
|
||||
export const getCancelLink = (calEvent: CalendarEvent): string => {
|
||||
return (
|
||||
WEBAPP_URL +
|
||||
`/booking/${getUid(
|
||||
calEvent
|
||||
)}?cancel=true&allRemainingBookings=${!!calEvent.recurringEvent}&${getSeatReferenceId}`
|
||||
);
|
||||
const cancelLink = new URL(WEBAPP_URL + `/booking/${getUid(calEvent)}`);
|
||||
cancelLink.searchParams.append("cancel", "true");
|
||||
cancelLink.searchParams.append("allRemainingBookings", String(!!calEvent.recurringEvent));
|
||||
const seatReferenceUid = getSeatReferenceId(calEvent);
|
||||
if (seatReferenceUid) cancelLink.searchParams.append("seatReferenceUid", seatReferenceUid);
|
||||
return cancelLink.toString();
|
||||
};
|
||||
|
||||
export const getRescheduleLink = (calEvent: CalendarEvent): string => {
|
||||
|
|
Loading…
Reference in New Issue