-
Notifications
You must be signed in to change notification settings - Fork 11.6k
Fixes collective availability for teams with overlapping day timezones #3898
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
9379c0c
WIP
zomars 34ec593
Fix for team availability with time offsets
alannnc 368aca3
Prevent empty schedule from opening up everything
emrysal 798130a
When no utcOffset or timeZone's are given, default to 0 utcOffset (UTC)
emrysal 061999f
timeZone should not be part of getUserAvailability
emrysal 303dfd3
Prevents {days:[X],startTime:0,endTime:0} error entry
emrysal 2627415
Merge branch 'main' into fixes/3872
zomars ee369fa
Added getAggregateWorkingHours() (#3913)
emrysal 6010344
Added test for getAggregateWorkingHours
zomars ecaecbb
Timezone isn't used here anymore
zomars fe05e7d
Merge branch 'main' into fixes/3872
zomars f9d5816
Merge branch 'main' into fixes/3872
zomars b64a7ba
fix: developer docs url (#3914)
Udit-takkar 99630ee
Merge branch 'main' into fixes/3872
zomars 256aa83
Test fixes
zomars 213ae42
Reinstate prisma (generate only) and few comments
emrysal f4c327d
Test fixes
zomars b81b3f4
Skipping getSchedule again
zomars 296fb06
Added await to expect() as it involves async logic causing the promis…
emrysal 20becc8
Test cleanup
zomars 782b036
Update jest.config.ts
zomars File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import { expect, it } from "@jest/globals"; | ||
| import MockDate from "mockdate"; | ||
|
|
||
| import { getAggregateWorkingHours } from "@calcom/core/getAggregateWorkingHours"; | ||
|
|
||
| MockDate.set("2021-06-20T11:59:59Z"); | ||
|
|
||
| const HAWAII_AND_NEWYORK_TEAM = [ | ||
| { | ||
| timeZone: "America/Detroit", // GMT -4 per 22th of Aug, 2022 | ||
| workingHours: [{ days: [1, 2, 3, 4, 5], startTime: 780, endTime: 1260 }], | ||
| busy: [], | ||
| }, | ||
| { | ||
| timeZone: "Pacific/Honolulu", // GMT -10 per 22th of Aug, 2022 | ||
| workingHours: [ | ||
| { days: [3, 4, 5], startTime: 0, endTime: 360 }, | ||
| { days: [6], startTime: 0, endTime: 180 }, | ||
| { days: [2, 3, 4], startTime: 780, endTime: 1439 }, | ||
| { days: [5], startTime: 780, endTime: 1439 }, | ||
| ], | ||
| busy: [], | ||
| }, | ||
| ]; | ||
|
|
||
| /* TODO: Make this test more "professional" */ | ||
| it("Sydney and Shiraz can live in harmony 🙏", async () => { | ||
| expect(getAggregateWorkingHours(HAWAII_AND_NEWYORK_TEAM, "COLLECTIVE")).toMatchInlineSnapshot(` | ||
| Array [ | ||
| Object { | ||
| "days": Array [ | ||
| 3, | ||
| 4, | ||
| 5, | ||
| ], | ||
| "endTime": 360, | ||
| "startTime": 780, | ||
| }, | ||
| Object { | ||
| "days": Array [ | ||
| 6, | ||
| ], | ||
| "endTime": 180, | ||
| "startTime": 0, | ||
| }, | ||
| Object { | ||
| "days": Array [ | ||
| 2, | ||
| 3, | ||
| 4, | ||
| ], | ||
| "endTime": 1260, | ||
| "startTime": 780, | ||
| }, | ||
| Object { | ||
| "days": Array [ | ||
| 5, | ||
| ], | ||
| "endTime": 1260, | ||
| "startTime": 780, | ||
| }, | ||
| ] | ||
| `); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,68 +1,38 @@ | ||
| import { UserPlan } from "@prisma/client"; | ||
|
|
||
| import { getLuckyUser } from "@calcom/lib/server"; | ||
| import { buildUser } from "@calcom/lib/test/builder"; | ||
|
|
||
| import { prismaMock } from "../../../../tests/config/singleton"; | ||
|
|
||
| const baseUser = { | ||
| id: 0, | ||
| username: "test", | ||
| name: "Test User", | ||
| credentials: [], | ||
| timeZone: "GMT", | ||
| bufferTime: 0, | ||
| email: "[email protected]", | ||
| destinationCalendar: null, | ||
| locale: "en", | ||
| theme: null, | ||
| brandColor: "#292929", | ||
| darkBrandColor: "#fafafa", | ||
| availability: [], | ||
| selectedCalendars: [], | ||
| startTime: 0, | ||
| endTime: 0, | ||
| schedules: [], | ||
| defaultScheduleId: null, | ||
| plan: UserPlan.PRO, | ||
| avatar: "", | ||
| hideBranding: true, | ||
| allowDynamicBooking: true, | ||
| }; | ||
|
|
||
| it("can find lucky user with maximize availability", async () => { | ||
| const users = [ | ||
| { | ||
| ...baseUser, | ||
| id: 1, | ||
| username: "test", | ||
| name: "Test User", | ||
| email: "[email protected]", | ||
| bookings: [ | ||
| { | ||
| createdAt: new Date("2022-01-25"), | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| ...baseUser, | ||
| id: 2, | ||
| username: "test2", | ||
| name: "Test 2 User", | ||
| email: "[email protected]", | ||
| bookings: [ | ||
| { | ||
| createdAt: new Date(), | ||
| }, | ||
| ], | ||
| }, | ||
| ]; | ||
|
|
||
| const user1 = buildUser({ | ||
| id: 1, | ||
| username: "test", | ||
| name: "Test User", | ||
| email: "[email protected]", | ||
| bookings: [ | ||
| { | ||
| createdAt: new Date("2022-01-25"), | ||
| }, | ||
| ], | ||
| }); | ||
| const user2 = buildUser({ | ||
| id: 1, | ||
| username: "test", | ||
| name: "Test User", | ||
| email: "[email protected]", | ||
| bookings: [ | ||
| { | ||
| createdAt: new Date("2022-01-25"), | ||
| }, | ||
| ], | ||
| }); | ||
| const users = [user1, user2]; | ||
| // TODO: we may be able to use native prisma generics somehow? | ||
| // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
| // @ts-ignore | ||
| prismaMock.user.findMany.mockResolvedValue(users); | ||
|
|
||
| expect( | ||
| await expect( | ||
| getLuckyUser("MAXIMIZE_AVAILABILITY", { | ||
| availableUsers: users, | ||
| eventTypeId: 1, | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import { SchedulingType } from "@prisma/client"; | ||
|
|
||
| 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<ReturnType<Awaited<typeof import("./getUserAvailability")>["getUserAvailability"]>>, | ||
| "currentSeats" | ||
| >[], | ||
| schedulingType: SchedulingType | null | ||
| ): WorkingHours[] => { | ||
| if (schedulingType !== SchedulingType.COLLECTIVE) { | ||
| return usersWorkingHoursAndBusySlots.flatMap((s) => s.workingHours); | ||
| } | ||
| return usersWorkingHoursAndBusySlots.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)); | ||
| return { | ||
| days: intersect, | ||
| startTime: Math.max(workingHour.startTime, compare.startTime), | ||
| endTime: Math.min(workingHour.endTime, compare.endTime), | ||
| }; | ||
| }) | ||
| ); | ||
| }); | ||
|
|
||
| return updatedWorkingHours; | ||
| }, []); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it does too much at the moment, but I have ideas 😎