From bcacc1d16655b19a185053ba5563b6bc4783daf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20L=C3=B3pez?= Date: Thu, 23 Sep 2021 11:18:29 -0600 Subject: [PATCH] More availability fixes (#755) * Abstracts MinutesField * Adds missing Minimum booking notice * Refactoring * Fixes int field sent as string * Sorts slots by time * Fixes availability page * Fixes available days * Type fixes * More availability bugfixes --- components/booking/AvailableTimes.tsx | 23 +++++++++++-- lib/hooks/useSlots.ts | 48 ++++++++++++--------------- pages/api/availability/[user].ts | 26 +++++++++++---- 3 files changed, 63 insertions(+), 34 deletions(-) diff --git a/components/booking/AvailableTimes.tsx b/components/booking/AvailableTimes.tsx index b6038e99..f70c4ec4 100644 --- a/components/booking/AvailableTimes.tsx +++ b/components/booking/AvailableTimes.tsx @@ -1,14 +1,32 @@ import { ExclamationIcon } from "@heroicons/react/solid"; import { SchedulingType } from "@prisma/client"; +import { Dayjs } from "dayjs"; import Link from "next/link"; import { useRouter } from "next/router"; -import React from "react"; +import React, { FC } from "react"; import { useSlots } from "@lib/hooks/useSlots"; import Loader from "@components/Loader"; -const AvailableTimes = ({ +type AvailableTimesProps = { + workingHours: { + days: number[]; + startTime: number; + endTime: number; + }[]; + timeFormat: string; + minimumBookingNotice: number; + eventTypeId: number; + eventLength: number; + date: Dayjs; + users: { + username: string | null; + }[]; + schedulingType: SchedulingType | null; +}; + +const AvailableTimes: FC = ({ date, eventLength, eventTypeId, @@ -28,6 +46,7 @@ const AvailableTimes = ({ workingHours, users, minimumBookingNotice, + eventTypeId, }); return ( diff --git a/lib/hooks/useSlots.ts b/lib/hooks/useSlots.ts index cd8d4c29..ab4b6fd3 100644 --- a/lib/hooks/useSlots.ts +++ b/lib/hooks/useSlots.ts @@ -1,8 +1,9 @@ -import { User, SchedulingType } from "@prisma/client"; +import { Availability, SchedulingType } from "@prisma/client"; import dayjs, { Dayjs } from "dayjs"; import isBetween from "dayjs/plugin/isBetween"; import utc from "dayjs/plugin/utc"; -import { useState, useEffect } from "react"; +import { stringify } from "querystring"; +import { useEffect, useState } from "react"; import getSlots from "@lib/slots"; @@ -13,12 +14,8 @@ dayjs.extend(utc); type AvailabilityUserResponse = { busy: FreeBusyTime; - workingHours: { - daysOfWeek: number[]; - timeZone: string; - startTime: number; - endTime: number; - }; + timeZone: string; + workingHours: Availability[]; }; type Slot = { @@ -28,15 +25,20 @@ type Slot = { type UseSlotsProps = { eventLength: number; + eventTypeId: number; minimumBookingNotice?: number; date: Dayjs; - workingHours: []; - users: User[]; - schedulingType: SchedulingType; + workingHours: { + days: number[]; + startTime: number; + endTime: number; + }[]; + users: { username: string | null }[]; + schedulingType: SchedulingType | null; }; export const useSlots = (props: UseSlotsProps) => { - const { eventLength, minimumBookingNotice = 0, date, users } = props; + const { eventLength, minimumBookingNotice = 0, date, users, eventTypeId } = props; const [slots, setSlots] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -46,12 +48,13 @@ export const useSlots = (props: UseSlotsProps) => { setLoading(true); setError(null); - const dateFrom = encodeURIComponent(date.startOf("day").format()); - const dateTo = encodeURIComponent(date.endOf("day").format()); + const dateFrom = date.startOf("day").format(); + const dateTo = date.endOf("day").format(); + const query = stringify({ dateFrom, dateTo, eventTypeId }); Promise.all( - users.map((user: User) => - fetch(`/api/availability/${user.username}?dateFrom=${dateFrom}&dateTo=${dateTo}`) + users.map((user) => + fetch(`/api/availability/${user.username}?${query}`) .then(handleAvailableSlots) .catch((e) => { console.error(e); @@ -61,7 +64,7 @@ export const useSlots = (props: UseSlotsProps) => { ).then((results) => { let loadedSlots: Slot[] = results[0]; if (results.length === 1) { - loadedSlots = loadedSlots.sort((a, b) => (a.time.isAfter(b.time) ? 1 : -1)); + loadedSlots = loadedSlots?.sort((a, b) => (a.time.isAfter(b.time) ? 1 : -1)); setSlots(loadedSlots); setLoading(false); return; @@ -102,19 +105,12 @@ export const useSlots = (props: UseSlotsProps) => { const handleAvailableSlots = async (res) => { const responseBody: AvailabilityUserResponse = await res.json(); - - const workingHours = { - days: responseBody.workingHours.daysOfWeek, - startTime: responseBody.workingHours.startTime, - endTime: responseBody.workingHours.endTime, - }; - const times = getSlots({ frequency: eventLength, inviteeDate: date, - workingHours: [workingHours], + workingHours: responseBody.workingHours, minimumBookingNotice, - organizerTimeZone: responseBody.workingHours.timeZone, + organizerTimeZone: responseBody.timeZone, }); // Check for conflicts diff --git a/pages/api/availability/[user].ts b/pages/api/availability/[user].ts index d53adb64..7058cd34 100644 --- a/pages/api/availability/[user].ts +++ b/pages/api/availability/[user].ts @@ -10,6 +10,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const user = asStringOrNull(req.query.user); const dateFrom = dayjs(asStringOrNull(req.query.dateFrom)); const dateTo = dayjs(asStringOrNull(req.query.dateTo)); + const eventTypeId = parseInt(asStringOrNull(req.query.eventTypeId) || ""); if (!dateFrom.isValid() || !dateTo.isValid()) { return res.status(400).json({ message: "Invalid time range given." }); @@ -31,6 +32,20 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }, }); + const eventType = await prisma.eventType.findUnique({ + where: { id: eventTypeId }, + select: { + timeZone: true, + availability: { + select: { + startTime: true, + endTime: true, + days: true, + }, + }, + }, + }); + if (!rawUser) throw new Error("No user found"); const { selectedCalendars, ...currentUser } = rawUser; @@ -49,13 +64,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) end: dayjs(a.end).add(currentUser.bufferTime, "minute").toString(), })); + const timeZone = eventType?.timeZone || currentUser.timeZone; + const workingHours = eventType?.availability.length ? eventType.availability : currentUser.availability; + res.status(200).json({ busy: bufferedBusyTimes, - workingHours: { - daysOfWeek: [0, 1, 2, 3, 4, 5, 6], - timeZone: currentUser.timeZone, - startTime: currentUser.startTime, - endTime: currentUser.endTime, - }, + timeZone, + workingHours, }); }