Skip to content

Commit 297a965

Browse files
authored
Bugfix/i3531 round robin assigns to unavailable members (#3813)
* wip commit * Finished new algorithm for fetching the least recently booked user * ROUND_ROBIN fix * Removed redundant import * Prisma dependency turned getLuckyUser into a server-only function * DRY avatars * Properly passThrough * name can be undefined. * Remove debug artefact
1 parent c644a10 commit 297a965

24 files changed

+350
-334
lines changed

apps/web/components/booking/AvailableTimes.tsx

-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { SchedulingType } from "@prisma/client";
21
import Link from "next/link";
32
import { useRouter } from "next/router";
43
import { FC, useEffect, useState } from "react";
@@ -18,10 +17,6 @@ type AvailableTimesProps = {
1817
recurringCount: number | undefined;
1918
eventTypeSlug: string;
2019
date: Dayjs;
21-
users: {
22-
username: string | null;
23-
}[];
24-
schedulingType: SchedulingType | null;
2520
seatsPerTimeSlot?: number | null;
2621
slots?: Slot[];
2722
isLoading: boolean;
@@ -35,7 +30,6 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
3530
eventTypeSlug,
3631
recurringCount,
3732
timeFormat,
38-
schedulingType,
3933
seatsPerTimeSlot,
4034
}) => {
4135
const { t, i18n } = useLocale();
@@ -82,10 +76,6 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
8276
bookingUrl.query.rescheduleUid = rescheduleUid as string;
8377
}
8478

