Check for working hours + rename isAvailable (#5342)
* Check for working hours + rename isAvailable * Return did not make it all the way back to callee * Removed redundant break clause * Fixes forEach returnpull/5365/head^2
parent
eb14c1b796
commit
328a354f4d
|
@ -21,7 +21,7 @@ import { cancelScheduledJobs, scheduleTrigger } from "@calcom/app-store/zapier/l
|
||||||
import EventManager from "@calcom/core/EventManager";
|
import EventManager from "@calcom/core/EventManager";
|
||||||
import { getEventName } from "@calcom/core/event";
|
import { getEventName } from "@calcom/core/event";
|
||||||
import { getUserAvailability } from "@calcom/core/getUserAvailability";
|
import { getUserAvailability } from "@calcom/core/getUserAvailability";
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs, { ConfigType } from "@calcom/dayjs";
|
||||||
import {
|
import {
|
||||||
sendAttendeeRequestEmail,
|
sendAttendeeRequestEmail,
|
||||||
sendOrganizerRequestEmail,
|
sendOrganizerRequestEmail,
|
||||||
|
@ -46,6 +46,7 @@ import { EventTypeMetaDataSchema, extendedBookingCreateBody } from "@calcom/pris
|
||||||
import type { BufferedBusyTime } from "@calcom/types/BufferedBusyTime";
|
import type { BufferedBusyTime } from "@calcom/types/BufferedBusyTime";
|
||||||
import type { AdditionalInformation, AppsStatus, CalendarEvent } from "@calcom/types/Calendar";
|
import type { AdditionalInformation, AppsStatus, CalendarEvent } from "@calcom/types/Calendar";
|
||||||
import type { EventResult, PartialReference } from "@calcom/types/EventManager";
|
import type { EventResult, PartialReference } from "@calcom/types/EventManager";
|
||||||
|
import { WorkingHours } from "@calcom/types/schedule";
|
||||||
|
|
||||||
import sendPayload, { EventTypeInfo } from "../../webhooks/lib/sendPayload";
|
import sendPayload, { EventTypeInfo } from "../../webhooks/lib/sendPayload";
|
||||||
|
|
||||||
|
@ -83,40 +84,56 @@ async function refreshCredentials(credentials: Array<Credential>): Promise<Array
|
||||||
return await async.mapLimit(credentials, 5, refreshCredential);
|
return await async.mapLimit(credentials, 5, refreshCredential);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAvailable(busyTimes: BufferedBusyTimes, time: dayjs.ConfigType, length: number): boolean {
|
const isWithinAvailableHours = (
|
||||||
// Check for conflicts
|
timeSlot: { start: ConfigType; end: ConfigType },
|
||||||
let t = true;
|
{
|
||||||
|
workingHours,
|
||||||
// Early return
|
}: {
|
||||||
if (!Array.isArray(busyTimes) || busyTimes.length < 1) return t;
|
workingHours: WorkingHours[];
|
||||||
|
}
|
||||||
let i = 0;
|
) => {
|
||||||
while (t === true && i < busyTimes.length) {
|
const timeSlotStart = dayjs(timeSlot.start).utc();
|
||||||
const busyTime = busyTimes[i];
|
const timeSlotEnd = dayjs(timeSlot.end).utc();
|
||||||
i++;
|
for (const workingHour of workingHours) {
|
||||||
const startTime = dayjs(busyTime.start);
|
// TODO: Double check & possibly fix timezone conversions.
|
||||||
const endTime = dayjs(busyTime.end);
|
const startTime = timeSlotStart.startOf("day").add(workingHour.startTime, "minute");
|
||||||
|
const endTime = timeSlotEnd.startOf("day").add(workingHour.endTime, "minute");
|
||||||
// Check if time is between start and end times
|
if (
|
||||||
if (dayjs(time).isBetween(startTime, endTime, null, "[)")) {
|
workingHour.days.includes(timeSlotStart.day()) &&
|
||||||
t = false;
|
// UTC mode, should be performant.
|
||||||
break;
|
timeSlotStart.isBetween(startTime, endTime, null, "[)") &&
|
||||||
}
|
timeSlotEnd.isBetween(startTime, endTime, null, "(]")
|
||||||
|
) {
|
||||||
// Check if slot end time is between start and end time
|
return true;
|
||||||
if (dayjs(time).add(length, "minutes").isBetween(startTime, endTime)) {
|
|
||||||
t = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if startTime is between slot
|
|
||||||
if (startTime.isBetween(dayjs(time), dayjs(time).add(length, "minutes"))) {
|
|
||||||
t = false;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return t;
|
// if true, there are conflicts.
|
||||||
|
function checkForConflicts(busyTimes: BufferedBusyTimes, time: dayjs.ConfigType, length: number) {
|
||||||
|
// Early return
|
||||||
|
if (!Array.isArray(busyTimes) || busyTimes.length < 1) {
|
||||||
|
return false; // guaranteed no conflicts when there is no busy times.
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const busyTime of busyTimes) {
|
||||||
|
const startTime = dayjs(busyTime.start);
|
||||||
|
const endTime = dayjs(busyTime.end);
|
||||||
|
// Check if time is between start and end times
|
||||||
|
if (dayjs(time).isBetween(startTime, endTime, null, "[)")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Check if slot end time is between start and end time
|
||||||
|
if (dayjs(time).add(length, "minutes").isBetween(startTime, endTime)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Check if startTime is between slot
|
||||||
|
if (startTime.isBetween(dayjs(time), dayjs(time).add(length, "minutes"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getEventTypesFromDB = async (eventTypeId: number) => {
|
const getEventTypesFromDB = async (eventTypeId: number) => {
|
||||||
|
@ -199,7 +216,7 @@ async function ensureAvailableUsers(
|
||||||
const availableUsers: typeof eventType.users = [];
|
const availableUsers: typeof eventType.users = [];
|
||||||
/** Let's start checking for availability */
|
/** Let's start checking for availability */
|
||||||
for (const user of eventType.users) {
|
for (const user of eventType.users) {
|
||||||
const { busy: bufferedBusyTimes } = await getUserAvailability(
|
const { busy: bufferedBusyTimes, workingHours } = await getUserAvailability(
|
||||||
{
|
{
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
eventTypeId: eventType.id,
|
eventTypeId: eventType.id,
|
||||||
|
@ -208,9 +225,22 @@ async function ensureAvailableUsers(
|
||||||
{ user, eventType }
|
{ user, eventType }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// check if time slot is outside of schedule.
|
||||||
|
if (
|
||||||
|
!isWithinAvailableHours(
|
||||||
|
{ start: input.dateFrom, end: input.dateTo },
|
||||||
|
{
|
||||||
|
workingHours,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// user does not have availability at this time, skip user.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
console.log("calendarBusyTimes==>>>", bufferedBusyTimes);
|
console.log("calendarBusyTimes==>>>", bufferedBusyTimes);
|
||||||
|
|
||||||
let isAvailableToBeBooked = true;
|
let foundConflict = true;
|
||||||
try {
|
try {
|
||||||
if (eventType.recurringEvent) {
|
if (eventType.recurringEvent) {
|
||||||
const recurringEvent = parseRecurringEvent(eventType.recurringEvent);
|
const recurringEvent = parseRecurringEvent(eventType.recurringEvent);
|
||||||
|
@ -219,23 +249,19 @@ async function ensureAvailableUsers(
|
||||||
// DONE: Decreased computational complexity from O(2^n) to O(n) by refactoring this loop to stop
|
// DONE: Decreased computational complexity from O(2^n) to O(n) by refactoring this loop to stop
|
||||||
// running at the first unavailable time.
|
// running at the first unavailable time.
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (isAvailableToBeBooked === true && i < allBookingDates.length) {
|
while (!foundConflict && i < allBookingDates.length) {
|
||||||
const aDate = allBookingDates[i];
|
foundConflict = checkForConflicts(bufferedBusyTimes, allBookingDates[i++], eventType.length);
|
||||||
i++;
|
|
||||||
isAvailableToBeBooked = isAvailable(bufferedBusyTimes, aDate, eventType.length);
|
|
||||||
// We bail at the first false, we don't need to keep checking
|
|
||||||
if (!isAvailableToBeBooked) break;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
isAvailableToBeBooked = isAvailable(bufferedBusyTimes, input.dateFrom, eventType.length);
|
foundConflict = checkForConflicts(bufferedBusyTimes, input.dateFrom, eventType.length);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
log.debug({
|
log.debug({
|
||||||
message: "Unable set isAvailableToBeBooked. Using true. ",
|
message: "Unable set isAvailableToBeBooked. Using true. ",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// no conflicts found, add to available users.
|
||||||
if (isAvailableToBeBooked) {
|
if (!foundConflict) {
|
||||||
availableUsers.push(user);
|
availableUsers.push(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue