From a41dd30467d3659aebb066c64d415efb3bd57fa9 Mon Sep 17 00:00:00 2001 From: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Date: Wed, 9 Feb 2022 23:55:58 +0530 Subject: [PATCH] Add reason for rejection (optional) (#1719) * init --WIP * added rejection option modal * migration added * lint fix * rejection reason in email added * moved getRejectedReason function * lint fix * --wip * Prevent undefineds and nulls on email messages * Cleanup Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: zomars --- @types/@wojtekmaj:react-daterange-picker.d.ts | 15 -- components/booking/BookingListItem.tsx | 162 +++++++++++------- .../templates/attendee-declined-email.ts | 2 + .../templates/attendee-scheduled-email.ts | 11 ++ .../calendar/interfaces/Calendar.ts | 1 + pages/api/book/confirm.ts | 4 +- .../migration.sql | 2 + prisma/schema.prisma | 1 + prisma/zod/booking.ts | 1 + public/static/locales/en/common.json | 4 + 10 files changed, 128 insertions(+), 75 deletions(-) delete mode 100644 @types/@wojtekmaj:react-daterange-picker.d.ts create mode 100644 prisma/migrations/20220209082843_add_rejection_reason/migration.sql diff --git a/@types/@wojtekmaj:react-daterange-picker.d.ts b/@types/@wojtekmaj:react-daterange-picker.d.ts deleted file mode 100644 index 83839408..00000000 --- a/@types/@wojtekmaj:react-daterange-picker.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -declare module "@wojtekmaj/react-daterange-picker/dist/entry.nostyle" { - import { CalendarProps } from "react-calendar"; - export type DateRangePickerCalendarProps = Omit< - CalendarProps, - "calendarClassName" | "onChange" | "value" - > & { - calendarClassName?: string; - onChange: (value: [Date, Date]) => void; - value: [Date, Date]; - clearIcon: JSX.Element | null; - calendarIcon: JSX.Element | null; - rangeDivider: JSX.Element | null; - }; - export default function DateRangePicker(props: DateRangePickerCalendarProps): JSX.Element; -} diff --git a/components/booking/BookingListItem.tsx b/components/booking/BookingListItem.tsx index 45efad84..edcf73f7 100644 --- a/components/booking/BookingListItem.tsx +++ b/components/booking/BookingListItem.tsx @@ -1,25 +1,35 @@ import { BanIcon, CheckIcon, ClockIcon, XIcon } from "@heroicons/react/outline"; import { BookingStatus } from "@prisma/client"; import dayjs from "dayjs"; +import { useState } from "react"; import { useMutation } from "react-query"; import { HttpError } from "@lib/core/http/error"; import { useLocale } from "@lib/hooks/useLocale"; import { inferQueryOutput, trpc } from "@lib/trpc"; +import { TextArea } from "@components/form/fields"; import TableActions, { ActionType } from "@components/ui/TableActions"; +import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@components/Dialog"; +import Button from "@components/ui/Button"; type BookingItem = inferQueryOutput<"viewer.bookings">["bookings"][number]; function BookingListItem(booking: BookingItem) { const { t, i18n } = useLocale(); const utils = trpc.useContext(); - + const [rejectionReason, setRejectionReason] = useState(""); + const [rejectionDialogIsOpen, setRejectionDialogIsOpen] = useState(false); const mutation = useMutation( async (confirm: boolean) => { const res = await fetch("/api/book/confirm", { method: "PATCH", - body: JSON.stringify({ id: booking.id, confirmed: confirm, language: i18n.language }), + body: JSON.stringify({ + id: booking.id, + confirmed: confirm, + language: i18n.language, + reason: rejectionReason, + }), headers: { "Content-Type": "application/json", }, @@ -41,7 +51,7 @@ function BookingListItem(booking: BookingItem) { { id: "reject", label: t("reject"), - onClick: () => mutation.mutate(false), + onClick: () => setRejectionDialogIsOpen(true), icon: BanIcon, disabled: mutation.isLoading, }, @@ -73,64 +83,98 @@ function BookingListItem(booking: BookingItem) { const startTime = dayjs(booking.startTime).format(isUpcoming ? "ddd, D MMM" : "D MMMM YYYY"); return ( - - -
{startTime}
-
- {dayjs(booking.startTime).format("HH:mm")} - {dayjs(booking.endTime).format("HH:mm")} -
- - -
- {!booking.confirmed && !booking.rejected && ( - {t("unconfirmed")} - )} - {!!booking?.eventType?.price && !booking.paid && ( - Pending payment - )} -
- {startTime}:{" "} - - {dayjs(booking.startTime).format("HH:mm")} - {dayjs(booking.endTime).format("HH:mm")} - -
-
-
- {booking.eventType?.team && {booking.eventType.team.name}: } - {booking.title} - {!!booking?.eventType?.price && !booking.paid && ( - Pending payment - )} - {!booking.confirmed && !booking.rejected && ( - {t("unconfirmed")} - )} -
- {booking.description && ( -
- "{booking.description}" -
- )} - {booking.attendees.length !== 0 && ( -
- {booking.attendees[0].email} -
- )} - + <> + + + - - {isUpcoming && !isCancelled ? ( - <> - {!booking.confirmed && !booking.rejected && } - {booking.confirmed && !booking.rejected && } - {!booking.confirmed && booking.rejected && ( -
{t("rejected")}
+

{t("rejection_reason_description")}

+

+ {t("rejection_reason")} + (Optional) +

+