Files
calendar/packages/lib/schedules/transformers/for-atom.ts
T
cd6293d220 fix: sort the availabilities based on start time for each day (#21656)
* sort the availabilities based on start time for each day

* move the sorting outside the reduce

* add guard variable for safe access

---------

Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
2025-07-07 23:11:47 +05:30

115 lines
3.6 KiB
TypeScript

import type { Availability } from "@prisma/client";
import dayjs from "@calcom/dayjs";
import { getWorkingHours } from "@calcom/lib/availability";
import { yyyymmdd } from "@calcom/lib/dayjs";
import type { Schedule, TimeRange } from "@calcom/types/schedule";
type ScheduleAvailability = Pick<Availability, "days" | "startTime" | "endTime">[];
type ScheduleOverride = Pick<Availability, "date" | "startTime" | "endTime">[];
export function transformWorkingHoursForAtom(schedule: {
timeZone: string | null;
availability: ScheduleAvailability;
}) {
return getWorkingHours(
{ timeZone: schedule.timeZone || undefined, utcOffset: 0 },
schedule.availability || []
);
}
export function transformAvailabilityForAtom(schedule: { availability: ScheduleAvailability }) {
return transformScheduleToAvailabilityForAtom(schedule).map((a) =>
a.map((startAndEnd) => ({
...startAndEnd,
end: new Date(startAndEnd.end.toISOString().replace("23:59:00.000Z", "23:59:59.999Z")),
}))
);
}
export function transformDateOverridesForAtom(
schedule: { availability: ScheduleOverride },
timeZone: string
) {
const acc = schedule.availability.reduce((acc, override) => {
// only if future date override
const currentUtcOffset = dayjs().tz(timeZone).utcOffset();
const currentTimeInTz = dayjs().utc().add(currentUtcOffset, "minute");
if (!override.date || dayjs(override.date).isBefore(currentTimeInTz, "day")) {
return acc;
}
const newValue = {
start: dayjs
.utc(override.date)
.hour(override.startTime.getUTCHours())
.minute(override.startTime.getUTCMinutes())
.toDate(),
end: dayjs
.utc(override.date)
.hour(override.endTime.getUTCHours())
.minute(override.endTime.getUTCMinutes())
.toDate(),
};
const dayRangeIndex = acc.findIndex(
// early return prevents override.date from ever being empty.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
(item) => yyyymmdd(item.ranges[0].start) === yyyymmdd(override.date!)
);
if (dayRangeIndex === -1) {
acc.push({ ranges: [newValue] });
return acc;
}
acc[dayRangeIndex].ranges.push(newValue);
return acc;
}, [] as { ranges: TimeRange[] }[]);
acc.sort((a, b) => {
const aTime = a.ranges?.[0]?.start?.getTime?.() ?? 0;
const bTime = b.ranges?.[0]?.start?.getTime?.() ?? 0;
return aTime - bTime;
});
return acc;
}
export const transformScheduleToAvailabilityForAtom = (schedule: { availability: ScheduleAvailability }) => {
const result = schedule.availability.reduce(
(schedule: Schedule, availability) => {
availability.days.forEach((day) => {
schedule[day].push({
start: new Date(
Date.UTC(
new Date().getUTCFullYear(),
new Date().getUTCMonth(),
new Date().getUTCDate(),
availability.startTime.getUTCHours(),
availability.startTime.getUTCMinutes()
)
),
end: new Date(
Date.UTC(
new Date().getUTCFullYear(),
new Date().getUTCMonth(),
new Date().getUTCDate(),
availability.endTime.getUTCHours(),
availability.endTime.getUTCMinutes()
)
),
});
});
return schedule;
},
Array.from([...Array(7)]).map(() => [])
);
result.forEach((daySlots) => {
daySlots.sort((a, b) => {
const aTime = a?.start?.getTime?.() ?? 0;
const bTime = b?.start?.getTime?.() ?? 0;
return aTime - bTime;
});
});
return result;
};