| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  | import { BanIcon, CheckIcon, ClockIcon, XIcon } from "@heroicons/react/outline"; | 
					
						
							|  |  |  | import { BookingStatus } from "@prisma/client"; | 
					
						
							|  |  |  | import dayjs from "dayjs"; | 
					
						
							| 
									
										
										
										
											2022-02-09 18:25:58 +00:00
										 |  |  | import { useState } from "react"; | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  | import { useMutation } from "react-query"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-16 23:36:43 +00:00
										 |  |  | import Button from "@calcom/ui/Button"; | 
					
						
							|  |  |  | import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog"; | 
					
						
							|  |  |  | import { TextArea } from "@calcom/ui/form/fields"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  | import { HttpError } from "@lib/core/http/error"; | 
					
						
							| 
									
										
										
										
											2021-10-15 10:53:42 +00:00
										 |  |  | import { useLocale } from "@lib/hooks/useLocale"; | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  | import { inferQueryOutput, trpc } from "@lib/trpc"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-28 16:24:47 +00:00
										 |  |  | import { useMeQuery } from "@components/Shell"; | 
					
						
							| 
									
										
										
										
											2022-02-11 22:20:10 +00:00
										 |  |  | import TableActions, { ActionType } from "@components/ui/TableActions"; | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-17 16:58:23 +00:00
										 |  |  | type BookingItem = inferQueryOutput<"viewer.bookings">["bookings"][number]; | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | function BookingListItem(booking: BookingItem) { | 
					
						
							| 
									
										
										
										
											2022-02-28 16:24:47 +00:00
										 |  |  |   // Get user so we can determine 12/24 hour format preferences
 | 
					
						
							|  |  |  |   const query = useMeQuery(); | 
					
						
							|  |  |  |   const user = query.data; | 
					
						
							| 
									
										
										
										
											2021-10-25 13:05:21 +00:00
										 |  |  |   const { t, i18n } = useLocale(); | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |   const utils = trpc.useContext(); | 
					
						
							| 
									
										
										
										
											2022-02-09 18:25:58 +00:00
										 |  |  |   const [rejectionReason, setRejectionReason] = useState<string>(""); | 
					
						
							|  |  |  |   const [rejectionDialogIsOpen, setRejectionDialogIsOpen] = useState(false); | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |   const mutation = useMutation( | 
					
						
							|  |  |  |     async (confirm: boolean) => { | 
					
						
							|  |  |  |       const res = await fetch("/api/book/confirm", { | 
					
						
							|  |  |  |         method: "PATCH", | 
					
						
							| 
									
										
										
										
											2022-02-09 18:25:58 +00:00
										 |  |  |         body: JSON.stringify({ | 
					
						
							|  |  |  |           id: booking.id, | 
					
						
							|  |  |  |           confirmed: confirm, | 
					
						
							|  |  |  |           language: i18n.language, | 
					
						
							|  |  |  |           reason: rejectionReason, | 
					
						
							|  |  |  |         }), | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |         headers: { | 
					
						
							|  |  |  |           "Content-Type": "application/json", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       if (!res.ok) { | 
					
						
							|  |  |  |         throw new HttpError({ statusCode: res.status }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       async onSettled() { | 
					
						
							| 
									
										
										
										
											2021-10-12 18:59:52 +00:00
										 |  |  |         await utils.invalidateQueries(["viewer.bookings"]); | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |       }, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   const isUpcoming = new Date(booking.endTime) >= new Date(); | 
					
						
							|  |  |  |   const isCancelled = booking.status === BookingStatus.CANCELLED; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-12 13:11:33 +00:00
										 |  |  |   const pendingActions: ActionType[] = [ | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |       id: "reject", | 
					
						
							| 
									
										
										
										
											2021-10-15 10:53:42 +00:00
										 |  |  |       label: t("reject"), | 
					
						
							| 
									
										
										
										
											2022-02-09 18:25:58 +00:00
										 |  |  |       onClick: () => setRejectionDialogIsOpen(true), | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |       icon: BanIcon, | 
					
						
							|  |  |  |       disabled: mutation.isLoading, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       id: "confirm", | 
					
						
							| 
									
										
										
										
											2021-10-15 10:53:42 +00:00
										 |  |  |       label: t("confirm"), | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |       onClick: () => mutation.mutate(true), | 
					
						
							|  |  |  |       icon: CheckIcon, | 
					
						
							|  |  |  |       disabled: mutation.isLoading, | 
					
						
							|  |  |  |       color: "primary", | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-12 13:11:33 +00:00
										 |  |  |   const bookedActions: ActionType[] = [ | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |       id: "cancel", | 
					
						
							| 
									
										
										
										
											2021-10-15 10:53:42 +00:00
										 |  |  |       label: t("cancel"), | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |       href: `/cancel/${booking.uid}`, | 
					
						
							|  |  |  |       icon: XIcon, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       id: "reschedule", | 
					
						
							| 
									
										
										
										
											2021-10-15 10:53:42 +00:00
										 |  |  |       label: t("reschedule"), | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |       href: `/reschedule/${booking.uid}`, | 
					
						
							|  |  |  |       icon: ClockIcon, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const startTime = dayjs(booking.startTime).format(isUpcoming ? "ddd, D MMM" : "D MMMM YYYY"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return ( | 
					
						
							| 
									
										
										
										
											2022-02-09 18:25:58 +00:00
										 |  |  |     <> | 
					
						
							|  |  |  |       <Dialog open={rejectionDialogIsOpen} onOpenChange={setRejectionDialogIsOpen}> | 
					
						
							|  |  |  |         <DialogContent> | 
					
						
							|  |  |  |           <DialogHeader title={t("rejection_reason_title")} /> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           <p className="-mt-4 text-sm text-gray-500">{t("rejection_reason_description")}</p> | 
					
						
							|  |  |  |           <p className="mt-6 mb-2 text-sm font-bold text-black"> | 
					
						
							|  |  |  |             {t("rejection_reason")} | 
					
						
							|  |  |  |             <span className="font-normal text-gray-500"> (Optional)</span> | 
					
						
							|  |  |  |           </p> | 
					
						
							|  |  |  |           <TextArea | 
					
						
							|  |  |  |             name={t("rejection_reason")} | 
					
						
							|  |  |  |             value={rejectionReason} | 
					
						
							|  |  |  |             onChange={(e) => setRejectionReason(e.target.value)} | 
					
						
							|  |  |  |             className="mb-5 sm:mb-6" | 
					
						
							|  |  |  |           /> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           <DialogFooter> | 
					
						
							|  |  |  |             <DialogClose> | 
					
						
							|  |  |  |               <Button color="secondary">{t("cancel")}</Button> | 
					
						
							|  |  |  |             </DialogClose> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             <Button | 
					
						
							|  |  |  |               disabled={mutation.isLoading} | 
					
						
							|  |  |  |               onClick={() => { | 
					
						
							|  |  |  |                 mutation.mutate(false); | 
					
						
							|  |  |  |               }}> | 
					
						
							|  |  |  |               {t("rejection_confirmation")} | 
					
						
							|  |  |  |             </Button> | 
					
						
							|  |  |  |           </DialogFooter> | 
					
						
							|  |  |  |         </DialogContent> | 
					
						
							|  |  |  |       </Dialog> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       <tr className="flex"> | 
					
						
							|  |  |  |         <td className="hidden whitespace-nowrap py-4 align-top ltr:pl-6 rtl:pr-6 sm:table-cell"> | 
					
						
							|  |  |  |           <div className="text-sm leading-6 text-gray-900">{startTime}</div> | 
					
						
							|  |  |  |           <div className="text-sm text-gray-500"> | 
					
						
							| 
									
										
										
										
											2022-02-28 16:24:47 +00:00
										 |  |  |             {dayjs(booking.startTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")} -{" "} | 
					
						
							|  |  |  |             {dayjs(booking.endTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")} | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |           </div> | 
					
						
							| 
									
										
										
										
											2022-02-09 18:25:58 +00:00
										 |  |  |         </td> | 
					
						
							|  |  |  |         <td className={"flex-1 py-4 ltr:pl-4 rtl:pr-4" + (booking.rejected ? " line-through" : "")}> | 
					
						
							|  |  |  |           <div className="sm:hidden"> | 
					
						
							|  |  |  |             {!booking.confirmed && !booking.rejected && ( | 
					
						
							|  |  |  |               <Tag className="mb-2 ltr:mr-2 rtl:ml-2">{t("unconfirmed")}</Tag> | 
					
						
							|  |  |  |             )} | 
					
						
							|  |  |  |             {!!booking?.eventType?.price && !booking.paid && ( | 
					
						
							|  |  |  |               <Tag className="mb-2 ltr:mr-2 rtl:ml-2">Pending payment</Tag> | 
					
						
							|  |  |  |             )} | 
					
						
							|  |  |  |             <div className="text-sm font-medium text-gray-900"> | 
					
						
							|  |  |  |               {startTime}:{" "} | 
					
						
							|  |  |  |               <small className="text-sm text-gray-500"> | 
					
						
							|  |  |  |                 {dayjs(booking.startTime).format("HH:mm")} - {dayjs(booking.endTime).format("HH:mm")} | 
					
						
							|  |  |  |               </small> | 
					
						
							|  |  |  |             </div> | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |           </div> | 
					
						
							| 
									
										
										
										
											2022-02-09 18:25:58 +00:00
										 |  |  |           <div | 
					
						
							|  |  |  |             title={booking.title} | 
					
						
							|  |  |  |             className="max-w-56 truncate text-sm font-medium leading-6 text-neutral-900 md:max-w-max"> | 
					
						
							|  |  |  |             {booking.eventType?.team && <strong>{booking.eventType.team.name}: </strong>} | 
					
						
							|  |  |  |             {booking.title} | 
					
						
							|  |  |  |             {!!booking?.eventType?.price && !booking.paid && ( | 
					
						
							|  |  |  |               <Tag className="hidden ltr:ml-2 rtl:mr-2 sm:inline-flex">Pending payment</Tag> | 
					
						
							|  |  |  |             )} | 
					
						
							|  |  |  |             {!booking.confirmed && !booking.rejected && ( | 
					
						
							|  |  |  |               <Tag className="hidden ltr:ml-2 rtl:mr-2 sm:inline-flex">{t("unconfirmed")}</Tag> | 
					
						
							|  |  |  |             )} | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |           </div> | 
					
						
							| 
									
										
										
										
											2022-02-09 18:25:58 +00:00
										 |  |  |           {booking.description && ( | 
					
						
							| 
									
										
										
										
											2022-02-09 22:32:31 +00:00
										 |  |  |             <div className="max-w-52 md:max-w-96 truncate text-sm text-gray-500" title={booking.description}> | 
					
						
							| 
									
										
										
										
											2022-02-09 18:25:58 +00:00
										 |  |  |               "{booking.description}" | 
					
						
							|  |  |  |             </div> | 
					
						
							|  |  |  |           )} | 
					
						
							|  |  |  |           {booking.attendees.length !== 0 && ( | 
					
						
							|  |  |  |             <div className="text-sm text-gray-900 hover:text-blue-500"> | 
					
						
							|  |  |  |               <a href={"mailto:" + booking.attendees[0].email}>{booking.attendees[0].email}</a> | 
					
						
							|  |  |  |             </div> | 
					
						
							|  |  |  |           )} | 
					
						
							|  |  |  |         </td> | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-09 18:25:58 +00:00
										 |  |  |         <td className="whitespace-nowrap py-4 text-right text-sm font-medium ltr:pr-4 rtl:pl-4"> | 
					
						
							|  |  |  |           {isUpcoming && !isCancelled ? ( | 
					
						
							|  |  |  |             <> | 
					
						
							| 
									
										
										
										
											2022-03-04 10:04:05 +00:00
										 |  |  |               {!booking.confirmed && !booking.rejected && user!.id === booking.user!.id && ( | 
					
						
							|  |  |  |                 <TableActions actions={pendingActions} /> | 
					
						
							|  |  |  |               )} | 
					
						
							| 
									
										
										
										
											2022-02-09 18:25:58 +00:00
										 |  |  |               {booking.confirmed && !booking.rejected && <TableActions actions={bookedActions} />} | 
					
						
							|  |  |  |               {!booking.confirmed && booking.rejected && ( | 
					
						
							|  |  |  |                 <div className="text-sm text-gray-500">{t("rejected")}</div> | 
					
						
							|  |  |  |               )} | 
					
						
							|  |  |  |             </> | 
					
						
							|  |  |  |           ) : null} | 
					
						
							|  |  |  |         </td> | 
					
						
							|  |  |  |       </tr> | 
					
						
							|  |  |  |     </> | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-17 16:58:23 +00:00
										 |  |  | const Tag = ({ children, className = "" }: React.PropsWithChildren<{ className?: string }>) => { | 
					
						
							|  |  |  |   return ( | 
					
						
							|  |  |  |     <span | 
					
						
							| 
									
										
										
										
											2022-02-09 00:05:13 +00:00
										 |  |  |       className={`inline-flex items-center rounded-sm bg-yellow-100 px-1.5 py-0.5 text-xs font-medium text-yellow-800 ${className}`}> | 
					
						
							| 
									
										
										
										
											2021-12-17 16:58:23 +00:00
										 |  |  |       {children} | 
					
						
							|  |  |  |     </span> | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-30 10:46:39 +00:00
										 |  |  | export default BookingListItem; |