2023-02-16 22:39:57 +00:00
|
|
|
import type { Dayjs } from "@calcom/dayjs";
|
|
|
|
import dayjs from "@calcom/dayjs";
|
|
|
|
import type { WorkingHours, TimeRange as DateOverride } from "@calcom/types/schedule";
|
2022-07-22 17:27:06 +00:00
|
|
|
|
|
|
|
import { getWorkingHours } from "./availability";
|
2023-03-17 11:53:05 +00:00
|
|
|
import { getTimeZone } from "./date-fns";
|
2023-07-05 16:47:41 +00:00
|
|
|
import type { DateRange } from "./date-ranges";
|
2022-07-22 17:27:06 +00:00
|
|
|
|
|
|
|
export type GetSlots = {
|
|
|
|
inviteeDate: Dayjs;
|
|
|
|
frequency: number;
|
2023-07-05 16:47:41 +00:00
|
|
|
workingHours?: WorkingHours[];
|
2022-12-14 17:30:55 +00:00
|
|
|
dateOverrides?: DateOverride[];
|
2023-07-05 16:47:41 +00:00
|
|
|
dateRanges?: DateRange[];
|
2022-07-22 17:27:06 +00:00
|
|
|
minimumBookingNotice: number;
|
|
|
|
eventLength: number;
|
2023-07-05 16:47:41 +00:00
|
|
|
offsetStart?: number;
|
2023-03-03 16:33:16 +00:00
|
|
|
organizerTimeZone: string;
|
2022-07-22 17:27:06 +00:00
|
|
|
};
|
2022-12-21 19:32:42 +00:00
|
|
|
export type TimeFrame = { userIds?: number[]; startTime: number; endTime: number };
|
2022-07-22 17:27:06 +00:00
|
|
|
|
2022-12-20 17:49:24 +00:00
|
|
|
const minimumOfOne = (input: number) => (input < 1 ? 1 : input);
|
2023-05-17 11:56:55 +00:00
|
|
|
const minimumOfZero = (input: number) => (input < 0 ? 0 : input);
|
2022-12-20 17:49:24 +00:00
|
|
|
|
2022-12-14 17:30:55 +00:00
|
|
|
function buildSlots({
|
|
|
|
startOfInviteeDay,
|
|
|
|
computedLocalAvailability,
|
|
|
|
frequency,
|
|
|
|
eventLength,
|
2023-07-05 16:47:41 +00:00
|
|
|
offsetStart = 0,
|
2022-12-14 17:30:55 +00:00
|
|
|
startDate,
|
2023-03-03 16:33:16 +00:00
|
|
|
organizerTimeZone,
|
|
|
|
inviteeTimeZone,
|
2022-12-14 17:30:55 +00:00
|
|
|
}: {
|
|
|
|
computedLocalAvailability: TimeFrame[];
|
|
|
|
startOfInviteeDay: Dayjs;
|
|
|
|
startDate: Dayjs;
|
|
|
|
frequency: number;
|
|
|
|
eventLength: number;
|
2023-07-05 16:47:41 +00:00
|
|
|
offsetStart?: number;
|
2023-03-03 16:33:16 +00:00
|
|
|
organizerTimeZone: string;
|
|
|
|
inviteeTimeZone: string;
|
2022-12-14 17:30:55 +00:00
|
|
|
}) {
|
2023-01-11 17:33:34 +00:00
|
|
|
// no slots today
|
|
|
|
if (startOfInviteeDay.isBefore(startDate, "day")) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
// keep the old safeguards in; may be needed.
|
|
|
|
frequency = minimumOfOne(frequency);
|
|
|
|
eventLength = minimumOfOne(eventLength);
|
2023-05-17 11:56:55 +00:00
|
|
|
offsetStart = minimumOfZero(offsetStart);
|
|
|
|
|
2023-01-11 17:33:34 +00:00
|
|
|
// A day starts at 00:00 unless the startDate is the same as the current day
|
|
|
|
const dayStart = startOfInviteeDay.isSame(startDate, "day")
|
|
|
|
? Math.ceil((startDate.hour() * 60 + startDate.minute()) / frequency) * frequency
|
|
|
|
: 0;
|
2022-12-14 17:30:55 +00:00
|
|
|
|
2023-01-11 17:33:34 +00:00
|
|
|
// Record type so we can use slotStart as key
|
|
|
|
const slotsTimeFrameAvailable: Record<
|
|
|
|
string,
|
|
|
|
{
|
|
|
|
userIds: number[];
|
|
|
|
startTime: number;
|
|
|
|
endTime: number;
|
|
|
|
}
|
|
|
|
> = {};
|
|
|
|
// get boundaries sorted by start time.
|
|
|
|
const boundaries = computedLocalAvailability
|
|
|
|
.map((item) => [item.startTime < dayStart ? dayStart : item.startTime, item.endTime])
|
|
|
|
.sort((a, b) => a[0] - b[0]);
|
|
|
|
|
|
|
|
const ranges: number[][] = [];
|
|
|
|
let currentRange: number[] = [];
|
|
|
|
for (const [start, end] of boundaries) {
|
|
|
|
// bypass invalid value
|
|
|
|
if (start >= end) continue;
|
|
|
|
// fill first elem
|
|
|
|
if (!currentRange.length) {
|
|
|
|
currentRange = [start, end];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (currentRange[1] < start) {
|
|
|
|
ranges.push(currentRange);
|
|
|
|
currentRange = [start, end];
|
|
|
|
} else if (currentRange[1] < end) {
|
|
|
|
currentRange[1] = end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (currentRange) {
|
|
|
|
ranges.push(currentRange);
|
|
|
|
}
|
2022-12-21 19:32:42 +00:00
|
|
|
|
2023-01-11 17:33:34 +00:00
|
|
|
for (const [boundaryStart, boundaryEnd] of ranges) {
|
|
|
|
// loop through the day, based on frequency.
|
2023-05-17 11:56:55 +00:00
|
|
|
for (
|
|
|
|
let slotStart = boundaryStart + offsetStart;
|
|
|
|
slotStart < boundaryEnd;
|
|
|
|
slotStart += offsetStart + frequency
|
|
|
|
) {
|
2023-01-11 17:33:34 +00:00
|
|
|
computedLocalAvailability.forEach((item) => {
|
|
|
|
// TODO: This logic does not allow for past-midnight bookings.
|
2023-03-17 20:02:56 +00:00
|
|
|
if (slotStart < item.startTime || slotStart > item.endTime + 1 - eventLength) {
|
2023-01-11 17:33:34 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
slotsTimeFrameAvailable[slotStart.toString()] = {
|
|
|
|
userIds: (slotsTimeFrameAvailable[slotStart]?.userIds || []).concat(item.userIds || []),
|
|
|
|
startTime: slotStart,
|
|
|
|
endTime: slotStart + eventLength,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2023-03-03 16:33:16 +00:00
|
|
|
|
2023-03-17 11:53:05 +00:00
|
|
|
const organizerDSTDiff =
|
|
|
|
dayjs().tz(organizerTimeZone).utcOffset() - startOfInviteeDay.tz(organizerTimeZone).utcOffset();
|
|
|
|
const inviteeDSTDiff =
|
|
|
|
dayjs().tz(inviteeTimeZone).utcOffset() - startOfInviteeDay.tz(inviteeTimeZone).utcOffset();
|
2023-01-11 17:33:34 +00:00
|
|
|
const slots: { time: Dayjs; userIds?: number[] }[] = [];
|
2023-03-03 16:33:16 +00:00
|
|
|
const getTime = (time: number) => {
|
2023-03-17 11:53:05 +00:00
|
|
|
const minutes = time + organizerDSTDiff - inviteeDSTDiff;
|
2023-03-03 16:33:16 +00:00
|
|
|
|
|
|
|
return startOfInviteeDay.tz(inviteeTimeZone).add(minutes, "minutes");
|
|
|
|
};
|
2023-01-11 17:33:34 +00:00
|
|
|
for (const item of Object.values(slotsTimeFrameAvailable)) {
|
2022-12-14 17:30:55 +00:00
|
|
|
/*
|
|
|
|
* @calcom/web:dev: 2022-11-06T00:00:00-04:00
|
|
|
|
* @calcom/web:dev: 2022-11-06T01:00:00-04:00
|
|
|
|
* @calcom/web:dev: 2022-11-06T01:00:00-04:00 <-- note there is no offset change, but we did lose an hour.
|
|
|
|
* @calcom/web:dev: 2022-11-06T02:00:00-04:00
|
|
|
|
* @calcom/web:dev: 2022-11-06T03:00:00-04:00
|
|
|
|
* ...
|
|
|
|
*/
|
2023-03-13 09:59:19 +00:00
|
|
|
slots.push({
|
2022-12-21 19:32:42 +00:00
|
|
|
userIds: item.userIds,
|
2023-03-03 16:33:16 +00:00
|
|
|
time: getTime(item.startTime),
|
2023-03-13 09:59:19 +00:00
|
|
|
});
|
2023-01-11 17:33:34 +00:00
|
|
|
}
|
2023-07-05 16:47:41 +00:00
|
|
|
|
|
|
|
return slots;
|
|
|
|
}
|
|
|
|
|
|
|
|
function buildSlotsWithDateRanges({
|
|
|
|
dateRanges,
|
|
|
|
frequency,
|
|
|
|
eventLength,
|
|
|
|
timeZone,
|
|
|
|
minimumBookingNotice,
|
|
|
|
organizerTimeZone,
|
|
|
|
offsetStart,
|
|
|
|
}: {
|
|
|
|
dateRanges: DateRange[];
|
|
|
|
frequency: number;
|
|
|
|
eventLength: number;
|
|
|
|
timeZone: string;
|
|
|
|
minimumBookingNotice: number;
|
|
|
|
organizerTimeZone: string;
|
|
|
|
offsetStart?: number;
|
|
|
|
}) {
|
|
|
|
// keep the old safeguards in; may be needed.
|
|
|
|
frequency = minimumOfOne(frequency);
|
|
|
|
eventLength = minimumOfOne(eventLength);
|
|
|
|
offsetStart = offsetStart ? minimumOfOne(offsetStart) : 0;
|
|
|
|
const slots: { time: Dayjs; userIds?: number[] }[] = [];
|
2023-07-18 00:57:34 +00:00
|
|
|
|
2023-07-05 16:47:41 +00:00
|
|
|
dateRanges.forEach((range) => {
|
|
|
|
const startTimeWithMinNotice = dayjs.utc().add(minimumBookingNotice, "minute");
|
|
|
|
|
2023-08-02 17:53:19 +00:00
|
|
|
let slotStartTime = range.start.utc().isAfter(startTimeWithMinNotice)
|
|
|
|
? range.start
|
|
|
|
: startTimeWithMinNotice;
|
2023-07-05 16:47:41 +00:00
|
|
|
|
2023-07-14 22:57:05 +00:00
|
|
|
let interval = 15;
|
fix: better slot starting times
## What does this PR do?
Currently, we start the first slot always at the nearest 15 minutes. This is not ideal as for some duration other slot starting time make more sense. So with this PR the starting times are defined as follow:
- Frequency is exact hours (60, 120, 180, ...), slot start time is a full hour
- Frequency is half hours (30, 90, ...), slot start time is half or full hours (8:00, 8:30, ...)
- Same with 20-minute events (20, 40, ...) and 10-minute events
- Everything else will start at the nearest 15 min slot
It also fixes that slot times are shifted when there is a busy slot with a different duration. Here is a before and after of a 30-min event with a 5-minute busy slot at 1:00 pm
Before:
![Screenshot 2023-07-07 at 13 31 45](https://github.com/calcom/cal.com/assets/30310907/b92d4ff4-49f1-48f4-a973-99266f61d919)
After
![Screenshot 2023-07-07 at 13 34 01](https://github.com/calcom/cal.com/assets/30310907/042c7ef7-8c2a-4cd9-b663-183bc07b5864)
#### 30 Minute events, availability starting at 7:15
Before:
![Screenshot 2023-07-06 at 12 40 00](https://github.com/calcom/cal.com/assets/30310907/752ed978-83cf-4ee9-a38d-b5795df6daec)
After:
![Screenshot 2023-07-06 at 12 40 42](https://github.com/calcom/cal.com/assets/30310907/5d51ec15-5be8-4f3b-b374-46dad35216b8)
## Type of change
- Bug fix (non-breaking change which fixes an issue)
## How should this be tested?
- Check if slot times are shown as described
- Test with different intervals/durations
- Test with busy times
- Test with different availabilities
## Mandatory Tasks
- [x] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.
2023-07-10 22:32:26 +00:00
|
|
|
|
2023-07-14 22:57:05 +00:00
|
|
|
const intervalsWithDefinedStartTimes = [60, 30, 20, 10];
|
fix: better slot starting times
## What does this PR do?
Currently, we start the first slot always at the nearest 15 minutes. This is not ideal as for some duration other slot starting time make more sense. So with this PR the starting times are defined as follow:
- Frequency is exact hours (60, 120, 180, ...), slot start time is a full hour
- Frequency is half hours (30, 90, ...), slot start time is half or full hours (8:00, 8:30, ...)
- Same with 20-minute events (20, 40, ...) and 10-minute events
- Everything else will start at the nearest 15 min slot
It also fixes that slot times are shifted when there is a busy slot with a different duration. Here is a before and after of a 30-min event with a 5-minute busy slot at 1:00 pm
Before:
![Screenshot 2023-07-07 at 13 31 45](https://github.com/calcom/cal.com/assets/30310907/b92d4ff4-49f1-48f4-a973-99266f61d919)
After
![Screenshot 2023-07-07 at 13 34 01](https://github.com/calcom/cal.com/assets/30310907/042c7ef7-8c2a-4cd9-b663-183bc07b5864)
#### 30 Minute events, availability starting at 7:15
Before:
![Screenshot 2023-07-06 at 12 40 00](https://github.com/calcom/cal.com/assets/30310907/752ed978-83cf-4ee9-a38d-b5795df6daec)
After:
![Screenshot 2023-07-06 at 12 40 42](https://github.com/calcom/cal.com/assets/30310907/5d51ec15-5be8-4f3b-b374-46dad35216b8)
## Type of change
- Bug fix (non-breaking change which fixes an issue)
## How should this be tested?
- Check if slot times are shown as described
- Test with different intervals/durations
- Test with busy times
- Test with different availabilities
## Mandatory Tasks
- [x] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.
2023-07-10 22:32:26 +00:00
|
|
|
|
2023-07-14 22:57:05 +00:00
|
|
|
for (let i = 0; i < intervalsWithDefinedStartTimes.length; i++) {
|
|
|
|
if (frequency % intervalsWithDefinedStartTimes[i] === 0) {
|
|
|
|
interval = intervalsWithDefinedStartTimes[i];
|
|
|
|
break;
|
fix: better slot starting times
## What does this PR do?
Currently, we start the first slot always at the nearest 15 minutes. This is not ideal as for some duration other slot starting time make more sense. So with this PR the starting times are defined as follow:
- Frequency is exact hours (60, 120, 180, ...), slot start time is a full hour
- Frequency is half hours (30, 90, ...), slot start time is half or full hours (8:00, 8:30, ...)
- Same with 20-minute events (20, 40, ...) and 10-minute events
- Everything else will start at the nearest 15 min slot
It also fixes that slot times are shifted when there is a busy slot with a different duration. Here is a before and after of a 30-min event with a 5-minute busy slot at 1:00 pm
Before:
![Screenshot 2023-07-07 at 13 31 45](https://github.com/calcom/cal.com/assets/30310907/b92d4ff4-49f1-48f4-a973-99266f61d919)
After
![Screenshot 2023-07-07 at 13 34 01](https://github.com/calcom/cal.com/assets/30310907/042c7ef7-8c2a-4cd9-b663-183bc07b5864)
#### 30 Minute events, availability starting at 7:15
Before:
![Screenshot 2023-07-06 at 12 40 00](https://github.com/calcom/cal.com/assets/30310907/752ed978-83cf-4ee9-a38d-b5795df6daec)
After:
![Screenshot 2023-07-06 at 12 40 42](https://github.com/calcom/cal.com/assets/30310907/5d51ec15-5be8-4f3b-b374-46dad35216b8)
## Type of change
- Bug fix (non-breaking change which fixes an issue)
## How should this be tested?
- Check if slot times are shown as described
- Test with different intervals/durations
- Test with busy times
- Test with different availabilities
## Mandatory Tasks
- [x] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.
2023-07-10 22:32:26 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-05 16:47:41 +00:00
|
|
|
|
2023-07-14 22:57:05 +00:00
|
|
|
slotStartTime =
|
2023-07-18 00:57:34 +00:00
|
|
|
slotStartTime.minute() % interval !== 0
|
2023-07-14 22:57:05 +00:00
|
|
|
? slotStartTime.startOf("hour").add(Math.ceil(slotStartTime.minute() / interval) * interval, "minute")
|
|
|
|
: slotStartTime;
|
|
|
|
|
2023-07-05 16:47:41 +00:00
|
|
|
// Adding 1 minute to date ranges that end at midnight to ensure that the last slot is included
|
|
|
|
const rangeEnd = range.end
|
|
|
|
.add(dayjs().tz(organizerTimeZone).utcOffset(), "minutes")
|
|
|
|
.isSame(range.end.endOf("day").add(dayjs().tz(organizerTimeZone).utcOffset(), "minutes"), "minute")
|
|
|
|
? range.end.add(1, "minute")
|
|
|
|
: range.end;
|
|
|
|
|
2023-08-02 17:53:19 +00:00
|
|
|
slotStartTime = slotStartTime.add(offsetStart ?? 0, "minutes").tz(timeZone);
|
|
|
|
|
|
|
|
while (!slotStartTime.add(eventLength, "minutes").subtract(1, "second").utc().isAfter(rangeEnd)) {
|
2023-07-05 16:47:41 +00:00
|
|
|
slots.push({
|
2023-08-02 17:53:19 +00:00
|
|
|
time: slotStartTime,
|
2023-07-05 16:47:41 +00:00
|
|
|
});
|
|
|
|
slotStartTime = slotStartTime.add(frequency + (offsetStart ?? 0), "minutes");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-01-11 17:33:34 +00:00
|
|
|
return slots;
|
2022-12-14 17:30:55 +00:00
|
|
|
}
|
|
|
|
|
2023-01-18 19:26:07 +00:00
|
|
|
function fromIndex<T>(cb: (val: T, i: number, a: T[]) => boolean, index: number) {
|
|
|
|
return function (e: T, i: number, a: T[]) {
|
|
|
|
return i >= index && cb(e, i, a);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-12-14 17:30:55 +00:00
|
|
|
const getSlots = ({
|
|
|
|
inviteeDate,
|
|
|
|
frequency,
|
|
|
|
minimumBookingNotice,
|
2023-07-05 16:47:41 +00:00
|
|
|
workingHours = [],
|
2022-12-14 17:30:55 +00:00
|
|
|
dateOverrides = [],
|
2023-07-05 16:47:41 +00:00
|
|
|
dateRanges,
|
2022-12-14 17:30:55 +00:00
|
|
|
eventLength,
|
2023-07-05 16:47:41 +00:00
|
|
|
offsetStart = 0,
|
2023-03-03 16:33:16 +00:00
|
|
|
organizerTimeZone,
|
2022-12-14 17:30:55 +00:00
|
|
|
}: GetSlots) => {
|
2023-07-05 16:47:41 +00:00
|
|
|
if (dateRanges) {
|
|
|
|
const slots = buildSlotsWithDateRanges({
|
|
|
|
dateRanges,
|
|
|
|
frequency,
|
|
|
|
eventLength,
|
|
|
|
timeZone: getTimeZone(inviteeDate),
|
|
|
|
minimumBookingNotice,
|
|
|
|
organizerTimeZone,
|
|
|
|
offsetStart,
|
|
|
|
});
|
|
|
|
return slots;
|
|
|
|
}
|
|
|
|
|
2022-07-22 17:27:06 +00:00
|
|
|
// current date in invitee tz
|
2023-01-12 21:09:12 +00:00
|
|
|
const startDate = dayjs().utcOffset(inviteeDate.utcOffset()).add(minimumBookingNotice, "minute");
|
2023-07-05 16:47:41 +00:00
|
|
|
|
2022-10-07 22:10:48 +00:00
|
|
|
// This code is ran client side, startOf() does some conversions based on the
|
|
|
|
// local tz of the client. Sometimes this shifts the day incorrectly.
|
|
|
|
const startOfDayUTC = dayjs.utc().set("hour", 0).set("minute", 0).set("second", 0);
|
2022-07-22 17:27:06 +00:00
|
|
|
const startOfInviteeDay = inviteeDate.startOf("day");
|
|
|
|
// checks if the start date is in the past
|
|
|
|
|
|
|
|
/**
|
2023-03-17 11:53:05 +00:00
|
|
|
* TODO: change "day" for "hour" to stop displaying 1 day before today
|
2022-07-22 17:27:06 +00:00
|
|
|
* This is displaying a day as available as sometimes difference between two dates is < 24 hrs.
|
|
|
|
* But when doing timezones an available day for an owner can be 2 days available in other users tz.
|
|
|
|
*
|
|
|
|
* */
|
|
|
|
if (inviteeDate.isBefore(startDate, "day")) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2023-03-03 16:33:16 +00:00
|
|
|
const timeZone: string = getTimeZone(inviteeDate);
|
2022-12-14 17:30:55 +00:00
|
|
|
const workingHoursUTC = workingHours.map((schedule) => ({
|
2022-12-21 19:32:42 +00:00
|
|
|
userId: schedule.userId,
|
2022-12-14 17:30:55 +00:00
|
|
|
days: schedule.days,
|
|
|
|
startTime: /* Why? */ startOfDayUTC.add(schedule.startTime, "minute"),
|
|
|
|
endTime: /* Why? */ startOfDayUTC.add(schedule.endTime, "minute"),
|
|
|
|
}));
|
|
|
|
|
2022-10-07 22:10:48 +00:00
|
|
|
const localWorkingHours = getWorkingHours(
|
|
|
|
{
|
|
|
|
// initialize current day with timeZone without conversion, just parse.
|
|
|
|
utcOffset: -dayjs.tz(dayjs(), timeZone).utcOffset(),
|
|
|
|
},
|
|
|
|
workingHoursUTC
|
|
|
|
).filter((hours) => hours.days.includes(inviteeDate.day()));
|
2022-07-22 17:27:06 +00:00
|
|
|
|
|
|
|
// Here we split working hour in chunks for every frequency available that can fit in whole working hours
|
2022-12-14 17:30:55 +00:00
|
|
|
const computedLocalAvailability: TimeFrame[] = [];
|
|
|
|
let tempComputeTimeFrame: TimeFrame | undefined;
|
2022-07-22 17:27:06 +00:00
|
|
|
const computeLength = localWorkingHours.length - 1;
|
2023-02-16 22:39:57 +00:00
|
|
|
const makeTimeFrame = (item: (typeof localWorkingHours)[0]): TimeFrame => ({
|
2022-12-21 19:32:42 +00:00
|
|
|
userIds: item.userId ? [item.userId] : [],
|
2022-07-22 17:27:06 +00:00
|
|
|
startTime: item.startTime,
|
|
|
|
endTime: item.endTime,
|
|
|
|
});
|
2022-12-14 17:30:55 +00:00
|
|
|
|
2022-07-22 17:27:06 +00:00
|
|
|
localWorkingHours.forEach((item, index) => {
|
|
|
|
if (!tempComputeTimeFrame) {
|
|
|
|
tempComputeTimeFrame = makeTimeFrame(item);
|
|
|
|
} else {
|
|
|
|
// please check the comment in splitAvailableTime func for the added 1 minute
|
|
|
|
if (tempComputeTimeFrame.endTime + 1 === item.startTime) {
|
|
|
|
// to deal with time that across the day, e.g. from 11:59 to to 12:01
|
|
|
|
tempComputeTimeFrame.endTime = item.endTime;
|
|
|
|
} else {
|
2022-12-14 17:30:55 +00:00
|
|
|
computedLocalAvailability.push(tempComputeTimeFrame);
|
2022-07-22 17:27:06 +00:00
|
|
|
tempComputeTimeFrame = makeTimeFrame(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (index == computeLength) {
|
2022-12-14 17:30:55 +00:00
|
|
|
computedLocalAvailability.push(tempComputeTimeFrame);
|
2022-07-22 17:27:06 +00:00
|
|
|
}
|
|
|
|
});
|
2022-12-21 19:32:42 +00:00
|
|
|
// an override precedes all the local working hour availability logic.
|
|
|
|
const activeOverrides = dateOverrides.filter((override) => {
|
|
|
|
return dayjs.utc(override.start).isBetween(startOfInviteeDay, startOfInviteeDay.endOf("day"), null, "[)");
|
|
|
|
});
|
2022-07-22 17:27:06 +00:00
|
|
|
|
2023-07-05 16:47:41 +00:00
|
|
|
if (activeOverrides.length) {
|
2023-04-19 08:43:08 +00:00
|
|
|
const overrides = activeOverrides.flatMap((override) => ({
|
|
|
|
userIds: override.userId ? [override.userId] : [],
|
|
|
|
startTime: override.start.getUTCHours() * 60 + override.start.getUTCMinutes(),
|
|
|
|
endTime: override.end.getUTCHours() * 60 + override.end.getUTCMinutes(),
|
|
|
|
}));
|
2023-01-18 19:26:07 +00:00
|
|
|
// unset all working hours that relate to this user availability override
|
2022-12-21 19:32:42 +00:00
|
|
|
overrides.forEach((override) => {
|
2023-01-18 19:26:07 +00:00
|
|
|
let i = -1;
|
|
|
|
const indexes: number[] = [];
|
|
|
|
while (
|
|
|
|
(i = computedLocalAvailability.findIndex(
|
|
|
|
fromIndex(
|
|
|
|
(a) => !a.userIds?.length || (!!override.userIds[0] && a.userIds?.includes(override.userIds[0])),
|
|
|
|
i + 1
|
|
|
|
)
|
|
|
|
)) != -1
|
|
|
|
) {
|
|
|
|
indexes.push(i);
|
2022-12-21 19:32:42 +00:00
|
|
|
}
|
2023-01-18 19:26:07 +00:00
|
|
|
// work backwards as splice modifies the original array.
|
|
|
|
indexes.reverse().forEach((idx) => computedLocalAvailability.splice(idx, 1));
|
2022-12-21 19:32:42 +00:00
|
|
|
});
|
2023-01-18 19:26:07 +00:00
|
|
|
// and push all overrides as new computed availability
|
|
|
|
computedLocalAvailability.push(...overrides);
|
2022-12-21 19:32:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return buildSlots({
|
|
|
|
computedLocalAvailability,
|
|
|
|
startOfInviteeDay,
|
|
|
|
startDate,
|
|
|
|
frequency,
|
|
|
|
eventLength,
|
2023-05-17 11:56:55 +00:00
|
|
|
offsetStart,
|
2023-03-03 16:33:16 +00:00
|
|
|
organizerTimeZone,
|
|
|
|
inviteeTimeZone: timeZone,
|
2022-12-21 19:32:42 +00:00
|
|
|
});
|
2022-07-22 17:27:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export default getSlots;
|