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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user