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
This commit is contained in:
Omar López 2021-09-23 11:18:29 -06:00 committed by GitHub
parent 790ed3e6b1
commit bcacc1d166
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 34 deletions

View file

@ -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<AvailableTimesProps> = ({
date,
eventLength,
eventTypeId,
@ -28,6 +46,7 @@ const AvailableTimes = ({
workingHours,
users,
minimumBookingNotice,
eventTypeId,
});
return (

View file

@ -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<Slot[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<Error | null>(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

View file

@ -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,
});
}