Skip to content
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

feat: checkbox to count managed events for team-wide limits #16923

Merged
merged 10 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/api/v1/lib/validations/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const schemaTeamBaseBodyParams = Team.omit({ id: true, createdAt: true })
smsLockState: true,
smsLockReviewedByAdmin: true,
bookingLimits: true,
includeManagedEventsInLimits: true,
});

const schemaTeamRequiredParams = z.object({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,8 @@ export class UpdateOrgTeamDto {
@IsOptional()
@IsString()
readonly bookingLimits?: string;

@IsOptional()
@IsBoolean()
readonly includeManagedEventsInLimits?: boolean;
}
3 changes: 2 additions & 1 deletion apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,7 @@
"workflow_example_4": "Send email reminder 1 hour before events starts to attendee",
"workflow_example_5": "Send custom email when event is rescheduled to host",
"workflow_example_6": "Send custom SMS when new event is booked to host",
"count_managed_to_limit": "Include booking counts from managed event types",
"welcome_to_cal_header": "Welcome to {{appName}}!",
"edit_form_later_subtitle": "You’ll be able to edit this later.",
"connect_calendar_later": "I'll connect my calendar later",
Expand Down Expand Up @@ -2622,7 +2623,7 @@
"disable_input_if_prefilled": "Disable input if the URL identifier is prefilled",
"booking_limits": "Booking Limits",
"booking_limits_team_description": "Booking limits for team members across all team event types",
"limit_team_booking_frequency_description": "Limit how many times members can be booked across all team event types (collective and round-robin)",
"limit_team_booking_frequency_description": "Limit how many times members can be booked across all team event types",
"booking_limits_updated_successfully": "Booking limits updated successfully",
"you_are_unauthorized_to_make_this_change_to_the_booking": "You are unauthorized to make this change to the booking",
"hide_calendar_event_details": "Hide calendar event details on shared calendars",
Expand Down
8 changes: 7 additions & 1 deletion apps/web/test/utils/bookingScenario/bookingScenario.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,15 @@ export type InputEventType = {
users?: { id: number }[];
hosts?: InputHost[];
schedulingType?: SchedulingType;
parent?: { id: number };
beforeEventBuffer?: number;
afterEventBuffer?: number;
teamId?: number | null;
team?: {
id?: number | null;
parentId?: number | null;
bookingLimits?: IntervalLimit;
includeManagedEventsInLimits?: boolean;
};
requiresConfirmation?: boolean;
destinationCalendar?: Prisma.DestinationCalendarCreateInput;
Expand Down Expand Up @@ -246,7 +248,7 @@ export async function addEventTypesToDb(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
schedule?: any;
metadata?: any;
team?: { id?: number | null; bookingLimits?: IntervalLimit };
team?: { id?: number | null; bookingLimits?: IntervalLimit; includeManagedEventsInLimits?: boolean };
})[]
) {
log.silly("TestData: Add EventTypes to DB", JSON.stringify(eventTypes));
Expand Down Expand Up @@ -292,6 +294,7 @@ export async function addEventTypesToDb(
data: {
id: eventType.team?.id,
bookingLimits: eventType.team?.bookingLimits,
includeManagedEventsInLimits: eventType.team?.includeManagedEventsInLimits,
name: "",
},
});
Expand Down Expand Up @@ -323,6 +326,7 @@ export async function addEventTypes(eventTypes: InputEventType[], usersStore: In
beforeEventBuffer: 0,
afterEventBuffer: 0,
bookingLimits: {},
includeManagedEventsInLimits: false,
schedulingType: null,
length: 15,
//TODO: What is the purpose of periodStartDate and periodEndDate? Test these?
Expand Down Expand Up @@ -378,6 +382,7 @@ export async function addEventTypes(eventTypes: InputEventType[], usersStore: In
: eventType.schedule,
owner: eventType.owner ? { connect: { id: eventType.owner } } : undefined,
schedulingType: eventType.schedulingType,
parent: eventType.parent ? { connect: { id: eventType.parent.id } } : undefined,
rescheduleWithSameRoundRobinHost: eventType.rescheduleWithSameRoundRobinHost,
};
});
Expand Down Expand Up @@ -1285,6 +1290,7 @@ export function getScenarioData(
id: eventType.teamId ?? eventType.team?.id,
parentId: org ? org.id : null,
bookingLimits: eventType?.team?.bookingLimits,
includeManagedEventsInLimits: eventType?.team?.includeManagedEventsInLimits,
},
title: `Test Event Type - ${index + 1}`,
description: `It's a test event type - ${index + 1}`,
Expand Down
13 changes: 13 additions & 0 deletions packages/core/bookingLimits/getBusyTimesFromLimits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const _getBusyTimesFromLimits = async (
duration: number | undefined,
eventType: NonNullable<EventType>,
bookings: EventBusyDetails[],
timeZone: string,
rescheduleUid?: string
) => {
performance.mark("limitsStart");
Expand All @@ -45,6 +46,7 @@ const _getBusyTimesFromLimits = async (
eventTypeId: eventType.id,
limitManager,
rescheduleUid,
timeZone,
});
performance.mark("bookingLimitsEnd");
performance.measure(`checking booking limits took $1'`, "bookingLimitsStart", "bookingLimitsEnd");
Expand Down Expand Up @@ -89,6 +91,8 @@ const _getBusyTimesFromBookingLimits = async (params: {
eventTypeId?: number;
teamId?: number;
user?: { id: number; email: string };
includeManagedEvents?: boolean;
timeZone?: string | null;
}) => {
const {
bookings,
Expand All @@ -100,6 +104,8 @@ const _getBusyTimesFromBookingLimits = async (params: {
teamId,
user,
rescheduleUid,
includeManagedEvents = false,
timeZone,
} = params;

for (const key of descendingLimitKeys) {
Expand All @@ -123,6 +129,8 @@ const _getBusyTimesFromBookingLimits = async (params: {
teamId,
user,
rescheduleUid,
includeManagedEvents,
timeZone,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not passing timeZone here caused this error with yearly booking limits:
Screenshot 2024-10-04 at 9 06 34 PM

});
} catch (_) {
limitManager.addBusyTime(periodStart, unit);
Expand Down Expand Up @@ -231,6 +239,8 @@ const _getBusyTimesFromTeamLimits = async (
dateFrom: Dayjs,
dateTo: Dayjs,
teamId: number,
includeManagedEvents: boolean,
timeZone: string,
rescheduleUid?: string
) => {
const { limitDateFrom, limitDateTo } = getStartEndDateforLimitCheck(
Expand All @@ -245,6 +255,7 @@ const _getBusyTimesFromTeamLimits = async (
startDate: limitDateFrom.toDate(),
endDate: limitDateTo.toDate(),
excludedUid: rescheduleUid,
includeManagedEvents,
});

const busyTimes = bookings.map(({ id, startTime, endTime, eventTypeId, title, userId }) => ({
Expand All @@ -266,6 +277,8 @@ const _getBusyTimesFromTeamLimits = async (
rescheduleUid,
teamId,
user,
includeManagedEvents,
timeZone,
});

return limitManager.getBusyTimes();
Expand Down
26 changes: 23 additions & 3 deletions packages/core/getUserAvailability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,22 @@ const _getEventType = async (id: number) => {
id: true,
seatsPerTimeSlot: true,
bookingLimits: true,
parent: {
select: {
team: {
select: {
id: true,
bookingLimits: true,
includeManagedEventsInLimits: true,
},
},
},
},
team: {
select: {
id: true,
bookingLimits: true,
includeManagedEventsInLimits: true,
},
},
hosts: {
Expand Down Expand Up @@ -286,20 +298,27 @@ const _getUserAvailability = async function getUsersWorkingHoursLifeTheUniverseA
duration,
eventType,
initialData?.busyTimesFromLimitsBookings ?? [],
timeZone,
initialData?.rescheduleUid ?? undefined
)
: [];

const teamBookingLimits = parseBookingLimit(eventType?.team?.bookingLimits);
const teamForBookingLimits =
eventType?.team ??
(eventType?.parent?.team?.includeManagedEventsInLimits ? eventType?.parent?.team : null);

const teamBookingLimits = parseBookingLimit(teamForBookingLimits?.bookingLimits);

const busyTimesFromTeamLimits =
eventType?.team && teamBookingLimits
teamForBookingLimits && teamBookingLimits
? await getBusyTimesFromTeamLimits(
user,
teamBookingLimits,
dateFrom.tz(timeZone),
dateTo.tz(timeZone),
eventType?.team.id,
teamForBookingLimits.id,
teamForBookingLimits.includeManagedEventsInLimits,
timeZone,
initialData?.rescheduleUid ?? undefined
)
: [];
Expand Down Expand Up @@ -458,6 +477,7 @@ const _getPeriodStartDatesBetween = (dateFrom: Dayjs, dateTo: Dayjs, period: Int
const dates = [];
let startDate = dayjs(dateFrom).startOf(period);
const endDate = dayjs(dateTo).endOf(period);

while (startDate.isBefore(endDate)) {
dates.push(startDate);
startDate = startDate.add(1, period);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const getEventTypesFromDB = async (eventTypeId: number) => {
name: true,
parentId: true,
bookingLimits: true,
includeManagedEventsInLimits: true,
},
},
bookingFields: true,
Expand Down Expand Up @@ -73,6 +74,13 @@ export const getEventTypesFromDB = async (eventTypeId: number) => {
parent: {
select: {
teamId: true,
team: {
select: {
id: true,
bookingLimits: true,
includeManagedEventsInLimits: true,
},
},
},
},
useEventTypeDestinationCalendarEmail: true,
Expand Down
Loading
Loading