Merge pull request #434 from joshsny/bugfix/cancel
Fix 500 error when trying to cancel a booking that was already cancelled.
This commit is contained in:
		
						commit
						dcb619af1f
					
				
					 2 changed files with 80 additions and 72 deletions
				
			
		|  | @ -1,26 +1,22 @@ | ||||||
| import Head from "next/head"; | import Loader from "@components/Loader"; | ||||||
| import Shell from "../../components/Shell"; | import prisma from "@lib/prisma"; | ||||||
| import { getSession, useSession } from "next-auth/client"; |  | ||||||
| import { useState } from "react"; |  | ||||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||||
| import utc from "dayjs/plugin/utc"; | import utc from "dayjs/plugin/utc"; | ||||||
| import { GetServerSideProps } from "next"; | import { GetServerSideProps } from "next"; | ||||||
| import prisma from "@lib/prisma"; | import { getSession } from "next-auth/client"; | ||||||
| import Loader from "@components/Loader"; | import Head from "next/head"; | ||||||
|  | import { useEffect, useState } from "react"; | ||||||
|  | import Shell from "../../components/Shell"; | ||||||
| 
 | 
 | ||||||
| dayjs.extend(utc); | dayjs.extend(utc); | ||||||
| 
 | 
 | ||||||
| export default function Troubleshoot({ user }) { | export default function Troubleshoot({ user }) { | ||||||
|   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|   const [session, loading] = useSession(); |   const [loading, setLoading] = useState(true); | ||||||
|   const [availability, setAvailability] = useState([]); |   const [availability, setAvailability] = useState([]); | ||||||
|   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|   const [selectedDate, setSelectedDate] = useState(dayjs()); |   const [selectedDate, setSelectedDate] = useState(dayjs()); | ||||||
| 
 | 
 | ||||||
|   if (loading) { |  | ||||||
|     return <Loader />; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   function convertMinsToHrsMins(mins) { |   function convertMinsToHrsMins(mins) { | ||||||
|     let h = Math.floor(mins / 60); |     let h = Math.floor(mins / 60); | ||||||
|     let m = mins % 60; |     let m = mins % 60; | ||||||
|  | @ -30,23 +26,29 @@ export default function Troubleshoot({ user }) { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const fetchAvailability = (date) => { |   const fetchAvailability = (date) => { | ||||||
|     fetch( |     const dateFrom = date.startOf("day").utc().format(); | ||||||
|       `/api/availability/${session.user.username}?dateFrom=${date |     const dateTo = date.endOf("day").utc().format(); | ||||||
|         .startOf("day") | 
 | ||||||
|         .utc() |     fetch(`/api/availability/${user.username}?dateFrom=${dateFrom}&dateTo=${dateTo}`) | ||||||
|         .startOf("day") |  | ||||||
|         .format()}&dateTo=${date.endOf("day").utc().endOf("day").format()}` |  | ||||||
|     ) |  | ||||||
|       .then((res) => { |       .then((res) => { | ||||||
|         return res.json(); |         return res.json(); | ||||||
|       }) |       }) | ||||||
|       .then((apires) => setAvailability(apires)) |       .then((availableIntervals) => { | ||||||
|  |         setAvailability(availableIntervals); | ||||||
|  |         setLoading(false); | ||||||
|  |       }) | ||||||
|       .catch((e) => { |       .catch((e) => { | ||||||
|         console.error(e); |         console.error(e); | ||||||
|       }); |       }); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   fetchAvailability(selectedDate); |   useEffect(() => { | ||||||
|  |     fetchAvailability(selectedDate); | ||||||
|  |   }, [selectedDate]); | ||||||
|  | 
 | ||||||
|  |   if (loading) { | ||||||
|  |     return <Loader />; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div> |     <div> | ||||||
|  | @ -106,11 +108,12 @@ export const getServerSideProps: GetServerSideProps = async (context) => { | ||||||
| 
 | 
 | ||||||
|   const user = await prisma.user.findFirst({ |   const user = await prisma.user.findFirst({ | ||||||
|     where: { |     where: { | ||||||
|       username: session.user.username, |       id: session.user.id, | ||||||
|     }, |     }, | ||||||
|     select: { |     select: { | ||||||
|       startTime: true, |       startTime: true, | ||||||
|       endTime: true, |       endTime: true, | ||||||
|  |       username: true, | ||||||
|     }, |     }, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,13 +1,13 @@ | ||||||
| import { useState } from "react"; | import { CalendarIcon, XIcon } from "@heroicons/react/solid"; | ||||||
| import Head from "next/head"; |  | ||||||
| import prisma from "../../lib/prisma"; |  | ||||||
| import { useRouter } from "next/router"; |  | ||||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||||
| import { CalendarIcon, ClockIcon, XIcon } from "@heroicons/react/solid"; |  | ||||||
| import isSameOrBefore from "dayjs/plugin/isSameOrBefore"; |  | ||||||
| import isBetween from "dayjs/plugin/isBetween"; | import isBetween from "dayjs/plugin/isBetween"; | ||||||
| import utc from "dayjs/plugin/utc"; | import isSameOrBefore from "dayjs/plugin/isSameOrBefore"; | ||||||
| import timezone from "dayjs/plugin/timezone"; | import timezone from "dayjs/plugin/timezone"; | ||||||
|  | import utc from "dayjs/plugin/utc"; | ||||||
|  | import Head from "next/head"; | ||||||
|  | import { useRouter } from "next/router"; | ||||||
|  | import { useState } from "react"; | ||||||
|  | import prisma from "../../lib/prisma"; | ||||||
| import { collectPageParameters, telemetryEventTypes, useTelemetry } from "../../lib/telemetry"; | import { collectPageParameters, telemetryEventTypes, useTelemetry } from "../../lib/telemetry"; | ||||||
| 
 | 
 | ||||||
| dayjs.extend(isSameOrBefore); | dayjs.extend(isSameOrBefore); | ||||||
|  | @ -23,7 +23,7 @@ export default function Type(props) { | ||||||
|   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|   const [is24h, setIs24h] = useState(false); |   const [is24h, setIs24h] = useState(false); | ||||||
|   const [loading, setLoading] = useState(false); |   const [loading, setLoading] = useState(false); | ||||||
|   const [error, setError] = useState(null); |   const [error, setError] = useState(props.booking ? null : "This booking was already cancelled"); | ||||||
|   const telemetry = useTelemetry(); |   const telemetry = useTelemetry(); | ||||||
| 
 | 
 | ||||||
|   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|  | @ -46,7 +46,7 @@ export default function Type(props) { | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     if (res.status >= 200 && res.status < 300) { |     if (res.status >= 200 && res.status < 300) { | ||||||
|       router.push("/cancel/success?user=" + props.user.username + "&title=" + props.eventType.title); |       router.push("/cancel/success?user=" + props.user.username + "&title=" + props.booking.title); | ||||||
|     } else { |     } else { | ||||||
|       setLoading(false); |       setLoading(false); | ||||||
|       setError("An error with status code " + res.status + " occurred. Please try again later."); |       setError("An error with status code " + res.status + " occurred. Please try again later."); | ||||||
|  | @ -57,7 +57,8 @@ export default function Type(props) { | ||||||
|     <div> |     <div> | ||||||
|       <Head> |       <Head> | ||||||
|         <title> |         <title> | ||||||
|           Cancel {props.booking.title} | {props.user.name || props.user.username} | Calendso |           Cancel {props.booking && `${props.booking.title} | ${props.user.name || props.user.username} `}| | ||||||
|  |           Calendso | ||||||
|         </title> |         </title> | ||||||
|         <link rel="icon" href="/favicon.ico" /> |         <link rel="icon" href="/favicon.ico" /> | ||||||
|       </Head> |       </Head> | ||||||
|  | @ -86,51 +87,49 @@ export default function Type(props) { | ||||||
|                   </div> |                   </div> | ||||||
|                 )} |                 )} | ||||||
|                 {!error && ( |                 {!error && ( | ||||||
|                   <div> |                   <> | ||||||
|                     <div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100"> |                     <div> | ||||||
|                       <XIcon className="h-6 w-6 text-red-600" /> |                       <div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100"> | ||||||
|                     </div> |                         <XIcon className="h-6 w-6 text-red-600" /> | ||||||
|                     <div className="mt-3 text-center sm:mt-5"> |  | ||||||
|                       <h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-headline"> |  | ||||||
|                         Really cancel your booking? |  | ||||||
|                       </h3> |  | ||||||
|                       <div className="mt-2"> |  | ||||||
|                         <p className="text-sm text-gray-500">Instead, you could also reschedule it.</p> |  | ||||||
|                       </div> |                       </div> | ||||||
|                       <div className="mt-4 border-t border-b py-4"> |                       <div className="mt-3 text-center sm:mt-5"> | ||||||
|                         <h2 className="text-lg font-medium text-gray-600 mb-2">{props.booking.title}</h2> |                         <h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-headline"> | ||||||
|                         <p className="text-gray-500 mb-1"> |                           Really cancel your booking? | ||||||
|                           <ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> |                         </h3> | ||||||
|                           {props.eventType.length} minutes |                         <div className="mt-2"> | ||||||
|                         </p> |                           <p className="text-sm text-gray-500">Instead, you could also reschedule it.</p> | ||||||
|                         <p className="text-gray-500"> |                         </div> | ||||||
|                           <CalendarIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> |                         <div className="mt-4 border-t border-b py-4"> | ||||||
|                           {dayjs |                           <h2 className="text-lg font-medium text-gray-600 mb-2">{props.booking.title}</h2> | ||||||
|                             .utc(props.booking.startTime) |                           <p className="text-gray-500"> | ||||||
|                             .format((is24h ? "H:mm" : "h:mma") + ", dddd DD MMMM YYYY")} |                             <CalendarIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> | ||||||
|                         </p> |                             {dayjs | ||||||
|  |                               .utc(props.booking.startTime) | ||||||
|  |                               .format((is24h ? "H:mm" : "h:mma") + ", dddd DD MMMM YYYY")} | ||||||
|  |                           </p> | ||||||
|  |                         </div> | ||||||
|                       </div> |                       </div> | ||||||
|                     </div> |                     </div> | ||||||
|                   </div> |                     <div className="mt-5 sm:mt-6 text-center"> | ||||||
|  |                       <div className="mt-5"> | ||||||
|  |                         <button | ||||||
|  |                           onClick={cancellationHandler} | ||||||
|  |                           disabled={loading} | ||||||
|  |                           type="button" | ||||||
|  |                           className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-red-700 bg-red-100 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:text-sm mx-2 btn-white"> | ||||||
|  |                           Cancel | ||||||
|  |                         </button> | ||||||
|  |                         <button | ||||||
|  |                           onClick={() => router.push("/reschedule/" + uid)} | ||||||
|  |                           disabled={loading} | ||||||
|  |                           type="button" | ||||||
|  |                           className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-gray-700 bg-gray-100 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:text-sm mx-2 btn-white"> | ||||||
|  |                           Reschedule | ||||||
|  |                         </button> | ||||||
|  |                       </div> | ||||||
|  |                     </div> | ||||||
|  |                   </> | ||||||
|                 )} |                 )} | ||||||
|                 <div className="mt-5 sm:mt-6 text-center"> |  | ||||||
|                   <div className="mt-5"> |  | ||||||
|                     <button |  | ||||||
|                       onClick={cancellationHandler} |  | ||||||
|                       disabled={loading} |  | ||||||
|                       type="button" |  | ||||||
|                       className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-red-700 bg-red-100 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:text-sm mx-2 btn-white"> |  | ||||||
|                       Cancel |  | ||||||
|                     </button> |  | ||||||
|                     <button |  | ||||||
|                       onClick={() => router.push("/reschedule/" + uid)} |  | ||||||
|                       disabled={loading} |  | ||||||
|                       type="button" |  | ||||||
|                       className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-gray-700 bg-gray-100 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:text-sm mx-2 btn-white"> |  | ||||||
|                       Reschedule |  | ||||||
|                     </button> |  | ||||||
|                   </div> |  | ||||||
|                 </div> |  | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|  | @ -163,6 +162,12 @@ export async function getServerSideProps(context) { | ||||||
|     }, |     }, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |   if (!booking) { | ||||||
|  |     return { | ||||||
|  |       props: { booking: null }, | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   // Workaround since Next.js has problems serializing date objects (see https://github.com/vercel/next.js/issues/11993)
 |   // Workaround since Next.js has problems serializing date objects (see https://github.com/vercel/next.js/issues/11993)
 | ||||||
|   const bookingObj = Object.assign({}, booking, { |   const bookingObj = Object.assign({}, booking, { | ||||||
|     startTime: booking.startTime.toString(), |     startTime: booking.startTime.toString(), | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Peer_Rich
						Peer_Rich