* add Add Group button * add host groups to schema * UI for host groups * raname groups to hostGroups * schema update * show groups in assignment tab * add no group hosts to Group 1 * add dummy group for non group hosts * fix type errors * use two dimensional array for luckyUserPools * fix empty array * group RR hosts in handleNewBooking * improve logic for grouping lucky users * find all lucky users of all groups * allow several RR hosts on booking * clean up migrations * create helper function * group hosts for slots logic * add group logic to loading available slots * adding hosts to groups * add groupId to hostSchema * disable hosts from other groups * handle groups in checkedTeamSelect * fix adding hosts to groups * remove and add groups * show hosts if there are no groups * fixing adding first group with existing hosts * show groups empty groups correctly * UI upddate fixes * fix adding hosts to existing first host group * small fixes + code clean up * add availability fix with test * create new round-robin test file * disable reassignment * fix losing fixed hosts * fix updating weights and priorities * disable load balancing with Round Robin Groups * automatically disable load balancing in update handler * allRRHosts should only include hosts from same group * fix type errors * fix type error * fix tests * fix type error * remove undefined from groupId type * type changes * add tests for hostGroups * add tests for host groups * fixes * fix type errors with undefined groupId * remove seperate host groups prop * fix editing weights * remove console.log * code clean up * improve getAggregatedAvailability tests * throw error when no available hosts in a group * add fixme comment * create constant for DEFAULT_GROUP_ID * clean up code * mock default_group_id for unit tests * don't show fixed hosts in edit weights side bar * add DEFAULT_GROUP_ID to mock test-setup * remove unused index variable * code clean up * fix updating host groups * fix imports * add default_group_id to mocks * add uuid() to zod schema * remove unused code * fix singular translation key * remove unnessary !! * Revert formatting changes * add additional tests for bookingActions * use createMany * import DEFAULT_GROUP_ID for mocks * fix mocks * clean up EventTeamAssignmentTab * fix type errors in tests * fix mocks * remove constants.example.test.ts * fix type error * add missing groupId * fix margin * clean up empty host groups * fix constants mock * useCalback * use reduce * extract handlers into seperate functions * fix handler functions * fix border radius * fix type error in CheckForEmptyAssignment * fix type error --------- Co-authored-by: CarinaWolli <wollencarina@gmail.com>
75 lines
2.7 KiB
TypeScript
75 lines
2.7 KiB
TypeScript
import { DEFAULT_GROUP_ID } from "@calcom/lib/constants";
|
|
import type { DateRange } from "@calcom/lib/date-ranges";
|
|
import { intersect } from "@calcom/lib/date-ranges";
|
|
import { SchedulingType } from "@calcom/prisma/enums";
|
|
|
|
import { filterRedundantDateRanges } from "./getAggregatedAvailability/date-range-utils/filterRedundantDateRanges";
|
|
import { mergeOverlappingDateRanges } from "./getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges";
|
|
|
|
function uniqueAndSortedDateRanges(ranges: DateRange[]): DateRange[] {
|
|
const seen = new Set<string>();
|
|
|
|
return ranges
|
|
.sort((a, b) => {
|
|
const startDiff = a.start.valueOf() - b.start.valueOf();
|
|
return startDiff !== 0 ? startDiff : a.end.valueOf() - b.end.valueOf();
|
|
})
|
|
.filter((range) => {
|
|
const key = `${range.start.valueOf()}-${range.end.valueOf()}`;
|
|
if (seen.has(key)) return false;
|
|
seen.add(key);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
export const getAggregatedAvailability = (
|
|
userAvailability: {
|
|
dateRanges: DateRange[];
|
|
oooExcludedDateRanges: DateRange[];
|
|
user?: { isFixed?: boolean; groupId?: string | null };
|
|
}[],
|
|
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 fixedDateRanges = mergeOverlappingDateRanges(
|
|
intersect(fixedHosts.map((s) => (!isTeamEvent ? s.dateRanges : s.oooExcludedDateRanges)))
|
|
);
|
|
const dateRangesToIntersect = !!fixedDateRanges.length ? [fixedDateRanges] : [];
|
|
const roundRobinHosts = userAvailability.filter(({ user }) => user?.isFixed !== true);
|
|
if (roundRobinHosts.length) {
|
|
// Group round robin hosts by their groupId
|
|
const hostsByGroup = roundRobinHosts.reduce((groups, host) => {
|
|
const groupId = host.user?.groupId || DEFAULT_GROUP_ID;
|
|
if (!groups[groupId]) {
|
|
groups[groupId] = [];
|
|
}
|
|
groups[groupId].push(host);
|
|
return groups;
|
|
}, {} as Record<string, typeof roundRobinHosts>);
|
|
|
|
// at least one host from each group needs to be available
|
|
Object.values(hostsByGroup).forEach((groupHosts) => {
|
|
if (groupHosts.length > 0) {
|
|
const groupDateRanges = groupHosts.flatMap((s) =>
|
|
!isTeamEvent ? s.dateRanges : s.oooExcludedDateRanges
|
|
);
|
|
dateRangesToIntersect.push(groupDateRanges ?? []);
|
|
}
|
|
});
|
|
}
|
|
|
|
const availability = intersect(dateRangesToIntersect);
|
|
|
|
const uniqueRanges = uniqueAndSortedDateRanges(availability);
|
|
|
|
return filterRedundantDateRanges(uniqueRanges);
|
|
};
|