85-
if (schedulingType === SchedulingType.ROUND_ROBIN) {
86-
bookingUrl.query.user = slot.users;
87-
}
88-
8979
// If event already has an attendee add booking id
9080
if (slot.bookingUid) {
9181
bookingUrl.query.bookingUid = slot.bookingUid;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { CAL_URL } from "@calcom/lib/constants";
2+
3+
import AvatarGroup, { AvatarGroupProps } from "@components/ui/AvatarGroup";
4+
5+
export const UserAvatars = ({
6+
profile,
7+
users,
8+
...props
9+
}: {
10+
profile: { image: string | null; name?: string | null };
11+
showMembers: boolean;
12+
users: { username: string | null; name?: string | null }[];
13+
} & Pick<AvatarGroupProps, "size" | "truncateAfter">) => {
14+
const showMembers = !users.find((user) => user.name === profile.name) && props.showMembers;
15+
return (
16+
<AvatarGroup
17+
border="border-2 dark:border-gray-800 border-white"
18+
items={
19+
[
20+
{ image: profile.image, alt: profile.name, title: profile.name },
21+
...(showMembers
22+
? users.map((user) => ({
23+
title: user.name,
24+
image: `${CAL_URL}/${user.username}/avatar.png`,
25+
alt: user.name || undefined,
26+
}))
27+
: []),
28+
].filter((item) => !!item.image) as { image: string; alt?: string; title?: string }[]
29+
}
30+
size={props.size}
31+
truncateAfter={props.truncateAfter}
32+
/>
33+
);
34+
};

apps/web/components/booking/pages/AvailabilityPage.tsx

+11-32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Get router variables
22
import { EventType } from "@prisma/client";
3+
import { SchedulingType } from "@prisma/client";
34
import * as Collapsible from "@radix-ui/react-collapsible";
45
import { TFunction } from "next-i18next";
56
import { useRouter } from "next/router";
@@ -18,7 +19,7 @@ import {
1819
import { useContracts } from "@calcom/features/ee/web3/contexts/contractsContext";
1920
import CustomBranding from "@calcom/lib/CustomBranding";
2021
import classNames from "@calcom/lib/classNames";
21-
import { CAL_URL, WEBSITE_URL } from "@calcom/lib/constants";
22+
import { WEBSITE_URL } from "@calcom/lib/constants";
2223
import { useLocale } from "@calcom/lib/hooks/useLocale";
2324
import useTheme from "@calcom/lib/hooks/useTheme";
2425
import notEmpty from "@calcom/lib/notEmpty";
@@ -37,9 +38,9 @@ import { isBrandingHidden } from "@lib/isBrandingHidden";
3738

3839
import AvailableTimes from "@components/booking/AvailableTimes";
3940
import TimeOptions from "@components/booking/TimeOptions";
41+
import { UserAvatars } from "@components/booking/UserAvatars";
4042
import EventTypeDescriptionSafeHTML from "@components/eventtype/EventTypeDescriptionSafeHTML";
4143
import { HeadSeo } from "@components/seo/head-seo";
42-
import AvatarGroup from "@components/ui/AvatarGroup";
4344
import PoweredByCal from "@components/ui/PoweredByCal";
4445

4546
import type { AvailabilityPageProps } from "../../../pages/[user]/[type]";
@@ -235,8 +236,6 @@ const SlotPicker = ({
235236
eventTypeSlug={eventType.slug}
236237
seatsPerTimeSlot={seatsPerTimeSlot}
237238
recurringCount={recurringEventCount}
238-
schedulingType={eventType.schedulingType}
239-
users={[]}
240239
/>
241240
)}
242241
</>
@@ -425,20 +424,10 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
425424
{/* mobile: details */}
426425
<div className="block px-4 pt-4 sm:p-8 md:hidden">
427426
<div>
428-
<AvatarGroup
429-
border="border-2 dark:border-gray-800 border-white"
430-
items={
431-
[
432-
{ image: profile.image, alt: profile.name, title: profile.name },
433-
...eventType.users
434-
.filter((user) => user.name !== profile.name)
435-
.map((user) => ({
436-
title: user.name,
437-
image: `${CAL_URL}/${user.username}/avatar.png`,
438-
alt: user.name || undefined,
439-
})),
440-
].filter((item) => !!item.image) as { image: string; alt?: string; title?: string }[]
441-
}
427+
<UserAvatars
428+
profile={profile}
429+
users={eventType.users}
430+
showMembers={eventType.schedulingType !== SchedulingType.ROUND_ROBIN}
442431
size={9}
443432
truncateAfter={5}
444433
/>
@@ -566,20 +555,10 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
566555
"hidden overflow-hidden border-gray-200 p-5 sm:border-r sm:dark:border-gray-700 md:flex md:flex-col " +
567556
(isAvailableTimesVisible ? "sm:w-1/3" : recurringEventCount ? "sm:w-2/3" : "sm:w-1/2")
568557
}>
569-
<AvatarGroup
570-
border="border-2 dark:border-gray-800 border-white"
571-
items={
572-
[
573-
{ image: profile.image, alt: profile.name, title: profile.name },
574-
...eventType.users
575-
.filter((user) => user.name !== profile.name)
576-
.map((user) => ({
577-
title: user.name,
578-
alt: user.name,
579-
image: `${CAL_URL}/${user.username}/avatar.png`,
580-
})),
581-
].filter((item) => !!item.image) as { image: string; alt?: string; title?: string }[]
582-
}
558+
<UserAvatars
559+
profile={profile}
560+
users={eventType.users}
561+
showMembers={eventType.schedulingType !== SchedulingType.ROUND_ROBIN}
583562
size={10}
584563
truncateAfter={3}
585564
/>

apps/web/components/booking/pages/BookingPage.tsx

+6-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { zodResolver } from "@hookform/resolvers/zod";
22
import { EventTypeCustomInputType, WorkflowActions } from "@prisma/client";
3+
import { SchedulingType } from "@prisma/client";
34
import { isValidPhoneNumber } from "libphonenumber-js";
45
import { useSession } from "next-auth/react";
56
import Head from "next/head";
@@ -42,7 +43,7 @@ import createRecurringBooking from "@lib/mutations/bookings/create-recurring-boo
4243
import { parseDate, parseRecurringDates } from "@lib/parseDate";
4344
import slugify from "@lib/slugify";
4445

45-
import AvatarGroup from "@components/ui/AvatarGroup";
46+
import { UserAvatars } from "@components/booking/UserAvatars";
4647

4748
import { BookPageProps } from "../../../pages/[user]/book";
4849
import { HashLinkPageProps } from "../../../pages/d/[link]/book";
@@ -499,20 +500,11 @@ const BookingPage = ({
499500
)}>
500501
<div className="sm:flex">
501502
<div className="px-6 pt-6 pb-0 sm:w-1/2 sm:border-r sm:pb-6 sm:dark:border-gray-700">
502-
<AvatarGroup
503-
border="border-2 border-white dark:border-gray-800"
503+
<UserAvatars
504+
profile={profile}
505+
users={eventType.users}
506+
showMembers={eventType.schedulingType !== SchedulingType.ROUND_ROBIN}
504507
size={14}
505-
items={[
506-
{ image: profile.image || "", alt: profile.name || "", title: profile.name || "" },
507-
].concat(
508-
eventType.users
509-
.filter((user) => user.name !== profile.name)
510-
.map((user) => ({
511-
title: user.name || "",
512-
image: user.avatar || "",
513-
alt: user.name || "",
514-
}))
515-
)}
516508
/>
517509
<h2 className="font-cal text-bookinglight mt-2 font-medium dark:text-gray-300">
518510
{profile.name}

apps/web/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@
151151
"env-cmd": "10.1.0",
152152
"eslint": "^8.16.0",
153153
"jest": "^26.0.0",
154+
"jest-mock-extended": "^2.0.7",
154155
"mockdate": "^3.0.5",
155156
"module-alias": "^2.2.2",
156157
"postcss": "^8.4.13",

apps/web/pages/[user]/book.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
142142
...e,
143143
periodStartDate: e.periodStartDate?.toString() ?? null,
144144
periodEndDate: e.periodEndDate?.toString() ?? null,
145+
schedulingType: null,
145146
users: users.map((u) => ({
146147
id: u.id,
147148
name: u.name,

0 commit comments

Comments
 (0)