diff --git a/packages/core/getUserAvailability.ts b/packages/core/getUserAvailability.ts index 1555766da62966..8d09d423791e1f 100644 --- a/packages/core/getUserAvailability.ts +++ b/packages/core/getUserAvailability.ts @@ -22,8 +22,7 @@ import { safeStringify } from "@calcom/lib/safeStringify"; import { EventTypeRepository } from "@calcom/lib/server/repository/eventType"; import { UserRepository } from "@calcom/lib/server/repository/user"; import prisma from "@calcom/prisma"; -import { SchedulingType } from "@calcom/prisma/enums"; -import { BookingStatus } from "@calcom/prisma/enums"; +import { BookingStatus, SchedulingType } from "@calcom/prisma/enums"; import { EventTypeMetaDataSchema, stringToDayjsZod } from "@calcom/prisma/zod-utils"; import type { EventBusyDetails, IntervalLimitUnit } from "@calcom/types/Calendar"; import type { TimeRange } from "@calcom/types/schedule"; @@ -547,7 +546,7 @@ const _getUserAvailability = async function getUsersWorkingHoursLifeTheUniverseA }, })); - const datesOutOfOffice: IOutOfOfficeData = calculateOutOfOfficeRanges(outOfOfficeDays, availability); + const datesOutOfOffice: IOutOfOfficeData = await calculateOutOfOfficeRanges(outOfOfficeDays, availability); const { dateRanges, oooExcludedDateRanges } = buildDateRanges({ dateFrom, @@ -636,15 +635,17 @@ export interface IOutOfOfficeData { }; } -const calculateOutOfOfficeRanges = ( +const calculateOutOfOfficeRanges = async ( outOfOfficeDays: GetUserAvailabilityInitialData["outOfOfficeDays"], availability: GetUserAvailabilityParamsDTO["availability"] -): IOutOfOfficeData => { +): Promise => { if (!outOfOfficeDays || outOfOfficeDays.length === 0) { return {}; } - return outOfOfficeDays.reduce((acc: IOutOfOfficeData, { start, end, toUser, user, reason }) => { + const acc: IOutOfOfficeData = {}; + + for (const { start, end, toUser, user, reason } of outOfOfficeDays) { // here we should use startDate or today if start is before today // consider timezone in start and end date range const startDateRange = dayjs(start).utc().isBefore(dayjs().startOf("day").utc()) @@ -665,19 +666,27 @@ const calculateOutOfOfficeRanges = ( continue; // Skip to the next iteration if day not found in flattenDays } + const enrichedToUser = toUser ? await UserRepository.enrichUserWithItsProfile({ user: toUser }) : null; + acc[date.format("YYYY-MM-DD")] = { // @TODO: would be good having start and end availability time here, but for now should be good // you can obtain that from user availability defined outside of here fromUser: { id: user.id, displayName: user.name }, // optional chaining destructuring toUser - toUser: !!toUser ? { id: toUser.id, displayName: toUser.name, username: toUser.username } : null, + ...(!!enrichedToUser && { + toUser: { + id: enrichedToUser.id, + username: enrichedToUser.username, + displayName: enrichedToUser.name, + }, + }), reason: !!reason ? reason.reason : null, emoji: !!reason ? reason.emoji : null, }; } + } - return acc; - }, {}); + return acc; }; type GetUsersAvailabilityProps = { diff --git a/packages/lib/slots.ts b/packages/lib/slots.ts index 645c71fa0abdb2..94a087c9278de1 100644 --- a/packages/lib/slots.ts +++ b/packages/lib/slots.ts @@ -184,6 +184,7 @@ function buildSlotsWithDateRanges({ } } + const processedOOODates = new Set(); dateRanges.forEach((range) => { const dateYYYYMMDD = range.start.format("YYYY-MM-DD"); const startTimeWithMinNotice = dayjs.utc().add(minimumBookingNotice, "minute"); @@ -206,6 +207,25 @@ function buildSlotsWithDateRanges({ slotStartTime = slotStartTime.add(offsetStart ?? 0, "minutes").tz(timeZone); + // Add OOO slot if exists and is before current range + if (datesOutOfOffice) { + Object.entries(datesOutOfOffice).forEach(([dateStr, oooData]) => { + const oooDate = dayjs(dateStr); + if (oooDate.isBefore(dateYYYYMMDD, "day") && !processedOOODates.has(dateStr)) { + slots.push({ + time: oooDate.startOf("day"), + away: true, + ...(oooData.fromUser && { fromUser: oooData.fromUser }), + ...(oooData.toUser && { toUser: oooData.toUser }), + ...(oooData.reason && { reason: oooData.reason }), + ...(oooData.emoji && { emoji: oooData.emoji }), + }); + // when the date range increases in the next iteration, we don't want to process this date again + processedOOODates.add(dateStr); + } + }); + } + while (!slotStartTime.add(eventLength, "minutes").subtract(1, "second").utc().isAfter(rangeEnd)) { const dateOutOfOfficeExists = datesOutOfOffice?.[dateYYYYMMDD]; let slotData: { @@ -231,8 +251,8 @@ function buildSlotsWithDateRanges({ ...(reason && { reason }), ...(emoji && { emoji }), }; + processedOOODates.add(dateYYYYMMDD); } - slots.push(slotData); slotStartTime = slotStartTime.add(frequency + (offsetStart ?? 0), "minutes"); }