diff --git a/components/booking/AvailableTimes.tsx b/components/booking/AvailableTimes.tsx
index 66bdc43e..6745ddc5 100644
--- a/components/booking/AvailableTimes.tsx
+++ b/components/booking/AvailableTimes.tsx
@@ -1,17 +1,18 @@
import Link from "next/link";
import { useRouter } from "next/router";
import Slots from "./Slots";
+import {ExclamationIcon} from "@heroicons/react/solid";
-const AvailableTimes = ({ date, eventLength, eventTypeId, workingHours, timeFormat }) => {
+const AvailableTimes = ({ date, eventLength, eventTypeId, workingHours, timeFormat, user }) => {
const router = useRouter();
- const { user, rescheduleUid } = router.query;
- const { slots, isFullyBooked } = Slots({ date, eventLength, workingHours });
+ const { rescheduleUid } = router.query;
+ const { slots, isFullyBooked, hasErrors } = Slots({ date, eventLength, workingHours });
return (
{date.format("dddd DD MMMM YYYY")}
- {slots.length > 0 ? (
+ {slots.length > 0 && (
slots.map((slot) => (
))
- ) : isFullyBooked ?
-
-
{user} is all booked today.
+ )}
+ {isFullyBooked &&
+
{user.name} is all booked today.
+ }
+
+ {!isFullyBooked && slots.length === 0 && !hasErrors &&
}
+
+ {hasErrors && (
+
+ )}
);
};
diff --git a/components/booking/Slots.tsx b/components/booking/Slots.tsx
index 76798e70..e3091e96 100644
--- a/components/booking/Slots.tsx
+++ b/components/booking/Slots.tsx
@@ -3,7 +3,9 @@ import { useRouter } from "next/router";
import getSlots from "../../lib/slots";
import dayjs, {Dayjs} from "dayjs";
import isBetween from "dayjs/plugin/isBetween";
+import utc from "dayjs/plugin/utc";
dayjs.extend(isBetween);
+dayjs.extend(utc);
type Props = {
eventLength: number;
@@ -19,18 +21,25 @@ const Slots = ({ eventLength, minimumBookingNotice, date, workingHours }: Props)
const { user } = router.query;
const [slots, setSlots] = useState([]);
const [isFullyBooked, setIsFullyBooked ] = useState(false);
+ const [hasErrors, setHasErrors ] = useState(false);
useEffect(() => {
setSlots([]);
setIsFullyBooked(false);
+ setHasErrors(false);
fetch(
- `/api/availability/${user}?dateFrom=${date.startOf("day").utc().format()}&dateTo=${date
+ `/api/availability/${user}?dateFrom=${date.startOf("day").utc().startOf('day').format()}&dateTo=${date
.endOf("day")
.utc()
+ .endOf('day')
.format()}`
)
.then((res) => res.json())
- .then(handleAvailableSlots);
+ .then(handleAvailableSlots)
+ .catch( e => {
+ console.error(e);
+ setHasErrors(true);
+ })
}, [date]);
const handleAvailableSlots = (busyTimes: []) => {
@@ -47,26 +56,24 @@ const Slots = ({ eventLength, minimumBookingNotice, date, workingHours }: Props)
// Check for conflicts
for (let i = times.length - 1; i >= 0; i -= 1) {
busyTimes.forEach((busyTime) => {
- const startTime = dayjs(busyTime.start);
- const endTime = dayjs(busyTime.end);
+
+ const startTime = dayjs(busyTime.start).utc();
+ const endTime = dayjs(busyTime.end).utc();
// Check if start times are the same
- if (dayjs(times[i]).format("HH:mm") == startTime.format("HH:mm")) {
+ if (times[i].utc().format("HH:mm") == startTime.format("HH:mm")) {
times.splice(i, 1);
}
-
// Check if time is between start and end times
- if (dayjs(times[i]).isBetween(startTime, endTime)) {
+ else if (times[i].utc().isBetween(startTime, endTime)) {
times.splice(i, 1);
}
-
// Check if slot end time is between start and end time
- if (dayjs(times[i]).add(eventLength, "minutes").isBetween(startTime, endTime)) {
+ else if (times[i].utc().add(eventLength, "minutes").isBetween(startTime, endTime)) {
times.splice(i, 1);
}
-
// Check if startTime is between slot
- if (startTime.isBetween(dayjs(times[i]), dayjs(times[i]).add(eventLength, "minutes"))) {
+ else if (startTime.isBetween(times[i].utc(), times[i].utc().add(eventLength, "minutes"))) {
times.splice(i, 1);
}
});
@@ -82,6 +89,7 @@ const Slots = ({ eventLength, minimumBookingNotice, date, workingHours }: Props)
return {
slots,
isFullyBooked,
+ hasErrors,
};
};
diff --git a/lib/slots.ts b/lib/slots.ts
index 00ffc738..c3dad4b5 100644
--- a/lib/slots.ts
+++ b/lib/slots.ts
@@ -1,11 +1,11 @@
-import dayjs, { Dayjs } from "dayjs";
+import dayjs, {Dayjs} from "dayjs";
import utc from "dayjs/plugin/utc";
dayjs.extend(utc);
interface GetSlotsType {
inviteeDate: Dayjs;
frequency: number;
- workingHours: { [WeekDay]: Boundary[] };
+ workingHours: [];
minimumBookingNotice?: number;
}
@@ -17,34 +17,30 @@ interface Boundary {
const freqApply: number = (cb, value: number, frequency: number): number => cb(value / frequency) * frequency;
const intersectBoundary = (a: Boundary, b: Boundary) => {
- if (a.upperBound < b.lowerBound || a.lowerBound > b.upperBound) {
+ if (
+ a.upperBound < b.lowerBound || a.lowerBound > b.upperBound
+ ) {
return;
}
return {
lowerBound: Math.max(b.lowerBound, a.lowerBound),
- upperBound: Math.min(b.upperBound, a.upperBound),
+ upperBound: Math.min(b.upperBound, a.upperBound)
};
-};
+}
// say invitee is -60,1380, and boundary is -120,240 - the overlap is -60,240
-const getOverlaps = (inviteeBoundary: Boundary, boundaries: Boundary[]) =>
- boundaries.map((boundary) => intersectBoundary(inviteeBoundary, boundary)).filter(Boolean);
+const getOverlaps = (inviteeBoundary: Boundary, boundaries: Boundary[]) => boundaries
+ .map(
+ (boundary) => intersectBoundary(inviteeBoundary, boundary)
+ ).filter(Boolean);
const organizerBoundaries = (workingHours: [], inviteeDate: Dayjs, inviteeBounds: Boundary): Boundary[] => {
const boundaries: Boundary[] = [];
- const startDay: number = +inviteeDate
- .utc()
- .startOf("day")
- .add(inviteeBounds.lowerBound, "minutes")
- .format("d");
- const endDay: number = +inviteeDate
- .utc()
- .startOf("day")
- .add(inviteeBounds.upperBound, "minutes")
- .format("d");
+ const startDay: number = +inviteeDate.utc().startOf('day').add(inviteeBounds.lowerBound, 'minutes').format('d');
+ const endDay: number = +inviteeDate.utc().startOf('day').add(inviteeBounds.upperBound, 'minutes').format('d');
- workingHours.forEach((item) => {
+ workingHours.forEach( (item) => {
const lowerBound: number = item.startTime;
const upperBound: number = lowerBound + item.length;
if (startDay !== endDay) {
@@ -66,7 +62,7 @@ const organizerBoundaries = (workingHours: [], inviteeDate: Dayjs, inviteeBounds
}
}
} else {
- boundaries.push({ lowerBound, upperBound });
+ boundaries.push({lowerBound, upperBound});
}
});
return boundaries;
@@ -76,33 +72,38 @@ const inviteeBoundary = (startTime: number, utcOffset: number, frequency: number
const upperBound: number = freqApply(Math.floor, 1440 - utcOffset, frequency);
const lowerBound: number = freqApply(Math.ceil, startTime - utcOffset, frequency);
return {
- lowerBound,
- upperBound,
+ lowerBound, upperBound,
};
};
-const getSlotsBetweenBoundary = (frequency: number, { lowerBound, upperBound }: Boundary) => {
+const getSlotsBetweenBoundary = (frequency: number, {lowerBound,upperBound}: Boundary) => {
const slots: Dayjs[] = [];
- for (let minutes = 0; lowerBound + minutes <= upperBound - frequency; minutes += frequency) {
- slots.push(
-
dayjs
- .utc()
- .startOf("day")
- .add(lowerBound + minutes, "minutes")
- );
+ for (
+ let minutes = 0;
+ lowerBound + minutes <= upperBound - frequency;
+ minutes += frequency
+ ) {
+ slots.push(dayjs.utc().startOf('day').add(lowerBound + minutes, 'minutes'));
}
return slots;
};
-const getSlots = ({ inviteeDate, frequency, minimumBookingNotice, workingHours }: GetSlotsType): Dayjs[] => {
- const startTime = dayjs.utc().isSame(dayjs(inviteeDate), "day")
- ? inviteeDate.hour() * 60 + inviteeDate.minute() + minimumBookingNotice
- : 0;
+const getSlots = (
+ { inviteeDate, frequency, minimumBookingNotice, workingHours, }: GetSlotsType
+): Dayjs[] => {
+
+ const startTime = (
+ dayjs.utc().isSame(dayjs(inviteeDate), 'day') ? inviteeDate.hour() * 60 + inviteeDate.minute() + minimumBookingNotice : 0
+ );
const inviteeBounds = inviteeBoundary(startTime, inviteeDate.utcOffset(), frequency);
- return getOverlaps(inviteeBounds, organizerBoundaries(workingHours, inviteeDate, inviteeBounds))
- .reduce((slots, boundary: Boundary) => [...slots, ...getSlotsBetweenBoundary(frequency, boundary)], [])
- .map((slot) => slot.utcOffset(dayjs(inviteeDate).utcOffset()));
-};
+ return getOverlaps(
+ inviteeBounds, organizerBoundaries(workingHours, inviteeDate, inviteeBounds)
+ ).reduce(
+ (slots, boundary: Boundary) => [...slots, ...getSlotsBetweenBoundary(frequency, boundary) ], []
+ ).map(
+ (slot) => slot.utcOffset(dayjs(inviteeDate).utcOffset())
+ )
+}
export default getSlots;
diff --git a/pages/[user]/[type].tsx b/pages/[user]/[type].tsx
index 973c8d78..328a2884 100644
--- a/pages/[user]/[type].tsx
+++ b/pages/[user]/[type].tsx
@@ -122,6 +122,7 @@ export default function Type(props): Type {
eventLength={props.eventType.length}
eventTypeId={props.eventType.id}
date={selectedDate}
+ user={props.user}
/>
)}