* feat: Implements maxLeadThreshold which puts a maximum to the amount of lead a given host is allowed to be ahead * Refactor getLuckyUser + implement in handleNewBooking + move logic to repo * Decoupled @prisma from findQualifiedHosts * Fix deep type error * Add maxLeadThreshold to builder.ts * Removed a console.log :O + fix handleChildrenEventTypes input * Add distribution UI to enable balanced distribution/maximize availability * 0-1-2 instead of 0-1-2-3 (3 bookings instead of 4)
121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
import type { Prisma } from "@prisma/client";
|
|
import type { IncomingMessage } from "http";
|
|
import type { Logger } from "tslog";
|
|
|
|
import { filterHostsByLeadThreshold } from "@calcom/lib/bookings/filterHostsByLeadThreshold";
|
|
import { HttpError } from "@calcom/lib/http-error";
|
|
import { getPiiFreeUser } from "@calcom/lib/piiFreeData";
|
|
import { safeStringify } from "@calcom/lib/safeStringify";
|
|
import { userSelect } from "@calcom/prisma";
|
|
import prisma from "@calcom/prisma";
|
|
import { SchedulingType } from "@calcom/prisma/enums";
|
|
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
|
|
|
import { loadUsers } from "./loadUsers";
|
|
import type { NewBookingEventType } from "./types";
|
|
|
|
type Users = (Awaited<ReturnType<typeof loadUsers>>[number] & {
|
|
isFixed?: boolean;
|
|
metadata?: Prisma.JsonValue;
|
|
createdAt?: Date;
|
|
})[];
|
|
|
|
type EventType = Pick<
|
|
NewBookingEventType,
|
|
"hosts" | "users" | "id" | "userId" | "schedulingType" | "maxLeadThreshold"
|
|
>;
|
|
|
|
type InputProps = {
|
|
req: IncomingMessage;
|
|
eventType: EventType;
|
|
eventTypeId: number;
|
|
dynamicUserList: string[];
|
|
logger: Logger<unknown>;
|
|
routedTeamMemberIds: number[] | null;
|
|
contactOwnerEmail: string | null;
|
|
};
|
|
|
|
export async function loadAndValidateUsers({
|
|
req,
|
|
eventType,
|
|
eventTypeId,
|
|
dynamicUserList,
|
|
logger,
|
|
routedTeamMemberIds,
|
|
contactOwnerEmail,
|
|
}: InputProps): Promise<Users> {
|
|
let users: Users = await loadUsers({
|
|
eventType,
|
|
dynamicUserList,
|
|
req,
|
|
routedTeamMemberIds,
|
|
contactOwnerEmail,
|
|
});
|
|
const isDynamicAllowed = !users.some((user) => !user.allowDynamicBooking);
|
|
if (!isDynamicAllowed && !eventTypeId) {
|
|
logger.warn({
|
|
message: "NewBooking: Some of the users in this group do not allow dynamic booking",
|
|
});
|
|
throw new HttpError({
|
|
message: "Some of the users in this group do not allow dynamic booking",
|
|
statusCode: 400,
|
|
});
|
|
}
|
|
|
|
// If this event was pre-relationship migration
|
|
// TODO: Establish whether this is dead code.
|
|
if (!users.length && eventType.userId) {
|
|
const eventTypeUser = await prisma.user.findUnique({
|
|
where: {
|
|
id: eventType.userId,
|
|
},
|
|
select: {
|
|
credentials: {
|
|
select: credentialForCalendarServiceSelect,
|
|
}, // Don't leak to client
|
|
...userSelect.select,
|
|
},
|
|
});
|
|
if (!eventTypeUser) {
|
|
logger.warn({ message: "NewBooking: eventTypeUser.notFound" });
|
|
throw new HttpError({ statusCode: 404, message: "eventTypeUser.notFound" });
|
|
}
|
|
users.push(eventTypeUser);
|
|
}
|
|
|
|
if (!users) throw new HttpError({ statusCode: 404, message: "eventTypeUser.notFound" });
|
|
// map fixed users
|
|
users = users.map((user) => ({
|
|
...user,
|
|
isFixed:
|
|
user.isFixed === false
|
|
? false
|
|
: user.isFixed || eventType.schedulingType !== SchedulingType.ROUND_ROBIN,
|
|
}));
|
|
|
|
const qualifiedHosts = await filterHostsByLeadThreshold({
|
|
eventTypeId: eventType.id,
|
|
hosts: eventType.hosts.map((host) => ({
|
|
isFixed: host.isFixed,
|
|
createdAt: host.createdAt,
|
|
email: host.user.email,
|
|
user: host.user,
|
|
})),
|
|
maxLeadThreshold: eventType.maxLeadThreshold,
|
|
});
|
|
if (qualifiedHosts.length) {
|
|
// remove users that are not in the qualified hosts array
|
|
const qualifiedHostIds = new Set(qualifiedHosts.map((qualifiedHost) => qualifiedHost.user.id));
|
|
users = users.filter((user) => qualifiedHostIds.has(user.id));
|
|
}
|
|
|
|
logger.debug(
|
|
"Concerned users",
|
|
safeStringify({
|
|
users: users.map(getPiiFreeUser),
|
|
})
|
|
);
|
|
|
|
return users;
|
|
}
|