Merge pull request #310 from femyeda/fix-244-can-book-when-not-available
Fix 244 can book when not available
This commit is contained in:
		
						commit
						cc02558ac6
					
				
					 2 changed files with 95 additions and 13 deletions
				
			
		|  | @ -1,7 +1,7 @@ | |||
| import dayjs from "dayjs"; | ||||
| import isBetween from "dayjs/plugin/isBetween"; | ||||
| dayjs.extend(isBetween); | ||||
| import { useEffect, useState } from "react"; | ||||
| import { useEffect, useState, useMemo } from "react"; | ||||
| import getSlots from "../../lib/slots"; | ||||
| import Link from "next/link"; | ||||
| import { timeZone } from "../../lib/clock"; | ||||
|  | @ -14,14 +14,18 @@ const AvailableTimes = (props) => { | |||
|   const [loaded, setLoaded] = useState(false); | ||||
|   const [error, setError] = useState(false); | ||||
| 
 | ||||
|   const times = getSlots({ | ||||
|     calendarTimeZone: props.user.timeZone, | ||||
|     selectedTimeZone: timeZone(), | ||||
|     eventLength: props.eventType.length, | ||||
|     selectedDate: props.date, | ||||
|     dayStartTime: props.user.startTime, | ||||
|     dayEndTime: props.user.endTime, | ||||
|   }); | ||||
|   const times = useMemo(() => { | ||||
|     const slots = getSlots({ | ||||
|       calendarTimeZone: props.user.timeZone, | ||||
|       selectedTimeZone: timeZone(), | ||||
|       eventLength: props.eventType.length, | ||||
|       selectedDate: props.date, | ||||
|       dayStartTime: props.user.startTime, | ||||
|       dayEndTime: props.user.endTime, | ||||
|     }); | ||||
| 
 | ||||
|     return slots; | ||||
|   }, [props.date]); | ||||
| 
 | ||||
|   const handleAvailableSlots = (busyTimes: []) => { | ||||
|     // Check for conflicts
 | ||||
|  | @ -80,6 +84,7 @@ const AvailableTimes = (props) => { | |||
|       </div> | ||||
|       {!error && | ||||
|         loaded && | ||||
|         times.length > 0 && | ||||
|         times.map((time) => ( | ||||
|           <div key={dayjs(time).utc().format()}> | ||||
|             <Link | ||||
|  | @ -95,6 +100,11 @@ const AvailableTimes = (props) => { | |||
|             </Link> | ||||
|           </div> | ||||
|         ))} | ||||
|       {!error && loaded && times.length == 0 && ( | ||||
|         <div className="w-full h-full flex flex-col justify-center content-center items-center -mt-4"> | ||||
|           <h1 className="text-xl font">{props.user.name} is all booked today.</h1> | ||||
|         </div> | ||||
|       )} | ||||
|       {!error && !loaded && <div className="loader" />} | ||||
|       {error && ( | ||||
|         <div className="bg-yellow-50 border-l-4 border-yellow-400 p-4"> | ||||
|  |  | |||
|  | @ -1,21 +1,53 @@ | |||
| import type { NextApiRequest, NextApiResponse } from "next"; | ||||
| import prisma from "../../../lib/prisma"; | ||||
| import { CalendarEvent, createEvent, updateEvent } from "../../../lib/calendarClient"; | ||||
| import { CalendarEvent, createEvent, updateEvent, getBusyCalendarTimes } from "../../../lib/calendarClient"; | ||||
| import async from "async"; | ||||
| import { v5 as uuidv5 } from "uuid"; | ||||
| import short from "short-uuid"; | ||||
| import { createMeeting, updateMeeting } from "../../../lib/videoClient"; | ||||
| import { createMeeting, updateMeeting, getBusyVideoTimes } from "../../../lib/videoClient"; | ||||
| import EventAttendeeMail from "../../../lib/emails/EventAttendeeMail"; | ||||
| import { getEventName } from "../../../lib/event"; | ||||
| import { LocationType } from "../../../lib/location"; | ||||
| import merge from "lodash.merge"; | ||||
| const translator = short(); | ||||
| import dayjs from "dayjs"; | ||||
| 
 | ||||
| interface p { | ||||
| const isAvailable = (busyTimes, time, length) => { | ||||
|   // Check for conflicts
 | ||||
|   let t = true; | ||||
|   busyTimes.forEach((busyTime) => { | ||||
|     const startTime = dayjs(busyTime.start); | ||||
|     const endTime = dayjs(busyTime.end); | ||||
| 
 | ||||
|     // Check if start times are the same
 | ||||
|     if (dayjs(time).format("HH:mm") == startTime.format("HH:mm")) { | ||||
|       t = false; | ||||
|     } | ||||
| 
 | ||||
|     // Check if time is between start and end times
 | ||||
|     if (dayjs(time).isBetween(startTime, endTime)) { | ||||
|       t = false; | ||||
|     } | ||||
| 
 | ||||
|     // Check if slot end time is between start and end time
 | ||||
|     if (dayjs(time).add(length, "minutes").isBetween(startTime, endTime)) { | ||||
|       t = false; | ||||
|     } | ||||
| 
 | ||||
|     // Check if startTime is between slot
 | ||||
|     if (startTime.isBetween(dayjs(time), dayjs(time).add(length, "minutes"))) { | ||||
|       t = false; | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   return t; | ||||
| }; | ||||
| 
 | ||||
| interface GetLocationRequestFromIntegrationRequest { | ||||
|   location: string; | ||||
| } | ||||
| 
 | ||||
| const getLocationRequestFromIntegration = ({ location }: p) => { | ||||
| const getLocationRequestFromIntegration = ({ location }: GetLocationRequestFromIntegrationRequest) => { | ||||
|   if (location === LocationType.GoogleMeet.valueOf()) { | ||||
|     const requestId = uuidv5(location, uuidv5.URL); | ||||
| 
 | ||||
|  | @ -47,10 +79,43 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) | |||
|     }, | ||||
|   }); | ||||
| 
 | ||||
|   const selectedCalendars = await prisma.selectedCalendar.findMany({ | ||||
|     where: { | ||||
|       userId: currentUser.id, | ||||
|     }, | ||||
|   }); | ||||
|   // Split credentials up into calendar credentials and video credentials
 | ||||
|   const calendarCredentials = currentUser.credentials.filter((cred) => cred.type.endsWith("_calendar")); | ||||
|   const videoCredentials = currentUser.credentials.filter((cred) => cred.type.endsWith("_video")); | ||||
| 
 | ||||
|   const hasCalendarIntegrations = | ||||
|     currentUser.credentials.filter((cred) => cred.type.endsWith("_calendar")).length > 0; | ||||
|   const hasVideoIntegrations = | ||||
|     currentUser.credentials.filter((cred) => cred.type.endsWith("_video")).length > 0; | ||||
| 
 | ||||
|   const calendarAvailability = await getBusyCalendarTimes( | ||||
|     currentUser.credentials, | ||||
|     dayjs(req.body.start).startOf("day").utc().format(), | ||||
|     dayjs(req.body.end).endOf("day").utc().format(), | ||||
|     selectedCalendars | ||||
|   ); | ||||
|   const videoAvailability = await getBusyVideoTimes( | ||||
|     currentUser.credentials, | ||||
|     dayjs(req.body.start).startOf("day").utc().format(), | ||||
|     dayjs(req.body.end).endOf("day").utc().format() | ||||
|   ); | ||||
|   let commonAvailability = []; | ||||
| 
 | ||||
|   if (hasCalendarIntegrations && hasVideoIntegrations) { | ||||
|     commonAvailability = calendarAvailability.filter((availability) => | ||||
|       videoAvailability.includes(availability) | ||||
|     ); | ||||
|   } else if (hasVideoIntegrations) { | ||||
|     commonAvailability = videoAvailability; | ||||
|   } else if (hasCalendarIntegrations) { | ||||
|     commonAvailability = calendarAvailability; | ||||
|   } | ||||
| 
 | ||||
|   const rescheduleUid = req.body.rescheduleUid; | ||||
| 
 | ||||
|   const selectedEventType = await prisma.eventType.findFirst({ | ||||
|  | @ -61,6 +126,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) | |||
|     select: { | ||||
|       eventName: true, | ||||
|       title: true, | ||||
|       length: true, | ||||
|     }, | ||||
|   }); | ||||
| 
 | ||||
|  | @ -103,6 +169,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) | |||
|     }, | ||||
|   }); | ||||
| 
 | ||||
|   const isAvailableToBeBooked = isAvailable(commonAvailability, req.body.start, selectedEventType.length); | ||||
| 
 | ||||
|   if (!isAvailableToBeBooked) { | ||||
|     return res.status(400).json({ message: `${currentUser.name} is unavailable at this time.` }); | ||||
|   } | ||||
| 
 | ||||
|   let results = []; | ||||
|   let referencesToCreate = []; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Bailey Pumfleet
						Bailey Pumfleet