Adds eventTypeId as a parameter (#1217)
This commit is contained in:
parent
8c1b69cc0f
commit
22aa083883
6 changed files with 116 additions and 59 deletions
|
@ -19,6 +19,7 @@ import { createPaymentLink } from "@ee/lib/stripe/client";
|
|||
|
||||
import { asStringOrNull } from "@lib/asStringOrNull";
|
||||
import { timeZone } from "@lib/clock";
|
||||
import { ensureArray } from "@lib/ensureArray";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import useTheme from "@lib/hooks/useTheme";
|
||||
import { LocationType } from "@lib/location";
|
||||
|
@ -137,19 +138,12 @@ const BookingPage = (props: BookingPageProps) => {
|
|||
};
|
||||
};
|
||||
|
||||
// can be shortened using .filter(), except TypeScript doesn't know what to make of the types.
|
||||
const guests = router.query.guest
|
||||
? Array.isArray(router.query.guest)
|
||||
? router.query.guest
|
||||
: [router.query.guest]
|
||||
: [];
|
||||
|
||||
const bookingForm = useForm<BookingFormValues>({
|
||||
defaultValues: {
|
||||
name: (router.query.name as string) || "",
|
||||
email: (router.query.email as string) || "",
|
||||
notes: (router.query.notes as string) || "",
|
||||
guests,
|
||||
guests: ensureArray(router.query.guest),
|
||||
customInputs: props.eventType.customInputs.reduce(
|
||||
(customInputs, input) => ({
|
||||
...customInputs,
|
||||
|
@ -213,7 +207,7 @@ const BookingPage = (props: BookingPageProps) => {
|
|||
timeZone: timeZone(),
|
||||
language: i18n.language,
|
||||
rescheduleUid,
|
||||
user: router.query.user as string,
|
||||
user: router.query.user,
|
||||
location: getLocationValue(booking),
|
||||
metadata,
|
||||
customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({
|
||||
|
|
|
@ -14,6 +14,7 @@ const config: Config.InitialOptions = {
|
|||
"^@components(.*)$": "<rootDir>/components$1",
|
||||
"^@lib(.*)$": "<rootDir>/lib$1",
|
||||
"^@server(.*)$": "<rootDir>/server$1",
|
||||
"^@ee(.*)$": "<rootDir>/ee$1",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
9
lib/ensureArray.ts
Normal file
9
lib/ensureArray.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
export function ensureArray<T>(val: unknown): T[] {
|
||||
if (Array.isArray(val)) {
|
||||
return val;
|
||||
}
|
||||
if (typeof val === "undefined") {
|
||||
return [];
|
||||
}
|
||||
return [val] as T[];
|
||||
}
|
|
@ -16,8 +16,7 @@ export type BookingCreateBody = {
|
|||
rescheduleUid?: string;
|
||||
start: string;
|
||||
timeZone: string;
|
||||
users?: string[];
|
||||
user?: string;
|
||||
user?: string | string[];
|
||||
language: string;
|
||||
customInputs: { label: string; value: string }[];
|
||||
metadata: {
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
sendRescheduledEmails,
|
||||
sendOrganizerRequestEmail,
|
||||
} from "@lib/emails/email-manager";
|
||||
import { ensureArray } from "@lib/ensureArray";
|
||||
import { getErrorFromUnknown } from "@lib/errors";
|
||||
import { getEventName } from "@lib/event";
|
||||
import EventManager, { EventResult, PartialReference } from "@lib/events/EventManager";
|
||||
|
@ -123,6 +124,59 @@ function isOutOfBounds(
|
|||
}
|
||||
}
|
||||
|
||||
const userSelect = Prisma.validator<Prisma.UserArgs>()({
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
username: true,
|
||||
timeZone: true,
|
||||
credentials: true,
|
||||
bufferTime: true,
|
||||
},
|
||||
});
|
||||
|
||||
const getUserNameWithBookingCounts = async (eventTypeId: number, selectedUserNames: string[]) => {
|
||||
const users = await prisma.user.findMany({
|
||||
where: {
|
||||
username: { in: selectedUserNames },
|
||||
bookings: {
|
||||
some: {
|
||||
startTime: {
|
||||
gt: new Date(),
|
||||
},
|
||||
eventTypeId,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
},
|
||||
});
|
||||
|
||||
const userNamesWithBookingCounts = await Promise.all(
|
||||
users.map(async (user) => ({
|
||||
username: user.username,
|
||||
bookingCount: await prisma.booking.count({
|
||||
where: {
|
||||
user: {
|
||||
id: user.id,
|
||||
},
|
||||
startTime: {
|
||||
gt: new Date(),
|
||||
},
|
||||
eventTypeId,
|
||||
},
|
||||
}),
|
||||
}))
|
||||
);
|
||||
|
||||
return userNamesWithBookingCounts;
|
||||
};
|
||||
|
||||
type User = Prisma.UserGetPayload<typeof userSelect>;
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const reqBody = req.body as BookingCreateBody;
|
||||
const eventTypeId = reqBody.eventTypeId;
|
||||
|
@ -144,28 +198,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
return res.status(400).json(error);
|
||||
}
|
||||
|
||||
const userSelect = Prisma.validator<Prisma.UserSelect>()({
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
username: true,
|
||||
timeZone: true,
|
||||
credentials: true,
|
||||
bufferTime: true,
|
||||
});
|
||||
|
||||
const userData = Prisma.validator<Prisma.UserArgs>()({
|
||||
select: userSelect,
|
||||
});
|
||||
|
||||
const eventType = await prisma.eventType.findUnique({
|
||||
where: {
|
||||
id: eventTypeId,
|
||||
},
|
||||
select: {
|
||||
users: {
|
||||
select: userSelect,
|
||||
},
|
||||
users: userSelect,
|
||||
team: {
|
||||
select: {
|
||||
id: true,
|
||||
|
@ -198,43 +236,19 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
where: {
|
||||
id: eventType.userId,
|
||||
},
|
||||
select: userSelect,
|
||||
...userSelect,
|
||||
});
|
||||
if (!eventTypeUser) return res.status(404).json({ message: "eventTypeUser.notFound" });
|
||||
users.push(eventTypeUser);
|
||||
}
|
||||
|
||||
if (eventType.schedulingType === SchedulingType.ROUND_ROBIN) {
|
||||
const selectedUsers = reqBody.users || [];
|
||||
const selectedUsersDataWithBookingsCount = await prisma.user.findMany({
|
||||
where: {
|
||||
username: { in: selectedUsers },
|
||||
bookings: {
|
||||
every: {
|
||||
startTime: {
|
||||
gt: new Date(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
username: true,
|
||||
_count: {
|
||||
select: { bookings: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
const bookingCounts = await getUserNameWithBookingCounts(
|
||||
eventTypeId,
|
||||
ensureArray(reqBody.user) || users.map((user) => user.username)
|
||||
);
|
||||
|
||||
const bookingCounts = selectedUsersDataWithBookingsCount.map((userData) => ({
|
||||
username: userData.username,
|
||||
bookingCount: userData._count?.bookings || 0,
|
||||
}));
|
||||
|
||||
if (!bookingCounts.length) users.slice(0, 1);
|
||||
|
||||
const [firstMostAvailableUser] = bookingCounts.sort((a, b) => (a.bookingCount > b.bookingCount ? 1 : -1));
|
||||
const luckyUser = users.find((user) => user.username === firstMostAvailableUser?.username);
|
||||
users = luckyUser ? [luckyUser] : users;
|
||||
users = getLuckyUsers(users, bookingCounts);
|
||||
}
|
||||
|
||||
const invitee = [{ email: reqBody.email, name: reqBody.name, timeZone: reqBody.timeZone }];
|
||||
|
@ -354,7 +368,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
|
||||
let results: EventResult[] = [];
|
||||
let referencesToCreate: PartialReference[] = [];
|
||||
type User = Prisma.UserGetPayload<typeof userData>;
|
||||
let user: User | null = null;
|
||||
|
||||
for (const currentUser of users) {
|
||||
|
@ -555,3 +568,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
// booking successful
|
||||
return res.status(201).json(booking);
|
||||
}
|
||||
|
||||
export function getLuckyUsers(
|
||||
users: User[],
|
||||
bookingCounts: Prisma.PromiseReturnType<typeof getUserNameWithBookingCounts>
|
||||
) {
|
||||
if (!bookingCounts.length) users.slice(0, 1);
|
||||
|
||||
const [firstMostAvailableUser] = bookingCounts.sort((a, b) => (a.bookingCount > b.bookingCount ? 1 : -1));
|
||||
const luckyUser = users.find((user) => user.username === firstMostAvailableUser?.username);
|
||||
return luckyUser ? [luckyUser] : users;
|
||||
}
|
||||
|
|
30
test/lib/team-event-types.test.ts
Normal file
30
test/lib/team-event-types.test.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { getLuckyUsers } from "../../pages/api/book/event";
|
||||
|
||||
it("can find lucky users", async () => {
|
||||
const users = [
|
||||
{
|
||||
id: 1,
|
||||
username: "test",
|
||||
name: "Test User",
|
||||
credentials: [],
|
||||
timeZone: "GMT",
|
||||
bufferTime: 0,
|
||||
email: "test@example.com",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
username: "test2",
|
||||
name: "Test 2 User",
|
||||
credentials: [],
|
||||
timeZone: "GMT",
|
||||
bufferTime: 0,
|
||||
email: "test2@example.com",
|
||||
},
|
||||
];
|
||||
expect(
|
||||
getLuckyUsers(users, [
|
||||
{ username: "test", bookingCount: 2 },
|
||||
{ username: "test2", bookingCount: 0 },
|
||||
])
|
||||
).toStrictEqual([users[1]]);
|
||||
});
|
Loading…
Reference in a new issue