Converts booking deletion to soft-delete + more robust cancellation (#581)
* Converts booking deletion to soft-delete + more robust cancellation * Update pages/api/cancel.ts infer type :) Co-authored-by: Alex Johansson <alexander@n1s.se> Co-authored-by: Alex Johansson <alexander@n1s.se> Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
This commit is contained in:
parent
e48318a34b
commit
8ee68e2ace
4 changed files with 49 additions and 19 deletions
|
@ -1,15 +1,17 @@
|
|||
import prisma from "../../lib/prisma";
|
||||
import { deleteEvent } from "../../lib/calendarClient";
|
||||
import prisma from "@lib/prisma";
|
||||
import { deleteEvent } from "@lib/calendarClient";
|
||||
import async from "async";
|
||||
import { deleteMeeting } from "../../lib/videoClient";
|
||||
import { deleteMeeting } from "@lib/videoClient";
|
||||
import { asStringOrNull } from "@lib/asStringOrNull";
|
||||
import { BookingStatus } from "@prisma/client";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
if (req.method == "POST") {
|
||||
const uid = req.body.uid;
|
||||
const uid = asStringOrNull(req.body.uid);
|
||||
|
||||
const bookingToDelete = await prisma.booking.findFirst({
|
||||
const bookingToDelete = await prisma.booking.findUnique({
|
||||
where: {
|
||||
uid: uid,
|
||||
uid,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
|
@ -28,6 +30,21 @@ export default async function handler(req, res) {
|
|||
},
|
||||
});
|
||||
|
||||
if (!bookingToDelete) {
|
||||
return res.status(404).end();
|
||||
}
|
||||
|
||||
// by cancelling first, and blocking whilst doing so; we can ensure a cancel
|
||||
// action always succeeds even if subsequent integrations fail cancellation.
|
||||
await prisma.booking.update({
|
||||
where: {
|
||||
uid,
|
||||
},
|
||||
data: {
|
||||
status: BookingStatus.CANCELLED,
|
||||
},
|
||||
});
|
||||
|
||||
const apiDeletes = async.mapLimit(bookingToDelete.user.credentials, 5, async (credential) => {
|
||||
const bookingRefUid = bookingToDelete.references.filter((ref) => ref.type === credential.type)[0]?.uid;
|
||||
if (bookingRefUid) {
|
||||
|
@ -38,23 +55,20 @@ export default async function handler(req, res) {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
const attendeeDeletes = prisma.attendee.deleteMany({
|
||||
where: {
|
||||
bookingId: bookingToDelete.id,
|
||||
},
|
||||
});
|
||||
|
||||
const bookingReferenceDeletes = prisma.bookingReference.deleteMany({
|
||||
where: {
|
||||
bookingId: bookingToDelete.id,
|
||||
},
|
||||
});
|
||||
const bookingDeletes = prisma.booking.delete({
|
||||
where: {
|
||||
id: bookingToDelete.id,
|
||||
},
|
||||
});
|
||||
|
||||
await Promise.all([apiDeletes, attendeeDeletes, bookingReferenceDeletes, bookingDeletes]);
|
||||
await Promise.all([apiDeletes, attendeeDeletes, bookingReferenceDeletes]);
|
||||
|
||||
//TODO Perhaps send emails to user and client to tell about the cancellation
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import classNames from "@lib/classNames";
|
|||
import { ClockIcon, XIcon } from "@heroicons/react/outline";
|
||||
import Loader from "@components/Loader";
|
||||
import { getSession } from "@lib/auth";
|
||||
import { BookingStatus } from "@prisma/client";
|
||||
|
||||
export default function Bookings({ bookings }) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
@ -44,8 +45,7 @@ export default function Bookings({ bookings }) {
|
|||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{bookings
|
||||
.filter((booking) => !booking.confirmed && !booking.rejected)
|
||||
.concat(bookings.filter((booking) => booking.confirmed || booking.rejected))
|
||||
.filter((booking) => booking.status !== BookingStatus.CANCELLED)
|
||||
.map((booking) => (
|
||||
<tr key={booking.id}>
|
||||
<td className={"px-6 py-4" + (booking.rejected ? " line-through" : "")}>
|
||||
|
@ -66,11 +66,13 @@ export default function Bookings({ bookings }) {
|
|||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-sm text-blue-500">
|
||||
<a href={"mailto:" + booking.attendees[0].email}>
|
||||
{booking.attendees[0].email}
|
||||
</a>
|
||||
</div>
|
||||
{booking.attendees.length !== 0 && (
|
||||
<div className="text-sm text-blue-500">
|
||||
<a href={"mailto:" + booking.attendees[0].email}>
|
||||
{booking.attendees[0].email}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
<td className="hidden sm:table-cell px-6 py-4 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-900">
|
||||
|
@ -232,6 +234,7 @@ export async function getServerSideProps(context) {
|
|||
id: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
status: true,
|
||||
},
|
||||
orderBy: {
|
||||
startTime: "asc",
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
-- CreateEnum
|
||||
CREATE TYPE "BookingStatus" AS ENUM ('cancelled', 'accepted', 'rejected', 'pending');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Booking" ADD COLUMN "status" "BookingStatus" NOT NULL DEFAULT E'accepted';
|
|
@ -136,6 +136,13 @@ model Attendee {
|
|||
bookingId Int?
|
||||
}
|
||||
|
||||
enum BookingStatus {
|
||||
CANCELLED @map("cancelled")
|
||||
ACCEPTED @map("accepted")
|
||||
REJECTED @map("rejected")
|
||||
PENDING @map("pending")
|
||||
}
|
||||
|
||||
model Booking {
|
||||
id Int @id @default(autoincrement())
|
||||
uid String @unique
|
||||
|
@ -157,6 +164,7 @@ model Booking {
|
|||
updatedAt DateTime?
|
||||
confirmed Boolean @default(true)
|
||||
rejected Boolean @default(false)
|
||||
status BookingStatus @default(ACCEPTED)
|
||||
}
|
||||
|
||||
model Schedule {
|
||||
|
|
Loading…
Reference in a new issue