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:
Alex van Andel 2021-09-13 09:57:56 +01:00 committed by GitHub
parent e48318a34b
commit 8ee68e2ace
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 19 deletions

View file

@ -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

View file

@ -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",

View file

@ -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';

View file

@ -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 {