Disable reserve slot on seated events (#8761)

* Disable reserve slot on seated events

* Move seats and reserve logic to backend

* Pass booking attendees

* Type fix

* Create booking when two users choose a slot before booking is created

* Update packages/trpc/server/routers/viewer/slots/reserveSlot.handler.ts

---------

Co-authored-by: alannnc <alannnc@gmail.com>
This commit is contained in:
Joe Au-Yeung
2023-05-09 11:02:19 +01:00
committed by GitHub
co-authored by alannnc
parent fbbcc401cb
commit 94917dbb1e
5 changed files with 68 additions and 27 deletions
@@ -72,6 +72,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
slotUtcStartDate: slot.time,
eventTypeId,
slotUtcEndDate: dayjs(slot.time).utc().add(duration, "minutes").format(),
bookingAttendees: bookingAttendees || undefined,
});
};
@@ -230,12 +230,14 @@ const BookingPage = ({
}),
{}
);
const reserveSlot = () => {
if (queryDuration) {
reserveSlotMutation.mutate({
eventTypeId: eventType.id,
slotUtcStartDate: dayjs(queryDate).utc().format(),
slotUtcEndDate: dayjs(queryDate).utc().add(parseInt(queryDuration), "minutes").format(),
bookingAttendees: currentSlotBooking ? currentSlotBooking.attendees.length : undefined,
});
}
};
@@ -1014,9 +1014,17 @@ async function handler(
message?: string;
})
| null = null;
const booking = await prisma.booking.findUniqueOrThrow({
const booking = await prisma.booking.findFirst({
where: {
uid: rescheduleUid || reqBody.bookingUid,
OR: [
{
uid: rescheduleUid || reqBody.bookingUid,
},
{
eventTypeId: eventType.id,
startTime: evt.startTime,
},
],
},
select: {
uid: true,
@@ -1029,6 +1037,11 @@ async function handler(
status: true,
},
});
if (!booking) {
throw new HttpError({ statusCode: 404, message: "Could not find booking" });
}
// See if attendee is already signed up for timeslot
if (
booking.attendees.find((attendee) => attendee.email === invitee[0].email) &&
@@ -21,41 +21,65 @@ interface ReserveSlotOptions {
export const reserveSlotHandler = async ({ ctx, input }: ReserveSlotOptions) => {
const { prisma, req, res } = ctx;
const uid = req?.cookies?.uid || uuid();
const { slotUtcStartDate, slotUtcEndDate, eventTypeId } = input;
const { slotUtcStartDate, slotUtcEndDate, eventTypeId, bookingAttendees } = input;
const releaseAt = dayjs.utc().add(parseInt(MINUTES_TO_BOOK), "minutes").format();
const eventType = await prisma.eventType.findUnique({
where: { id: eventTypeId },
select: { users: { select: { id: true } }, seatsPerTimeSlot: true },
});
if (eventType) {
await Promise.all(
eventType.users.map((user) =>
prisma.selectedSlots.upsert({
where: { selectedSlotUnique: { userId: user.id, slotUtcStartDate, slotUtcEndDate, uid } },
update: {
slotUtcStartDate,
slotUtcEndDate,
releaseAt,
eventTypeId,
},
create: {
userId: user.id,
eventTypeId,
slotUtcStartDate,
slotUtcEndDate,
uid,
releaseAt,
isSeat: eventType.seatsPerTimeSlot !== null,
},
})
)
);
} else {
if (!eventType) {
throw new TRPCError({
message: "Event type not found",
code: "NOT_FOUND",
});
}
let shouldReserveSlot = true;
// If this is a seated event then don't reserve a slot
if (eventType.seatsPerTimeSlot) {
// Check to see if this is the last attendee
if (bookingAttendees) {
const seatsLeft = eventType.seatsPerTimeSlot - bookingAttendees;
if (seatsLeft < 1) shouldReserveSlot = false;
} else {
// If there is no booking yet then don't reserve the slot
shouldReserveSlot = false;
}
}
if (eventType && shouldReserveSlot) {
try {
await Promise.all(
eventType.users.map((user) =>
prisma.selectedSlots.upsert({
where: { selectedSlotUnique: { userId: user.id, slotUtcStartDate, slotUtcEndDate, uid } },
update: {
slotUtcStartDate,
slotUtcEndDate,
releaseAt,
eventTypeId,
},
create: {
userId: user.id,
eventTypeId,
slotUtcStartDate,
slotUtcEndDate,
uid,
releaseAt,
isSeat: eventType.seatsPerTimeSlot !== null,
},
})
)
);
} catch {
throw new TRPCError({
message: "Event type not found",
code: "NOT_FOUND",
});
}
}
res?.setHeader("Set-Cookie", serialize("uid", uid, { path: "/", sameSite: "lax" }));
return;
};
@@ -33,6 +33,7 @@ export const reserveSlotSchema = z
slotUtcStartDate: z.string(),
// endTime ISOString
slotUtcEndDate: z.string(),
bookingAttendees: z.number().optional(),
})
.refine(
(data) => !!data.eventTypeId || !!data.slotUtcStartDate || !!data.slotUtcEndDate,