Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
adriangonzales committed Jul 6, 2022
1 parent ed6d8dd commit 2a6492d
Showing 1 changed file with 50 additions and 51 deletions.
101 changes: 50 additions & 51 deletions apps/web/server/routers/viewer/slots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { z } from "zod";

import type { CurrentSeats } from "@calcom/core/getUserAvailability";
import { getUserAvailability } from "@calcom/core/getUserAvailability";
import { yyyymmdd } from "@calcom/lib/date-fns";
import { availabilityUserSelect } from "@calcom/prisma";
import { stringToDayjs } from "@calcom/prisma/zod-utils";
import { TimeRange, WorkingHours } from "@calcom/types/schedule";
import { TimeRange } from "@calcom/types/schedule";

import isOutOfBounds from "@lib/isOutOfBounds";
import getSlots from "@lib/slots";

import { createRouter } from "@server/createRouter";
Expand All @@ -17,11 +16,13 @@ import { TRPCError } from "@trpc/server";
const getScheduleSchema = z
.object({
// startTime ISOString
startTime: stringToDayjs,
startTime: z.string(),
// endTime ISOString
endTime: stringToDayjs,
endTime: z.string(),
// Event type ID
eventTypeId: z.number().optional(),
// invitee timezone
timeZone: z.string().optional(),
// or list of users (for dynamic events)
usernameList: z.array(z.string()).optional(),
})
Expand All @@ -40,54 +41,32 @@ export type Slot = {
const checkForAvailability = ({
time,
busy,
workingHours,
eventLength,
beforeBufferTime,
afterBufferTime,
currentSeats,
}: {
time: Dayjs;
busy: (TimeRange | { start: string; end: string })[];
workingHours: WorkingHours[];
eventLength: number;
beforeBufferTime: number;
afterBufferTime: number;
currentSeats?: CurrentSeats;
}) => {
if (
!workingHours.some((workingHour) => {
if (!workingHour.days.includes(time.day())) {
return false;
}
if (
!time.isBetween(
time.utc().startOf("day").add(workingHour.startTime, "minutes"),
time.utc().startOf("day").add(workingHour.endTime, "minutes"),
null,
"[)"
)
) {
return false;
}
return true;
})
) {
return false;
}

if (currentSeats?.some((booking) => booking.startTime.toISOString() === time.toISOString())) {
return true;
}

const slotEndTime = time.add(eventLength, "minutes");
const slotStartTimeWithBeforeBuffer = time.subtract(beforeBufferTime, "minutes");
const slotEndTimeWithAfterBuffer = time.add(eventLength + afterBufferTime, "minutes");
const slotEndTime = time.add(eventLength, "minutes").utc();
const slotStartTimeWithBeforeBuffer = time.subtract(beforeBufferTime, "minutes").utc();
const slotEndTimeWithAfterBuffer = time.add(eventLength + afterBufferTime, "minutes").utc();

return busy.every((busyTime) => {
const startTime = dayjs.utc(busyTime.start);
const endTime = dayjs.utc(busyTime.end);

return busy.every((busyTime): boolean => {
const startTime = dayjs(busyTime.start);
const endTime = dayjs(busyTime.end);
// Check if start times are the same
if (time.isBetween(startTime, endTime, null, "[)")) {
if (time.utc().isBetween(startTime, endTime, null, "[)")) {
return false;
}
// Check if slot end time is between start and end time
Expand Down Expand Up @@ -116,6 +95,7 @@ const checkForAvailability = ({
) {
return false;
}

return true;
});
};
Expand All @@ -137,6 +117,11 @@ export const slotsRouter = createRouter().query("getSchedule", {
beforeEventBuffer: true,
afterEventBuffer: true,
schedulingType: true,
periodType: true,
periodStartDate: true,
periodEndDate: true,
periodCountCalendarDays: true,
periodDays: true,
schedule: {
select: {
availability: true,
Expand All @@ -163,11 +148,16 @@ export const slotsRouter = createRouter().query("getSchedule", {
throw new TRPCError({ code: "NOT_FOUND" });
}

const { startTime, endTime } = input;
const startTime =
input.timeZone === "Etc/GMT"
? dayjs.utc(input.startTime)
: dayjs(input.startTime).utc().tz(input.timeZone);
const endTime =
input.timeZone === "Etc/GMT" ? dayjs.utc(input.endTime) : dayjs(input.endTime).utc().tz(input.timeZone);

if (!startTime.isValid() || !endTime.isValid()) {
throw new TRPCError({ message: "Invalid time range given.", code: "BAD_REQUEST" });
}

let currentSeats: CurrentSeats | undefined = undefined;

const userSchedules = await Promise.all(
Expand Down Expand Up @@ -195,15 +185,25 @@ export const slotsRouter = createRouter().query("getSchedule", {
);

const workingHours = userSchedules.flatMap((s) => s.workingHours);

const slots: Record<string, Slot[]> = {};
const availabilityCheckProps = {
eventLength: eventType.length,
beforeBufferTime: eventType.beforeEventBuffer,
afterBufferTime: eventType.afterEventBuffer,
currentSeats,
};
const isWithinBounds = (_time: Parameters<typeof isOutOfBounds>[0]) =>
!isOutOfBounds(_time, {
periodType: eventType.periodType,
periodStartDate: eventType.periodStartDate,
periodEndDate: eventType.periodEndDate,
periodCountCalendarDays: eventType.periodCountCalendarDays,
periodDays: eventType.periodDays,
});

let time = startTime;

let time = dayjs(startTime);
do {
// get slots retrieves the available times for a given day
const times = getSlots({
Expand All @@ -215,20 +215,19 @@ export const slotsRouter = createRouter().query("getSchedule", {
});

// if ROUND_ROBIN - slots stay available on some() - if normal / COLLECTIVE - slots only stay available on every()
const filteredTimes =
const filterStrategy =
!eventType.schedulingType || eventType.schedulingType === SchedulingType.COLLECTIVE
? times.filter((time) =>
userSchedules.every((schedule) =>
checkForAvailability({ time, ...schedule, ...availabilityCheckProps })
)
)
: times.filter((time) =>
userSchedules.some((schedule) =>
checkForAvailability({ time, ...schedule, ...availabilityCheckProps })
)
);

slots[yyyymmdd(time.toDate())] = filteredTimes.map((time) => ({
? ("every" as const)
: ("some" as const);
const filteredTimes = times
.filter(isWithinBounds)
.filter((time) =>
userSchedules[filterStrategy]((schedule) =>
checkForAvailability({ time, ...schedule, ...availabilityCheckProps })
)
);

slots[time.format("YYYY-MM-DD")] = filteredTimes.map((time) => ({
time: time.toISOString(),
users: eventType.users.map((user) => user.username || ""),
// Conditionally add the attendees and booking id to slots object if there is already a booking during that time
Expand Down

0 comments on commit 2a6492d

Please sign in to comment.