Skip to content

Commit 6f7c8bd

Browse files
feat: add wrong assignment report dialog and webhook for routing form bookings (calcom#25839)
Co-authored-by: peer@cal.com <peer@cal.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent 89ae748 commit 6f7c8bd

16 files changed

Lines changed: 1027 additions & 7 deletions

File tree

apps/web/components/booking/BookingListItem.tsx

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { Tooltip } from "@calcom/ui/components/tooltip";
4040

4141
import assignmentReasonBadgeTitleMap from "@lib/booking/assignmentReasonBadgeTitleMap";
4242

43+
import { WrongAssignmentDialog } from "../dialog/WrongAssignmentDialog";
4344
import { buildBookingLink } from "../../modules/bookings/lib/buildBookingLink";
4445
import { useBookingDetailsSheetStore } from "../../modules/bookings/store/bookingDetailsSheetStore";
4546
import type { BookingAttendee } from "../../modules/bookings/types";
@@ -273,6 +274,12 @@ function BookingListItem(booking: BookingItemProps) {
273274

274275
const setIsOpenReportDialog = useBookingActionsStoreContext((state) => state.setIsOpenReportDialog);
275276
const setIsCancelDialogOpen = useBookingActionsStoreContext((state) => state.setIsCancelDialogOpen);
277+
const isOpenWrongAssignmentDialog = useBookingActionsStoreContext(
278+
(state) => state.isOpenWrongAssignmentDialog
279+
);
280+
const setIsOpenWrongAssignmentDialog = useBookingActionsStoreContext(
281+
(state) => state.setIsOpenWrongAssignmentDialog
282+
);
276283

277284
const reportAction = getReportAction(actionContext);
278285
const reportActionWithHandler = {
@@ -512,7 +519,22 @@ function BookingListItem(booking: BookingItemProps) {
512519
userTimeFormat={userTimeFormat}
513520
userTimeZone={userTimeZone}
514521
isRescheduled={isRescheduled}
522+
onAssignmentReasonClick={
523+
isBookingFromRoutingForm ? () => setIsOpenWrongAssignmentDialog(true) : undefined
524+
}
515525
/>
526+
{isBookingFromRoutingForm && (
527+
<WrongAssignmentDialog
528+
isOpenDialog={isOpenWrongAssignmentDialog}
529+
setIsOpenDialog={setIsOpenWrongAssignmentDialog}
530+
bookingUid={booking.uid}
531+
routingReason={booking.assignmentReason[0]?.reasonString ?? null}
532+
guestEmail={booking.attendees[0]?.email ?? ""}
533+
hostEmail={booking.user?.email ?? ""}
534+
hostName={booking.user?.name ?? null}
535+
teamId={booking.eventType?.team?.id ?? null}
536+
/>
537+
)}
516538
</div>
517539
);
518540
}
@@ -525,6 +547,7 @@ const BookingItemBadges = ({
525547
userTimeFormat,
526548
userTimeZone,
527549
isRescheduled,
550+
onAssignmentReasonClick,
528551
}: {
529552
booking: BookingItemProps;
530553
isPending: boolean;
@@ -533,6 +556,7 @@ const BookingItemBadges = ({
533556
userTimeFormat: number | null | undefined;
534557
userTimeZone: string | undefined;
535558
isRescheduled: boolean;
559+
onAssignmentReasonClick?: () => void;
536560
}) => {
537561
const { t } = useLocale();
538562

@@ -561,7 +585,10 @@ const BookingItemBadges = ({
561585
</Badge>
562586
)}
563587
{booking?.assignmentReason.length > 0 && (
564-
<AssignmentReasonTooltip assignmentReason={booking.assignmentReason[0]} />
588+
<AssignmentReasonTooltip
589+
assignmentReason={booking.assignmentReason[0]}
590+
onClick={onAssignmentReasonClick}
591+
/>
565592
)}
566593
{booking.report && (
567594
<Tooltip
@@ -1092,12 +1119,21 @@ const DisplayAttendees = ({
10921119
);
10931120
};
10941121

1095-
const AssignmentReasonTooltip = ({ assignmentReason }: { assignmentReason: AssignmentReason }) => {
1122+
const AssignmentReasonTooltip = ({
1123+
assignmentReason,
1124+
onClick,
1125+
}: {
1126+
assignmentReason: AssignmentReason;
1127+
onClick?: () => void;
1128+
}) => {
10961129
const { t } = useLocale();
10971130
const badgeTitle = assignmentReasonBadgeTitleMap(assignmentReason.reasonEnum);
10981131
return (
10991132
<Tooltip content={<p>{assignmentReason.reasonString}</p>}>
1100-
<Badge className="ltr:mr-2 rtl:ml-2" variant="gray">
1133+
<Badge
1134+
className={classNames("ltr:mr-2 rtl:ml-2", onClick && "cursor-pointer hover:opacity-80")}
1135+
variant="gray"
1136+
onClick={onClick}>
11011137
{t(badgeTitle)}
11021138
</Badge>
11031139
</Tooltip>

apps/web/components/booking/actions/BookingActionsDropdown.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { RejectionReasonDialog } from "@components/dialog/RejectionReasonDialog"
3131
import { ReportBookingDialog } from "@components/dialog/ReportBookingDialog";
3232
import { RerouteDialog } from "@components/dialog/RerouteDialog";
3333
import { RescheduleDialog } from "@components/dialog/RescheduleDialog";
34+
import { WrongAssignmentDialog } from "@components/dialog/WrongAssignmentDialog";
3435

3536
import { useBookingConfirmation } from "../hooks/useBookingConfirmation";
3637
import type { BookingItemProps } from "../types";
@@ -118,6 +119,12 @@ export function BookingActionsDropdown({
118119
const setIsOpenAddGuestsDialog = useBookingActionsStoreContext((state) => state.setIsOpenAddGuestsDialog);
119120
const isOpenReportDialog = useBookingActionsStoreContext((state) => state.isOpenReportDialog);
120121
const setIsOpenReportDialog = useBookingActionsStoreContext((state) => state.setIsOpenReportDialog);
122+
const isOpenWrongAssignmentDialog = useBookingActionsStoreContext(
123+
(state) => state.isOpenWrongAssignmentDialog
124+
);
125+
const setIsOpenWrongAssignmentDialog = useBookingActionsStoreContext(
126+
(state) => state.setIsOpenWrongAssignmentDialog
127+
);
121128
const rerouteDialogIsOpen = useBookingActionsStoreContext((state) => state.rerouteDialogIsOpen);
122129
const setRerouteDialogIsOpen = useBookingActionsStoreContext((state) => state.setRerouteDialogIsOpen);
123130
const isCancelDialogOpen = useBookingActionsStoreContext((state) => state.isCancelDialogOpen);
@@ -440,6 +447,18 @@ export function BookingActionsDropdown({
440447
isRecurring={isRecurring}
441448
status={getBookingStatus()}
442449
/>
450+
{isBookingFromRoutingForm && (
451+
<WrongAssignmentDialog
452+
isOpenDialog={isOpenWrongAssignmentDialog}
453+
setIsOpenDialog={setIsOpenWrongAssignmentDialog}
454+
bookingUid={booking.uid}
455+
routingReason={booking.assignmentReason[0]?.reasonString ?? null}
456+
guestEmail={booking.attendees[0]?.email ?? ""}
457+
hostEmail={booking.user?.email ?? ""}
458+
hostName={booking.user?.name ?? null}
459+
teamId={booking.eventType?.team?.id ?? null}
460+
/>
461+
)}
443462
{booking.paid && booking.payment[0] && (
444463
<ChargeCardDialog
445464
isOpenDialog={chargeCardDialogIsOpen}
@@ -683,6 +702,21 @@ export function BookingActionsDropdown({
683702
{reportActionWithHandler.label}
684703
</DropdownItem>
685704
</DropdownMenuItem>
705+
{isBookingFromRoutingForm && (
706+
<DropdownMenuItem className="rounded-lg" key="report_wrong_assignment">
707+
<DropdownItem
708+
type="button"
709+
color="destructive"
710+
StartIcon="user-x"
711+
onClick={(e) => {
712+
e.stopPropagation();
713+
setIsOpenWrongAssignmentDialog(true);
714+
}}
715+
data-testid="report_wrong_assignment">
716+
{t("report_wrong_assignment")}
717+
</DropdownItem>
718+
</DropdownMenuItem>
719+
)}
686720
</>
687721
<DropdownMenuSeparator />
688722
<DropdownMenuItem

apps/web/components/booking/actions/store.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type BookingActionsStore = {
1717
isOpenReportDialog: boolean;
1818
rerouteDialogIsOpen: boolean;
1919
isCancelDialogOpen: boolean;
20+
isOpenWrongAssignmentDialog: boolean;
2021

2122
// Dialog setters
2223
setRejectionDialogIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
@@ -31,6 +32,7 @@ export type BookingActionsStore = {
3132
setIsOpenReportDialog: React.Dispatch<React.SetStateAction<boolean>>;
3233
setRerouteDialogIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
3334
setIsCancelDialogOpen: React.Dispatch<React.SetStateAction<boolean>>;
35+
setIsOpenWrongAssignmentDialog: React.Dispatch<React.SetStateAction<boolean>>;
3436
};
3537

3638
export const createBookingActionsStore = () => {
@@ -48,6 +50,7 @@ export const createBookingActionsStore = () => {
4850
isOpenReportDialog: false,
4951
rerouteDialogIsOpen: false,
5052
isCancelDialogOpen: false,
53+
isOpenWrongAssignmentDialog: false,
5154

5255
// Dialog setters
5356
setRejectionDialogIsOpen: (isOpen) =>
@@ -101,5 +104,10 @@ export const createBookingActionsStore = () => {
101104
set((state) => ({
102105
isCancelDialogOpen: typeof isOpen === "function" ? isOpen(state.isCancelDialogOpen) : isOpen,
103106
})),
107+
setIsOpenWrongAssignmentDialog: (isOpen) =>
108+
set((state) => ({
109+
isOpenWrongAssignmentDialog:
110+
typeof isOpen === "function" ? isOpen(state.isOpenWrongAssignmentDialog) : isOpen,
111+
})),
104112
}));
105113
};

0 commit comments

Comments
 (0)