diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 309c84d10a..f7b9b69008 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,8 +12,7 @@ /packages/app-store/larkcalendar/**/* @calcom/Foundation /packages/app-store/office365calendar/**/* @calcom/Foundation /packages/app-store/zohocalendar/**/* @calcom/Foundation -/packages/core/getAggregatedAvailability/index.ts @calcom/Foundation -/packages/core/getAggregateWorkingHours.ts @calcom/Foundation +/packages/core/getAggregatedAvailability.ts @calcom/Foundation /packages/core/getUserAvailability.ts @calcom/Foundation /packages/features/bookings/lib/**/* @calcom/Foundation /packages/lib/server/getLuckyUser.ts @calcom/Foundation diff --git a/apps/web/test/lib/getAggregateWorkingHours.test.ts b/apps/web/test/lib/getAggregateWorkingHours.test.ts deleted file mode 100644 index 9cb2688eb4..0000000000 --- a/apps/web/test/lib/getAggregateWorkingHours.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { expect, it, beforeAll, vi } from "vitest"; - -import { getAggregateWorkingHours } from "@calcom/core/getAggregateWorkingHours"; - -beforeAll(() => { - vi.setSystemTime(new Date("2021-06-20T11:59:59Z")); -}); - -const HAWAII_AND_NEWYORK_TEAM = [ - { - timeZone: "America/Detroit", // GMT -4 per 22th of Aug, 2022 - workingHours: [{ userId: 1, days: [1, 2, 3, 4, 5], startTime: 780, endTime: 1260 }], - busy: [], - dateOverrides: [], - datesOutOfOffice: {}, - }, - { - timeZone: "Pacific/Honolulu", // GMT -10 per 22th of Aug, 2022 - workingHours: [ - { userId: 1, days: [3, 4, 5], startTime: 0, endTime: 360 }, - { userId: 2, days: [6], startTime: 0, endTime: 180 }, - { userId: 3, days: [2, 3, 4], startTime: 780, endTime: 1439 }, - { userId: 4, days: [5], startTime: 780, endTime: 1439 }, - ], - busy: [], - dateOverrides: [], - datesOutOfOffice: {}, - }, -]; - -/* TODO: Make this test more "professional" */ -it("Sydney and Shiraz can live in harmony 🙏", async () => { - expect(getAggregateWorkingHours(HAWAII_AND_NEWYORK_TEAM, "COLLECTIVE")).toMatchInlineSnapshot(` - [ - { - "days": [ - 3, - 4, - 5, - ], - "endTime": 360, - "startTime": 780, - }, - { - "days": [ - 6, - ], - "endTime": 180, - "startTime": 0, - "userId": 2, - }, - { - "days": [ - 2, - 3, - 4, - ], - "endTime": 1260, - "startTime": 780, - }, - { - "days": [ - 5, - ], - "endTime": 1260, - "startTime": 780, - }, - ] - `); - - expect(getAggregateWorkingHours(HAWAII_AND_NEWYORK_TEAM, "ROUND_ROBIN")).toMatchInlineSnapshot(` - [ - { - "days": [ - 1, - 2, - 3, - 4, - 5, - ], - "endTime": 1260, - "startTime": 780, - "userId": 1, - }, - { - "days": [ - 3, - 4, - 5, - ], - "endTime": 360, - "startTime": 0, - "userId": 1, - }, - { - "days": [ - 6, - ], - "endTime": 180, - "startTime": 0, - "userId": 2, - }, - { - "days": [ - 2, - 3, - 4, - ], - "endTime": 1439, - "startTime": 780, - "userId": 3, - }, - { - "days": [ - 5, - ], - "endTime": 1439, - "startTime": 780, - "userId": 4, - }, - ] - `); -}); diff --git a/packages/core/getAggregateWorkingHours.test.ts b/packages/core/getAggregateWorkingHours.test.ts deleted file mode 100644 index 0780c1e051..0000000000 --- a/packages/core/getAggregateWorkingHours.test.ts +++ /dev/null @@ -1,340 +0,0 @@ -import { describe, it, expect } from "vitest"; - -import type { WorkingHours } from "@calcom/types/schedule"; - -import { getAggregateWorkingHours } from "./getAggregateWorkingHours"; - -describe("getAggregateWorkingHours", () => { - it("should return all schedules if no scheduling type", () => { - const workingHours: WorkingHours[] = [ - { - days: [1, 2, 3], - startTime: 0, - endTime: 720, - }, - ]; - const result = getAggregateWorkingHours( - [ - { - busy: [], - timeZone: "Europe/London", - workingHours, - dateOverrides: [], - datesOutOfOffice: {}, - }, - { - busy: [], - timeZone: "Europe/London", - workingHours, - dateOverrides: [], - datesOutOfOffice: {}, - }, - ], - null - ); - - expect(result).toEqual([...workingHours, ...workingHours]); - }); - - it("should return all schedules if no fixed users exist", () => { - const workingHours: WorkingHours[] = [ - { - days: [1, 2, 3], - startTime: 0, - endTime: 720, - }, - ]; - const result = getAggregateWorkingHours( - [ - { - busy: [], - timeZone: "Europe/London", - workingHours, - dateOverrides: [], - datesOutOfOffice: {}, - }, - { - busy: [], - timeZone: "Europe/London", - workingHours, - dateOverrides: [], - datesOutOfOffice: {}, - }, - ], - "MANAGED" - ); - - expect(result).toEqual([...workingHours, ...workingHours]); - }); - - it("should consider all schedules fixed if collective", () => { - const workingHoursA: WorkingHours[] = [ - { - days: [1, 2], - startTime: 0, - endTime: 200, - }, - ]; - const workingHoursB: WorkingHours[] = [ - { - days: [2, 3], - startTime: 100, - endTime: 300, - }, - ]; - const result = getAggregateWorkingHours( - [ - { - busy: [], - timeZone: "Europe/London", - workingHours: workingHoursA, - dateOverrides: [], - datesOutOfOffice: {}, - user: { - isFixed: false, - }, - }, - { - busy: [], - timeZone: "Europe/London", - workingHours: workingHoursB, - dateOverrides: [], - datesOutOfOffice: {}, - user: { - isFixed: false, - }, - }, - ], - "COLLECTIVE" - ); - - expect(result).toEqual([ - { - days: [2], - startTime: 100, - endTime: 200, - }, - ]); - }); - - it("should include loose host hours", () => { - const workingHoursA: WorkingHours[] = [ - { - days: [1, 2], - startTime: 0, - endTime: 200, - }, - ]; - const workingHoursB: WorkingHours[] = [ - { - days: [2, 3], - startTime: 100, - endTime: 300, - }, - ]; - const result = getAggregateWorkingHours( - [ - { - busy: [], - timeZone: "Europe/London", - workingHours: workingHoursA, - dateOverrides: [], - datesOutOfOffice: {}, - user: { - isFixed: false, - }, - }, - { - busy: [], - timeZone: "Europe/London", - workingHours: workingHoursB, - dateOverrides: [], - datesOutOfOffice: {}, - user: { - isFixed: true, - }, - }, - ], - "COLLECTIVE" - ); - - expect(result).toEqual([ - { - days: [2], - startTime: 100, - endTime: 200, - userId: undefined, - }, - ]); - }); - - it("should return last user's hours if no intersection", () => { - const workingHoursA: WorkingHours[] = [ - { - days: [1], - startTime: 0, - endTime: 200, - }, - ]; - const workingHoursB: WorkingHours[] = [ - { - days: [2], - startTime: 100, - endTime: 300, - }, - ]; - const result = getAggregateWorkingHours( - [ - { - busy: [], - timeZone: "Europe/London", - workingHours: workingHoursA, - dateOverrides: [], - datesOutOfOffice: {}, - user: { - isFixed: true, - }, - }, - { - busy: [], - timeZone: "Europe/London", - workingHours: workingHoursB, - dateOverrides: [], - datesOutOfOffice: {}, - user: { - isFixed: true, - }, - }, - ], - "COLLECTIVE" - ); - - expect(result).toEqual([...workingHoursB]); - }); - - it("should include user IDs when not collective", () => { - const workingHoursA: WorkingHours[] = [ - { - days: [1, 2], - startTime: 0, - endTime: 200, - userId: 1, - }, - ]; - const workingHoursB: WorkingHours[] = [ - { - days: [2, 3], - startTime: 100, - endTime: 300, - userId: 2, - }, - ]; - const result = getAggregateWorkingHours( - [ - { - busy: [], - timeZone: "Europe/London", - workingHours: workingHoursA, - dateOverrides: [], - datesOutOfOffice: {}, - user: { - isFixed: true, - }, - }, - { - busy: [], - timeZone: "Europe/London", - workingHours: workingHoursB, - dateOverrides: [], - datesOutOfOffice: {}, - user: { - isFixed: true, - }, - }, - ], - "MANAGED" - ); - - expect(result).toEqual([ - { - userId: 1, - days: [2], - startTime: 100, - endTime: 200, - }, - ]); - }); - - it("should handle multiple intersections", () => { - const workingHoursA: WorkingHours[] = [ - { - days: [1, 2], - startTime: 0, - endTime: 200, - }, - { - days: [3, 4], - startTime: 100, - endTime: 300, - }, - ]; - const workingHoursB: WorkingHours[] = [ - { - days: [2, 3], - startTime: 100, - endTime: 300, - }, - { - days: [4, 5], - startTime: 0, - endTime: 200, - }, - ]; - const result = getAggregateWorkingHours( - [ - { - busy: [], - timeZone: "Europe/London", - workingHours: workingHoursA, - dateOverrides: [], - datesOutOfOffice: {}, - user: { - isFixed: true, - }, - }, - { - busy: [], - timeZone: "Europe/London", - workingHours: workingHoursB, - dateOverrides: [], - datesOutOfOffice: {}, - user: { - isFixed: true, - }, - }, - ], - "COLLECTIVE" - ); - - expect(result).toEqual([ - { - days: [2], - startTime: 100, - endTime: 200, - userId: undefined, - }, - { - days: [3], - startTime: 100, - endTime: 300, - userId: undefined, - }, - { - days: [4], - startTime: 100, - endTime: 200, - userId: undefined, - }, - ]); - }); -}); diff --git a/packages/core/getAggregateWorkingHours.ts b/packages/core/getAggregateWorkingHours.ts deleted file mode 100644 index c72295a2f9..0000000000 --- a/packages/core/getAggregateWorkingHours.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { SchedulingType } from "@calcom/prisma/enums"; -import type { WorkingHours } from "@calcom/types/schedule"; - -/** - * This function gets team members working hours and busy slots, - * offsets them to UTC and intersects them for collective events. - **/ -export const getAggregateWorkingHours = ( - usersWorkingHoursAndBusySlots: (Omit< - Awaited["getUserAvailability"]>>, - "currentSeats" | "dateRanges" | "oooExcludedDateRanges" - > & { user?: { isFixed?: boolean } })[], - // eslint-disable-next-line @typescript-eslint/no-unused-vars - schedulingType: SchedulingType | null -): WorkingHours[] => { - // during personal events, just flatMap. - if (!schedulingType) { - return usersWorkingHoursAndBusySlots.flatMap((s) => s.workingHours); - } - const looseHostWorkingHours = usersWorkingHoursAndBusySlots - .filter(({ user }) => schedulingType !== SchedulingType.COLLECTIVE && user?.isFixed !== true) - .flatMap((s) => s.workingHours); - - const fixedHostSchedules = usersWorkingHoursAndBusySlots.filter( - ({ user }) => schedulingType === SchedulingType.COLLECTIVE || user?.isFixed - ); - // return early when there are no fixed hosts. - if (!fixedHostSchedules.length) { - return looseHostWorkingHours; - } - return fixedHostSchedules.reduce((currentWorkingHours: WorkingHours[], s) => { - const updatedWorkingHours: typeof currentWorkingHours = []; - - s.workingHours.forEach((workingHour) => { - const sameDayWorkingHours = currentWorkingHours.filter((compare) => - compare.days.find((day) => workingHour.days.includes(day)) - ); - if (!sameDayWorkingHours.length) { - updatedWorkingHours.push(workingHour); // the first day is always added. - return; - } - // days are overlapping when different users are involved, instead of adding we now need to subtract - updatedWorkingHours.push( - ...sameDayWorkingHours.map((compare) => { - const intersect = workingHour.days.filter((day) => compare.days.includes(day)); - const retVal: WorkingHours & { userId?: number | null } = { - days: intersect, - startTime: Math.max(workingHour.startTime, compare.startTime), - endTime: Math.min(workingHour.endTime, compare.endTime), - }; - if (schedulingType !== SchedulingType.COLLECTIVE) { - retVal.userId = compare.userId; - } - return retVal; - }) - ); - }); - return updatedWorkingHours; - }, looseHostWorkingHours); -}; diff --git a/packages/core/getAggregatedAvailability/getAggregatedAvailability.test.ts b/packages/core/getAggregatedAvailability.test.ts similarity index 99% rename from packages/core/getAggregatedAvailability/getAggregatedAvailability.test.ts rename to packages/core/getAggregatedAvailability.test.ts index 19c92663a9..8062c3654e 100644 --- a/packages/core/getAggregatedAvailability/getAggregatedAvailability.test.ts +++ b/packages/core/getAggregatedAvailability.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect } from "vitest"; import type { Dayjs } from "@calcom/dayjs"; import dayjs from "@calcom/dayjs"; -import { getAggregatedAvailabilityNew as getAggregatedAvailability } from "."; +import { getAggregatedAvailability } from "./getAggregatedAvailability"; // Helper to check if a time range overlaps with availability const isAvailable = (availability: { start: Dayjs; end: Dayjs }[], range: { start: Dayjs; end: Dayjs }) => { diff --git a/packages/core/getAggregatedAvailability/getAggregatedAvailability.ts b/packages/core/getAggregatedAvailability.ts similarity index 94% rename from packages/core/getAggregatedAvailability/getAggregatedAvailability.ts rename to packages/core/getAggregatedAvailability.ts index 145a090320..ecb47bd148 100644 --- a/packages/core/getAggregatedAvailability/getAggregatedAvailability.ts +++ b/packages/core/getAggregatedAvailability.ts @@ -2,7 +2,7 @@ import type { DateRange } from "@calcom/lib/date-ranges"; import { intersect } from "@calcom/lib/date-ranges"; import { SchedulingType } from "@calcom/prisma/enums"; -import { mergeOverlappingDateRanges } from "./date-range-utils/mergeOverlappingDateRanges"; +import { mergeOverlappingDateRanges } from "./getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges"; function uniqueAndSortedDateRanges(ranges: DateRange[]): DateRange[] { const seen = new Set(); diff --git a/packages/core/getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges/mergeOverlappingDateRanges.test.ts b/packages/core/getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges.test.ts similarity index 97% rename from packages/core/getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges/mergeOverlappingDateRanges.test.ts rename to packages/core/getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges.test.ts index d38537f1c8..2aa8cc6809 100644 --- a/packages/core/getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges/mergeOverlappingDateRanges.test.ts +++ b/packages/core/getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect } from "vitest"; import dayjs from "@calcom/dayjs"; import type { DateRange } from "@calcom/lib/date-ranges"; -import { mergeOverlappingDateRanges } from "."; +import { mergeOverlappingDateRanges } from "./mergeOverlappingDateRanges"; const november2 = "2023-11-02"; const november3 = "2023-11-03"; diff --git a/packages/core/getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges/index.ts b/packages/core/getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges.ts similarity index 100% rename from packages/core/getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges/index.ts rename to packages/core/getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges.ts diff --git a/packages/core/getAggregatedAvailability/getAggregatedAvailabilityOld.ts b/packages/core/getAggregatedAvailability/getAggregatedAvailabilityOld.ts deleted file mode 100644 index 0f4143a243..0000000000 --- a/packages/core/getAggregatedAvailability/getAggregatedAvailabilityOld.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { DateRange } from "@calcom/lib/date-ranges"; -import { intersect } from "@calcom/lib/date-ranges"; -import { SchedulingType } from "@calcom/prisma/enums"; - -import { mergeOverlappingDateRanges } from "./date-range-utils/mergeOverlappingDateRanges"; - -export const getAggregatedAvailability = ( - userAvailability: { - dateRanges: DateRange[]; - oooExcludedDateRanges: DateRange[]; - user?: { isFixed?: boolean }; - }[], - schedulingType: SchedulingType | null -): DateRange[] => { - const isTeamEvent = - schedulingType === SchedulingType.COLLECTIVE || - schedulingType === SchedulingType.ROUND_ROBIN || - userAvailability.length > 1; - const fixedHosts = userAvailability.filter( - ({ user }) => !schedulingType || schedulingType === SchedulingType.COLLECTIVE || user?.isFixed - ); - - const dateRangesToIntersect = fixedHosts.map((s) => - !isTeamEvent ? s.dateRanges : s.oooExcludedDateRanges - ); - - const unfixedHosts = userAvailability.filter(({ user }) => user?.isFixed !== true); - if (unfixedHosts.length) { - dateRangesToIntersect.push( - unfixedHosts.flatMap((s) => (!isTeamEvent ? s.dateRanges : s.oooExcludedDateRanges)) - ); - } - - const availability = intersect(dateRangesToIntersect); - - return mergeOverlappingDateRanges(availability); -}; diff --git a/packages/core/getAggregatedAvailability/index.ts b/packages/core/getAggregatedAvailability/index.ts deleted file mode 100644 index 1e1f53e1b9..0000000000 --- a/packages/core/getAggregatedAvailability/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { getAggregatedAvailability as getAggregatedAvailabilityNew } from "./getAggregatedAvailability"; - -export { getAggregatedAvailability as getAggregatedAvailability } from "./getAggregatedAvailabilityOld"; diff --git a/packages/lib/slots.test.ts b/packages/lib/slots.test.ts index f16ec9df4e..8a4a4c5adc 100644 --- a/packages/lib/slots.test.ts +++ b/packages/lib/slots.test.ts @@ -243,17 +243,8 @@ describe("Tests the slot logic", () => { frequency: 20, minimumBookingNotice: 0, dateRanges: [{ start: dayjs("2021-06-21T00:00:00.000Z"), end: dayjs("2021-06-21T23:45:00.000Z") }], - /*workingHours: [ - { - userId: 1, - days: Array.from(Array(7).keys()), - startTime: MINUTES_DAY_START, - endTime: MINUTES_DAY_END - 14, // 23:45 - }, - ],*/ eventLength: 20, offsetStart: 0, - organizerTimeZone: "America/Toronto", }); // 71 20-minutes events in a 24h - 15m day @@ -271,7 +262,6 @@ describe("Tests the slot logic", () => { ], minimumBookingNotice: 120, frequency: 15, - organizerTimeZone: "Europe/London", }).reverse(); expect(slots[0].time.format()).toBe("2023-07-13T22:45:00+02:00"); diff --git a/packages/lib/slots.ts b/packages/lib/slots.ts index 3abe0bdd75..34a9000272 100644 --- a/packages/lib/slots.ts +++ b/packages/lib/slots.ts @@ -8,234 +8,15 @@ import type { DateRange } from "./date-ranges"; export type GetSlots = { inviteeDate: Dayjs; frequency: number; - dateRanges?: DateRange[]; + dateRanges: DateRange[]; minimumBookingNotice: number; eventLength: number; offsetStart?: number; - organizerTimeZone?: string; datesOutOfOffice?: IOutOfOfficeData; }; export type TimeFrame = { userIds?: number[]; startTime: number; endTime: number }; const minimumOfOne = (input: number) => (input < 1 ? 1 : input); -const minimumOfZero = (input: number) => (input < 0 ? 0 : input); - -function buildSlots({ - startOfInviteeDay, - computedLocalAvailability, - frequency, - eventLength, - offsetStart = 0, - startDate, - organizerTimeZone, - inviteeTimeZone, -}: { - computedLocalAvailability: TimeFrame[]; - startOfInviteeDay: Dayjs; - startDate: Dayjs; - frequency: number; - eventLength: number; - offsetStart?: number; - organizerTimeZone: string; - inviteeTimeZone: string; -}) { - // no slots today - if (startOfInviteeDay.isBefore(startDate, "day")) { - return []; - } - // keep the old safeguards in; may be needed. - frequency = minimumOfOne(frequency); - eventLength = minimumOfOne(eventLength); - offsetStart = minimumOfZero(offsetStart); - - // 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; - - // 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); - } - - for (const [boundaryStart, boundaryEnd] of ranges) { - // loop through the day, based on frequency. - for ( - let slotStart = boundaryStart + offsetStart; - slotStart < boundaryEnd; - slotStart += offsetStart + frequency - ) { - computedLocalAvailability.forEach((item) => { - // TODO: This logic does not allow for past-midnight bookings. - if (slotStart < item.startTime || slotStart > item.endTime + 1 - eventLength) { - return; - } - slotsTimeFrameAvailable[slotStart.toString()] = { - userIds: (slotsTimeFrameAvailable[slotStart]?.userIds || []).concat(item.userIds || []), - startTime: slotStart, - endTime: slotStart + eventLength, - }; - }); - } - } - - const organizerDSTDiff = - dayjs().tz(organizerTimeZone).utcOffset() - startOfInviteeDay.tz(organizerTimeZone).utcOffset(); - const inviteeDSTDiff = - dayjs().tz(inviteeTimeZone).utcOffset() - startOfInviteeDay.tz(inviteeTimeZone).utcOffset(); - const slots: { time: Dayjs; userIds?: number[] }[] = []; - const getTime = (time: number) => { - const minutes = time + organizerDSTDiff - inviteeDSTDiff; - - return startOfInviteeDay.tz(inviteeTimeZone).add(minutes, "minutes"); - }; - for (const item of Object.values(slotsTimeFrameAvailable)) { - /* - * @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 - * ... - */ - slots.push({ - userIds: item.userIds, - time: getTime(item.startTime), - }); - } - - return slots; -} - -function buildSlotsWithDateRangesOld({ - dateRanges, - frequency, - eventLength, - timeZone, - minimumBookingNotice, - organizerTimeZone, - offsetStart, - datesOutOfOffice, -}: { - dateRanges: DateRange[]; - frequency: number; - eventLength: number; - timeZone: string; - minimumBookingNotice: number; - organizerTimeZone: string; - offsetStart?: number; - datesOutOfOffice?: IOutOfOfficeData; -}) { - // 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[]; - away?: boolean; - fromUser?: IFromUser; - toUser?: IToUser; - reason?: string; - emoji?: string; - }[] = []; - - let interval = Number(process.env.NEXT_PUBLIC_AVAILABILITY_SCHEDULE_INTERVAL) || 1; - const intervalsWithDefinedStartTimes = [60, 30, 20, 15, 10, 5]; - - for (let i = 0; i < intervalsWithDefinedStartTimes.length; i++) { - if (frequency % intervalsWithDefinedStartTimes[i] === 0) { - interval = intervalsWithDefinedStartTimes[i]; - break; - } - } - - dateRanges.forEach((range) => { - const dateYYYYMMDD = range.start.format("YYYY-MM-DD"); - const startTimeWithMinNotice = dayjs.utc().add(minimumBookingNotice, "minute"); - - let slotStartTime = range.start.utc().isAfter(startTimeWithMinNotice) - ? range.start - : startTimeWithMinNotice; - - slotStartTime = - slotStartTime.minute() % interval !== 0 - ? slotStartTime.startOf("hour").add(Math.ceil(slotStartTime.minute() / interval) * interval, "minute") - : slotStartTime; - - // 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; - - slotStartTime = slotStartTime.add(offsetStart ?? 0, "minutes").tz(timeZone); - - while (!slotStartTime.add(eventLength, "minutes").subtract(1, "second").utc().isAfter(rangeEnd)) { - const dateOutOfOfficeExists = datesOutOfOffice?.[dateYYYYMMDD]; - let slotData: { - time: Dayjs; - userIds?: number[]; - away?: boolean; - fromUser?: IFromUser; - toUser?: IToUser; - reason?: string; - emoji?: string; - } = { - time: slotStartTime, - }; - - if (dateOutOfOfficeExists) { - const { toUser, fromUser, reason, emoji } = dateOutOfOfficeExists; - - slotData = { - time: slotStartTime, - away: true, - ...(fromUser && { fromUser }), - ...(toUser && { toUser }), - ...(reason && { reason }), - ...(emoji && { emoji }), - }; - } - - slots.push(slotData); - slotStartTime = slotStartTime.add(frequency + (offsetStart ?? 0), "minutes"); - } - }); - - return slots; -} function buildSlotsWithDateRanges({ dateRanges, @@ -359,12 +140,6 @@ function buildSlotsWithDateRanges({ return Array.from(slots.values()); } -function fromIndex(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); - }; -} - const getSlots = ({ inviteeDate, frequency, @@ -372,7 +147,6 @@ const getSlots = ({ dateRanges, eventLength, offsetStart = 0, - organizerTimeZone, datesOutOfOffice, }: GetSlots): { time: Dayjs; @@ -383,30 +157,15 @@ const getSlots = ({ reason?: string; emoji?: string; }[] => { - if (dateRanges && !organizerTimeZone) { - return buildSlotsWithDateRanges({ - dateRanges, - frequency, - eventLength, - timeZone: getTimeZone(inviteeDate), - minimumBookingNotice, - offsetStart, - datesOutOfOffice, - }); - } else if (dateRanges && organizerTimeZone) { - return buildSlotsWithDateRangesOld({ - dateRanges, - frequency, - eventLength, - timeZone: getTimeZone(inviteeDate), - minimumBookingNotice, - organizerTimeZone, - offsetStart, - datesOutOfOffice, - }); - } - // just to ensure we don't call this anywhere. APIv1/v2 + webapp use dateRanges. - throw new Error("Deprecated invocation of getSlots, use dateRanges instead."); + return buildSlotsWithDateRanges({ + dateRanges, + frequency, + eventLength, + timeZone: getTimeZone(inviteeDate), + minimumBookingNotice, + offsetStart, + datesOutOfOffice, + }); }; export default getSlots; diff --git a/packages/trpc/server/routers/viewer/slots/util.ts b/packages/trpc/server/routers/viewer/slots/util.ts index d229aa3865..05f0db3f72 100644 --- a/packages/trpc/server/routers/viewer/slots/util.ts +++ b/packages/trpc/server/routers/viewer/slots/util.ts @@ -3,10 +3,7 @@ import { countBy } from "lodash"; import type { Logger } from "tslog"; import { v4 as uuid } from "uuid"; -import { - getAggregatedAvailability, - getAggregatedAvailabilityNew, -} from "@calcom/core/getAggregatedAvailability"; +import { getAggregatedAvailability } from "@calcom/core/getAggregatedAvailability"; import { getBusyTimesForLimitChecks } from "@calcom/core/getBusyTimes"; import type { CurrentSeats, GetAvailabilityUser, IFromUser, IToUser } from "@calcom/core/getUserAvailability"; import { getUsersAvailability } from "@calcom/core/getUserAvailability"; @@ -484,12 +481,6 @@ async function _getAvailableSlots({ input, ctx }: GetScheduleOptions): Promise 1; - // timeZone isn't directly set on eventType now(So, it is legacy) - // schedule is always expected to be set for an eventType now so it must never fallback to allUsersAvailability[0].timeZone(fallback is again legacy behavior) - // TODO: Also, handleNewBooking only seems to be using eventType?.schedule?.timeZone which seems to confirm that we should simplify it as well. - const eventTimeZone = - eventType.timeZone || eventType?.schedule?.timeZone || allUsersAvailability?.[0]?.timeZone; - const timeSlots = monitorCallbackSync(getSlots, { inviteeDate: startTime, eventLength: input.duration || eventType.length, @@ -497,51 +488,9 @@ async function _getAvailableSlots({ input, ctx }: GetScheduleOptions): Promise, day) => { - const t1 = ts[day] || [], - t2 = tsNew[day] || []; - const missingInNew = t1.filter((t) => !t2.includes(t)); - const missingInOld = t2.filter((t) => !t1.includes(t)); - - if (missingInNew.length || missingInOld.length) { - acc[day] = { onlyInTimeSlots: missingInNew, onlyInTimeSlotsNew: missingInOld }; - } - - return acc; - }, - {} - ); - - if (Object.keys(differences).length) { - loggerWithEventDetails.info({ - routedTeamMemberIds, - differences, - }); - } - let availableTimeSlots: typeof timeSlots = []; const bookerClientUid = ctx?.req?.cookies?.uid; @@ -678,6 +627,12 @@ async function _getAvailableSlots({ input, ctx }: GetScheduleOptions): Promise