Bugfix/year change (#1323)
This commit is contained in:
parent
e6f71c81bb
commit
a3bd226347
6 changed files with 132 additions and 110 deletions
|
@ -1,18 +1,22 @@
|
||||||
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid";
|
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid";
|
||||||
import { PeriodType } from "@prisma/client";
|
import { EventType, PeriodType } from "@prisma/client";
|
||||||
import dayjs, { Dayjs } from "dayjs";
|
import dayjs, { Dayjs } from "dayjs";
|
||||||
// Then, include dayjs-business-time
|
|
||||||
import dayjsBusinessTime from "dayjs-business-time";
|
import dayjsBusinessTime from "dayjs-business-time";
|
||||||
|
import timezone from "dayjs/plugin/timezone";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
import classNames from "@lib/classNames";
|
import classNames from "@lib/classNames";
|
||||||
|
import { timeZone } from "@lib/clock";
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
import getSlots from "@lib/slots";
|
import getSlots from "@lib/slots";
|
||||||
import { WorkingHours } from "@lib/types/schedule";
|
import { WorkingHours } from "@lib/types/schedule";
|
||||||
|
|
||||||
|
import Loader from "@components/Loader";
|
||||||
|
|
||||||
dayjs.extend(dayjsBusinessTime);
|
dayjs.extend(dayjsBusinessTime);
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
dayjs.extend(timezone);
|
||||||
|
|
||||||
type DatePickerProps = {
|
type DatePickerProps = {
|
||||||
weekStart: string;
|
weekStart: string;
|
||||||
|
@ -20,7 +24,7 @@ type DatePickerProps = {
|
||||||
workingHours: WorkingHours[];
|
workingHours: WorkingHours[];
|
||||||
eventLength: number;
|
eventLength: number;
|
||||||
date: Dayjs | null;
|
date: Dayjs | null;
|
||||||
periodType: string;
|
periodType: PeriodType;
|
||||||
periodStartDate: Date | null;
|
periodStartDate: Date | null;
|
||||||
periodEndDate: Date | null;
|
periodEndDate: Date | null;
|
||||||
periodDays: number | null;
|
periodDays: number | null;
|
||||||
|
@ -28,6 +32,43 @@ type DatePickerProps = {
|
||||||
minimumBookingNotice: number;
|
minimumBookingNotice: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isOutOfBounds(
|
||||||
|
time: dayjs.ConfigType,
|
||||||
|
{
|
||||||
|
periodType,
|
||||||
|
periodDays,
|
||||||
|
periodCountCalendarDays,
|
||||||
|
periodStartDate,
|
||||||
|
periodEndDate,
|
||||||
|
}: Pick<
|
||||||
|
EventType,
|
||||||
|
"periodType" | "periodDays" | "periodCountCalendarDays" | "periodStartDate" | "periodEndDate"
|
||||||
|
>
|
||||||
|
) {
|
||||||
|
const date = dayjs(time);
|
||||||
|
|
||||||
|
switch (periodType) {
|
||||||
|
case PeriodType.ROLLING: {
|
||||||
|
const periodRollingEndDay = periodCountCalendarDays
|
||||||
|
? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
dayjs().utcOffset(date.utcOffset()).add(periodDays!, "days").endOf("day")
|
||||||
|
: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
dayjs().utcOffset(date.utcOffset()).addBusinessTime(periodDays!, "days").endOf("day");
|
||||||
|
return date.endOf("day").isAfter(periodRollingEndDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
case PeriodType.RANGE: {
|
||||||
|
const periodRangeStartDay = dayjs(periodStartDate).utcOffset(date.utcOffset()).endOf("day");
|
||||||
|
const periodRangeEndDay = dayjs(periodEndDate).utcOffset(date.utcOffset()).endOf("day");
|
||||||
|
return date.endOf("day").isBefore(periodRangeStartDay) || date.endOf("day").isAfter(periodRangeEndDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
case PeriodType.UNLIMITED:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function DatePicker({
|
function DatePicker({
|
||||||
weekStart,
|
weekStart,
|
||||||
onDatePicked,
|
onDatePicked,
|
||||||
|
@ -42,37 +83,21 @@ function DatePicker({
|
||||||
minimumBookingNotice,
|
minimumBookingNotice,
|
||||||
}: DatePickerProps): JSX.Element {
|
}: DatePickerProps): JSX.Element {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const [days, setDays] = useState<({ disabled: boolean; date: number } | null)[]>([]);
|
|
||||||
|
|
||||||
const [selectedMonth, setSelectedMonth] = useState<number>(
|
const [browsingDate, setBrowsingDate] = useState<Dayjs | null>(date);
|
||||||
date
|
|
||||||
? periodType === PeriodType.RANGE
|
|
||||||
? dayjs(periodStartDate).utcOffset(date.utcOffset()).month()
|
|
||||||
: date.month()
|
|
||||||
: dayjs().month() /* High chance server is going to have the same month */
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dayjs().month() !== selectedMonth) {
|
if (!browsingDate || (date && browsingDate.utcOffset() !== date?.utcOffset())) {
|
||||||
setSelectedMonth(dayjs().month());
|
setBrowsingDate(date || dayjs().tz(timeZone()));
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [date, browsingDate]);
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Handle month changes
|
const days = useMemo(() => {
|
||||||
const incrementMonth = () => {
|
if (!browsingDate) {
|
||||||
setSelectedMonth((selectedMonth ?? 0) + 1);
|
return [];
|
||||||
};
|
}
|
||||||
|
|
||||||
const decrementMonth = () => {
|
|
||||||
setSelectedMonth((selectedMonth ?? 0) - 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const inviteeDate = (): Dayjs => (date || dayjs()).month(selectedMonth);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Create placeholder elements for empty days in first week
|
// Create placeholder elements for empty days in first week
|
||||||
let weekdayOfFirst = inviteeDate().date(1).day();
|
let weekdayOfFirst = browsingDate.startOf("month").day();
|
||||||
if (weekStart === "Monday") {
|
if (weekStart === "Monday") {
|
||||||
weekdayOfFirst -= 1;
|
weekdayOfFirst -= 1;
|
||||||
if (weekdayOfFirst < 0) weekdayOfFirst = 6;
|
if (weekdayOfFirst < 0) weekdayOfFirst = 6;
|
||||||
|
@ -81,65 +106,45 @@ function DatePicker({
|
||||||
const days = Array(weekdayOfFirst).fill(null);
|
const days = Array(weekdayOfFirst).fill(null);
|
||||||
|
|
||||||
const isDisabled = (day: number) => {
|
const isDisabled = (day: number) => {
|
||||||
const date: Dayjs = inviteeDate().date(day);
|
const date = browsingDate.startOf("day").date(day);
|
||||||
switch (periodType) {
|
return (
|
||||||
case PeriodType.ROLLING: {
|
isOutOfBounds(date, {
|
||||||
if (!periodDays) {
|
periodType,
|
||||||
throw new Error("PeriodType rolling requires periodDays");
|
periodStartDate,
|
||||||
}
|
periodEndDate,
|
||||||
const periodRollingEndDay = periodCountCalendarDays
|
periodCountCalendarDays,
|
||||||
? dayjs.utc().add(periodDays, "days").endOf("day")
|
periodDays,
|
||||||
: (dayjs.utc() as Dayjs).addBusinessTime(periodDays, "days").endOf("day");
|
}) ||
|
||||||
return (
|
!getSlots({
|
||||||
date.endOf("day").isBefore(dayjs().utcOffset(date.utcOffset())) ||
|
inviteeDate: date,
|
||||||
date.endOf("day").isAfter(periodRollingEndDay) ||
|
frequency: eventLength,
|
||||||
!getSlots({
|
minimumBookingNotice,
|
||||||
inviteeDate: date,
|
workingHours,
|
||||||
frequency: eventLength,
|
}).length
|
||||||
minimumBookingNotice,
|
);
|
||||||
workingHours,
|
|
||||||
}).length
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
case PeriodType.RANGE: {
|
|
||||||
const periodRangeStartDay = dayjs(periodStartDate).utc().endOf("day");
|
|
||||||
const periodRangeEndDay = dayjs(periodEndDate).utc().endOf("day");
|
|
||||||
return (
|
|
||||||
date.endOf("day").isBefore(dayjs().utcOffset(date.utcOffset())) ||
|
|
||||||
date.endOf("day").isBefore(periodRangeStartDay) ||
|
|
||||||
date.endOf("day").isAfter(periodRangeEndDay) ||
|
|
||||||
!getSlots({
|
|
||||||
inviteeDate: date,
|
|
||||||
frequency: eventLength,
|
|
||||||
minimumBookingNotice,
|
|
||||||
workingHours,
|
|
||||||
}).length
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
case PeriodType.UNLIMITED:
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
date.endOf("day").isBefore(dayjs().utcOffset(date.utcOffset())) ||
|
|
||||||
!getSlots({
|
|
||||||
inviteeDate: date,
|
|
||||||
frequency: eventLength,
|
|
||||||
minimumBookingNotice,
|
|
||||||
workingHours,
|
|
||||||
}).length
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const daysInMonth = inviteeDate().daysInMonth();
|
const daysInMonth = browsingDate.daysInMonth();
|
||||||
for (let i = 1; i <= daysInMonth; i++) {
|
for (let i = 1; i <= daysInMonth; i++) {
|
||||||
days.push({ disabled: isDisabled(i), date: i });
|
days.push({ disabled: isDisabled(i), date: i });
|
||||||
}
|
}
|
||||||
|
|
||||||
setDays(days);
|
return days;
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [selectedMonth]);
|
}, [browsingDate]);
|
||||||
|
|
||||||
|
if (!browsingDate) {
|
||||||
|
return <Loader />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle month changes
|
||||||
|
const incrementMonth = () => {
|
||||||
|
setBrowsingDate(browsingDate?.add(1, "month"));
|
||||||
|
};
|
||||||
|
|
||||||
|
const decrementMonth = () => {
|
||||||
|
setBrowsingDate(browsingDate?.subtract(1, "month"));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -152,20 +157,18 @@ function DatePicker({
|
||||||
<div className="flex mb-4 text-xl font-light text-gray-600">
|
<div className="flex mb-4 text-xl font-light text-gray-600">
|
||||||
<span className="w-1/2 text-gray-600 dark:text-white">
|
<span className="w-1/2 text-gray-600 dark:text-white">
|
||||||
<strong className="text-gray-900 dark:text-white">
|
<strong className="text-gray-900 dark:text-white">
|
||||||
{t(inviteeDate().format("MMMM").toLowerCase())}
|
{t(browsingDate.format("MMMM").toLowerCase())}
|
||||||
</strong>{" "}
|
</strong>{" "}
|
||||||
<span className="text-gray-500">{inviteeDate().format("YYYY")}</span>
|
<span className="text-gray-500">{browsingDate.format("YYYY")}</span>
|
||||||
</span>
|
</span>
|
||||||
<div className="w-1/2 text-right text-gray-600 dark:text-gray-400">
|
<div className="w-1/2 text-right text-gray-600 dark:text-gray-400">
|
||||||
<button
|
<button
|
||||||
onClick={decrementMonth}
|
onClick={decrementMonth}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"group mr-2 p-1",
|
"group mr-2 p-1",
|
||||||
typeof selectedMonth === "number" &&
|
browsingDate.startOf("month").isBefore(dayjs()) && "text-gray-400 dark:text-gray-600"
|
||||||
selectedMonth <= dayjs().month() &&
|
|
||||||
"text-gray-400 dark:text-gray-600"
|
|
||||||
)}
|
)}
|
||||||
disabled={typeof selectedMonth === "number" && selectedMonth <= dayjs().month()}
|
disabled={browsingDate.startOf("month").isBefore(dayjs())}
|
||||||
data-testid="decrementMonth">
|
data-testid="decrementMonth">
|
||||||
<ChevronLeftIcon className="w-5 h-5 group-hover:text-black dark:group-hover:text-white" />
|
<ChevronLeftIcon className="w-5 h-5 group-hover:text-black dark:group-hover:text-white" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -195,13 +198,13 @@ function DatePicker({
|
||||||
<div key={`e-${idx}`} />
|
<div key={`e-${idx}`} />
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={() => onDatePicked(inviteeDate().date(day.date))}
|
onClick={() => onDatePicked(browsingDate.date(day.date))}
|
||||||
disabled={day.disabled}
|
disabled={day.disabled}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"absolute w-full top-0 left-0 right-0 bottom-0 rounded-sm text-center mx-auto",
|
"absolute w-full top-0 left-0 right-0 bottom-0 rounded-sm text-center mx-auto",
|
||||||
"hover:border hover:border-brand dark:hover:border-white",
|
"hover:border hover:border-brand dark:hover:border-white",
|
||||||
day.disabled ? "text-gray-400 font-light hover:border-0 cursor-default" : "font-medium",
|
day.disabled ? "text-gray-400 font-light hover:border-0 cursor-default" : "font-medium",
|
||||||
date && date.isSame(inviteeDate().date(day.date), "day")
|
date && date.isSame(browsingDate.date(day.date), "day")
|
||||||
? "bg-brand text-brandcontrast"
|
? "bg-brand text-brandcontrast"
|
||||||
: !day.disabled
|
: !day.disabled
|
||||||
? " bg-gray-100 dark:bg-gray-600 dark:text-white"
|
? " bg-gray-100 dark:bg-gray-600 dark:text-white"
|
||||||
|
|
|
@ -79,7 +79,7 @@ export function getWorkingHours(
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
const utcOffset = relativeTimeUnit.utcOffset || dayjs().tz(relativeTimeUnit.timeZone).utcOffset();
|
const utcOffset = relativeTimeUnit.utcOffset ?? dayjs().tz(relativeTimeUnit.timeZone).utcOffset();
|
||||||
|
|
||||||
const workingHours = availability.reduce((workingHours: WorkingHours[], schedule) => {
|
const workingHours = availability.reduce((workingHours: WorkingHours[], schedule) => {
|
||||||
// Get times localised to the given utcOffset/timeZone
|
// Get times localised to the given utcOffset/timeZone
|
||||||
|
|
|
@ -107,7 +107,6 @@ export const useSlots = (props: UseSlotsProps) => {
|
||||||
workingHours: responseBody.workingHours,
|
workingHours: responseBody.workingHours,
|
||||||
minimumBookingNotice,
|
minimumBookingNotice,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check for conflicts
|
// Check for conflicts
|
||||||
for (let i = times.length - 1; i >= 0; i -= 1) {
|
for (let i = times.length - 1; i >= 0; i -= 1) {
|
||||||
responseBody.busy.every((busyTime): boolean => {
|
responseBody.busy.every((busyTime): boolean => {
|
||||||
|
|
22
lib/slots.ts
22
lib/slots.ts
|
@ -19,7 +19,7 @@ export type GetSlots = {
|
||||||
|
|
||||||
const getMinuteOffset = (date: Dayjs, step: number) => {
|
const getMinuteOffset = (date: Dayjs, step: number) => {
|
||||||
// Diffs the current time with the given date and iff same day; (handled by 1440) - return difference; otherwise 0
|
// Diffs the current time with the given date and iff same day; (handled by 1440) - return difference; otherwise 0
|
||||||
const minuteOffset = Math.min(date.diff(dayjs().startOf("day"), "minute"), 1440) % 1440;
|
const minuteOffset = Math.min(date.diff(dayjs.utc().startOf("day"), "minute"), 1440) % 1440;
|
||||||
// round down to nearest step
|
// round down to nearest step
|
||||||
return Math.ceil(minuteOffset / step) * step;
|
return Math.ceil(minuteOffset / step) * step;
|
||||||
};
|
};
|
||||||
|
@ -27,15 +27,11 @@ const getMinuteOffset = (date: Dayjs, step: number) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const getSlots = ({ inviteeDate, frequency, minimumBookingNotice, workingHours }: GetSlots) => {
|
const getSlots = ({ inviteeDate, frequency, minimumBookingNotice, workingHours }: GetSlots) => {
|
||||||
// current date in invitee tz
|
// current date in invitee tz
|
||||||
let startDate = dayjs(inviteeDate); // .add(minimumBookingNotice, "minute");
|
const startDate = dayjs().add(minimumBookingNotice, "minute");
|
||||||
// checks if the start date is in the past
|
// checks if the start date is in the past
|
||||||
if (startDate.isBefore(dayjs(), "day")) {
|
if (inviteeDate.isBefore(startDate, "day")) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
// Add the current time to the startDate if the day is today
|
|
||||||
if (startDate.isToday()) {
|
|
||||||
startDate = startDate.add(dayjs().diff(startDate, "minute"), "minute");
|
|
||||||
}
|
|
||||||
|
|
||||||
const localWorkingHours = getWorkingHours(
|
const localWorkingHours = getWorkingHours(
|
||||||
{ utcOffset: -inviteeDate.utcOffset() },
|
{ utcOffset: -inviteeDate.utcOffset() },
|
||||||
|
@ -47,14 +43,18 @@ const getSlots = ({ inviteeDate, frequency, minimumBookingNotice, workingHours }
|
||||||
).filter((hours) => hours.days.includes(inviteeDate.day()));
|
).filter((hours) => hours.days.includes(inviteeDate.day()));
|
||||||
|
|
||||||
const slots: Dayjs[] = [];
|
const slots: Dayjs[] = [];
|
||||||
for (let minutes = getMinuteOffset(startDate, frequency); minutes < 1440; minutes += frequency) {
|
for (let minutes = getMinuteOffset(inviteeDate, frequency); minutes < 1440; minutes += frequency) {
|
||||||
const slot = startDate.startOf("day").add(minutes, "minute");
|
const slot = dayjs(inviteeDate).startOf("day").add(minutes, "minute");
|
||||||
|
// check if slot happened already
|
||||||
|
if (slot.isBefore(startDate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// add slots to available slots if it is found to be between the start and end time of the checked working hours.
|
// add slots to available slots if it is found to be between the start and end time of the checked working hours.
|
||||||
if (
|
if (
|
||||||
localWorkingHours.some((hours) =>
|
localWorkingHours.some((hours) =>
|
||||||
slot.isBetween(
|
slot.isBetween(
|
||||||
startDate.startOf("day").add(hours.startTime, "minute"),
|
inviteeDate.startOf("day").add(hours.startTime, "minute"),
|
||||||
startDate.startOf("day").add(hours.endTime, "minute"),
|
inviteeDate.startOf("day").add(hours.endTime, "minute"),
|
||||||
null,
|
null,
|
||||||
"[)"
|
"[)"
|
||||||
)
|
)
|
||||||
|
|
|
@ -121,9 +121,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
periodStartDate: req.body.periodStartDate,
|
periodStartDate: req.body.periodStartDate,
|
||||||
periodEndDate: req.body.periodEndDate,
|
periodEndDate: req.body.periodEndDate,
|
||||||
periodCountCalendarDays: req.body.periodCountCalendarDays,
|
periodCountCalendarDays: req.body.periodCountCalendarDays,
|
||||||
minimumBookingNotice: req.body.minimumBookingNotice
|
minimumBookingNotice:
|
||||||
? parseInt(req.body.minimumBookingNotice)
|
req.body.minimumBookingNotice || req.body.minimumBookingNotice === 0
|
||||||
: undefined,
|
? parseInt(req.body.minimumBookingNotice, 10)
|
||||||
|
: undefined,
|
||||||
price: req.body.price,
|
price: req.body.price,
|
||||||
currency: req.body.currency,
|
currency: req.body.currency,
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ it("can fit 24 hourly slots for an empty day", async () => {
|
||||||
// 24h in a day.
|
// 24h in a day.
|
||||||
expect(
|
expect(
|
||||||
getSlots({
|
getSlots({
|
||||||
inviteeDate: dayjs().add(1, "day"),
|
inviteeDate: dayjs.utc().add(1, "day").startOf("day"),
|
||||||
frequency: 60,
|
frequency: 60,
|
||||||
minimumBookingNotice: 0,
|
minimumBookingNotice: 0,
|
||||||
workingHours: [
|
workingHours: [
|
||||||
|
@ -30,11 +30,12 @@ it("can fit 24 hourly slots for an empty day", async () => {
|
||||||
).toHaveLength(24);
|
).toHaveLength(24);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip("only shows future booking slots on the same day", async () => {
|
// TODO: This test is sound; it should pass!
|
||||||
|
it("only shows future booking slots on the same day", async () => {
|
||||||
// The mock date is 1s to midday, so 12 slots should be open given 0 booking notice.
|
// The mock date is 1s to midday, so 12 slots should be open given 0 booking notice.
|
||||||
expect(
|
expect(
|
||||||
getSlots({
|
getSlots({
|
||||||
inviteeDate: dayjs(),
|
inviteeDate: dayjs.utc(),
|
||||||
frequency: 60,
|
frequency: 60,
|
||||||
minimumBookingNotice: 0,
|
minimumBookingNotice: 0,
|
||||||
workingHours: [
|
workingHours: [
|
||||||
|
@ -65,7 +66,7 @@ it("can cut off dates that due to invitee timezone differences fall on the next
|
||||||
).toHaveLength(0);
|
).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip("can cut off dates that due to invitee timezone differences fall on the previous day", async () => {
|
it("can cut off dates that due to invitee timezone differences fall on the previous day", async () => {
|
||||||
const workingHours = [
|
const workingHours = [
|
||||||
{
|
{
|
||||||
days: [0],
|
days: [0],
|
||||||
|
@ -75,10 +76,28 @@ it.skip("can cut off dates that due to invitee timezone differences fall on the
|
||||||
];
|
];
|
||||||
expect(
|
expect(
|
||||||
getSlots({
|
getSlots({
|
||||||
inviteeDate: dayjs().startOf("day"), // time translation -01:00
|
inviteeDate: dayjs().tz("Atlantic/Cape_Verde").startOf("day"), // time translation -01:00
|
||||||
frequency: 60,
|
frequency: 60,
|
||||||
minimumBookingNotice: 0,
|
minimumBookingNotice: 0,
|
||||||
workingHours,
|
workingHours,
|
||||||
})
|
})
|
||||||
).toHaveLength(0);
|
).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("adds minimum booking notice correctly", async () => {
|
||||||
|
// 24h in a day.
|
||||||
|
expect(
|
||||||
|
getSlots({
|
||||||
|
inviteeDate: dayjs.utc().add(1, "day").startOf("day"),
|
||||||
|
frequency: 60,
|
||||||
|
minimumBookingNotice: 1500,
|
||||||
|
workingHours: [
|
||||||
|
{
|
||||||
|
days: Array.from(Array(7).keys()),
|
||||||
|
startTime: MINUTES_DAY_START,
|
||||||
|
endTime: MINUTES_DAY_END,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toHaveLength(11);
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue