From 44d6c1f3fb5e942fa304a90fca273f4cdd550c24 Mon Sep 17 00:00:00 2001
From: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Date: Tue, 10 Dec 2024 16:31:48 -0500
Subject: [PATCH] feat: Show routing reason on booking page [CAL-4814] (#18053)
---
.../components/booking/BookingListItem.tsx | 379 ++++++++++--------
.../booking/assignmentReasonBadgeTitleMap.ts | 18 +
apps/web/public/static/locales/en/common.json | 4 +
.../routers/viewer/bookings/get.handler.ts | 6 +
4 files changed, 245 insertions(+), 162 deletions(-)
create mode 100644 apps/web/lib/booking/assignmentReasonBadgeTitleMap.ts
diff --git a/apps/web/components/booking/BookingListItem.tsx b/apps/web/components/booking/BookingListItem.tsx
index 0d9cbc216b..3cfa857580 100644
--- a/apps/web/components/booking/BookingListItem.tsx
+++ b/apps/web/components/booking/BookingListItem.tsx
@@ -1,3 +1,4 @@
+import type { AssignmentReason } from "@prisma/client";
import Link from "next/link";
import { useState } from "react";
import { Controller, useFieldArray, useForm } from "react-hook-form";
@@ -45,6 +46,8 @@ import {
Tooltip,
} from "@calcom/ui";
+import assignmentReasonBadgeTitleMap from "@lib/booking/assignmentReasonBadgeTitleMap";
+
import { AddGuestsDialog } from "@components/dialog/AddGuestsDialog";
import { ChargeCardDialog } from "@components/dialog/ChargeCardDialog";
import { EditLocationDialog } from "@components/dialog/EditLocationDialog";
@@ -508,72 +511,94 @@ function BookingListItem(booking: BookingItemProps) {
-
-
-
- {eventTypeColor && }
-
-
- {startTime}
-
- {formatTime(booking.startTime, userTimeFormat, userTimeZone)} -{" "}
- {formatTime(booking.endTime, userTimeFormat, userTimeZone)}
-
-
- {!isPending && (
-
- {(provider?.label || locationToDisplay?.startsWith("https://")) &&
- locationToDisplay.startsWith("http") && (
- e.stopPropagation()}
- target="_blank"
- title={locationToDisplay}
- rel="noreferrer"
- className="text-sm leading-6 text-blue-600 hover:underline dark:text-blue-400">
-
- {provider?.iconUrl && (
- 
- )}
- {provider?.label
- ? t("join_event_location", { eventLocationType: provider?.label })
- : t("join_meeting")}
-
-
- )}
+
+
+
+
+ {eventTypeColor && (
+
+ )}
+
+
+ {startTime}
+
+ {formatTime(booking.startTime, userTimeFormat, userTimeZone)} -{" "}
+ {formatTime(booking.endTime, userTimeFormat, userTimeZone)}
+
- )}
+ {!isPending && (
+
+ )}
+
+
+
+ |
+
+
+ {/* Time and Badges for mobile */}
+
+
+ {startTime}
+
+ {formatTime(booking.startTime, userTimeFormat, userTimeZone)} -{" "}
+ {formatTime(booking.endTime, userTimeFormat, userTimeZone)}
+
+
+
+
{isPending && (
-
+
{t("unconfirmed")}
)}
{booking.eventType?.team && (
-
+
{booking.eventType.team.name}
)}
- {booking.paid && !booking.payment[0] ? (
-
- {t("error_collecting_card")}
+ {showPendingPayment && (
+
+ {t("pending_payment")}
- ) : booking.paid ? (
-
- {booking.payment[0].paymentOption === "HOLD" ? t("card_held") : t("paid")}
-
- ) : null}
+ )}
{recurringDates !== undefined && (
-
- |
-
-
- {/* Time and Badges for mobile */}
-
-
- {startTime}
-
- {formatTime(booking.startTime, userTimeFormat, userTimeZone)} -{" "}
- {formatTime(booking.endTime, userTimeFormat, userTimeZone)}
-
+
+
+
+ {title}
+
+
+ {showPendingPayment && (
+
+ {t("pending_payment")}
+
+ )}
-
-
- {isPending && (
-
- {t("unconfirmed")}
-
- )}
- {booking.eventType?.team && (
-
- {booking.eventType.team.name}
-
- )}
- {showPendingPayment && (
-
- {t("pending_payment")}
-
- )}
- {recurringDates !== undefined && (
-
-
+ "{booking.description}"
+
+ )}
+ {booking.attendees.length !== 0 && (
+
-
- )}
-
-
-
-
- {title}
-
-
- {showPendingPayment && (
-
- {t("pending_payment")}
-
+ )}
+ {isCancelled && booking.rescheduled && (
+
+
+
)}
- {booking.description && (
-
- "{booking.description}"
+
+ |
+
+ {isUpcoming && !isCancelled ? (
+ <>
+ {isPending && }
+ {isConfirmed && }
+ {isRejected && {t("rejected")} }
+ >
+ ) : null}
+ {isBookingInPast && isPending && !isConfirmed ? : null}
+ {isBookingInPast && isConfirmed ? : null}
+ {(showViewRecordingsButton || showCheckRecordingButton) && (
+
+ )}
+ {isCancelled && booking.rescheduled && (
+
+
+
+ )}
+ {booking.status === "ACCEPTED" &&
+ booking.paid &&
+ booking.payment[0]?.paymentOption === "HOLD" && (
+
)}
- {booking.attendees.length !== 0 && (
-
- )}
- {isCancelled && booking.rescheduled && (
-
-
-
- )}
-
-
- |
-
- {isUpcoming && !isCancelled ? (
- <>
- {isPending && }
- {isConfirmed && }
- {isRejected && {t("rejected")} }
- >
- ) : null}
- {isBookingInPast && isPending && !isConfirmed ? : null}
- {isBookingInPast && isConfirmed ? : null}
- {(showViewRecordingsButton || showCheckRecordingButton) && (
-
- )}
- {isCancelled && booking.rescheduled && (
-
-
-
- )}
- {booking.status === "ACCEPTED" && booking.paid && booking.payment[0]?.paymentOption === "HOLD" && (
-
- )}
- |
+
+
+
+
{isBookingReroutable(parsedBooking) && (
{
+ const { t } = useLocale();
+
+ return (
+
+ {isPending && (
+
+ {t("unconfirmed")}
+
+ )}
+ {booking.eventType?.team && (
+
+ {booking.eventType.team.name}
+
+ )}
+ {booking?.assignmentReason.length > 0 && (
+
+ )}
+ {booking.paid && !booking.payment[0] ? (
+
+ {t("error_collecting_card")}
+
+ ) : booking.paid ? (
+
+ {booking.payment[0].paymentOption === "HOLD" ? t("card_held") : t("paid")}
+
+ ) : null}
+ {recurringDates !== undefined && (
+
+
+
+ )}
+
+ );
+};
+
interface RecurringBookingsTooltipProps {
booking: BookingItemProps;
recurringDates: Date[];
@@ -1205,4 +1246,18 @@ const DisplayAttendees = ({
);
};
+const AssignmentReasonTooltip = ({ assignmentReason }: { assignmentReason: AssignmentReason }) => {
+ const { t } = useLocale();
+ const badgeTitle = assignmentReasonBadgeTitleMap(assignmentReason.reasonEnum);
+ return (
+ {assignmentReason.reasonString}}>
+
+
+ {t(badgeTitle)}
+
+
+
+ );
+};
+
export default BookingListItem;
diff --git a/apps/web/lib/booking/assignmentReasonBadgeTitleMap.ts b/apps/web/lib/booking/assignmentReasonBadgeTitleMap.ts
new file mode 100644
index 0000000000..b695ac2e67
--- /dev/null
+++ b/apps/web/lib/booking/assignmentReasonBadgeTitleMap.ts
@@ -0,0 +1,18 @@
+import { AssignmentReasonEnum } from "@calcom/prisma/enums";
+
+const assignmentReasonBadgeTitleMap = (assignmentReason: AssignmentReasonEnum) => {
+ switch (assignmentReason) {
+ case AssignmentReasonEnum.ROUTING_FORM_ROUTING || AssignmentReasonEnum.ROUTING_FORM_ROUTING_FALLBACK:
+ return "routed";
+ case AssignmentReasonEnum.REASSIGNED:
+ return "reassigned";
+ case AssignmentReasonEnum.REROUTED:
+ return "rerouted";
+ case AssignmentReasonEnum.SALESFORCE_ASSIGNMENT:
+ return "salesforce_assignment";
+ default:
+ return "routed";
+ }
+};
+
+export default assignmentReasonBadgeTitleMap;
diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json
index 5401779406..3c5c886669 100644
--- a/apps/web/public/static/locales/en/common.json
+++ b/apps/web/public/static/locales/en/common.json
@@ -2863,5 +2863,9 @@
"lock_attribute_for_assignment_description": "Locking would only allow assignments from Directory Sync",
"attribute_edited_successfully": "Attribute edited successfully",
"new_group_option": "New group option",
+ "routed": "Routed",
+ "reassigned": "Reassigned",
+ "rerouted": "Rerouted",
+ "salesforce_assigned": "Salesforce assignment",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
diff --git a/packages/trpc/server/routers/viewer/bookings/get.handler.ts b/packages/trpc/server/routers/viewer/bookings/get.handler.ts
index b76472c999..df8d6af1fc 100644
--- a/packages/trpc/server/routers/viewer/bookings/get.handler.ts
+++ b/packages/trpc/server/routers/viewer/bookings/get.handler.ts
@@ -1,3 +1,5 @@
+import { Prisma as PrismaClientType } from "@prisma/client";
+
import { parseRecurringEvent, parseEventTypeColor } from "@calcom/lib";
import getAllUserBookings from "@calcom/lib/bookings/getAllUserBookings";
import type { PrismaClient } from "@calcom/prisma";
@@ -283,6 +285,10 @@ export async function getBookings({
},
},
},
+ assignmentReason: {
+ orderBy: { createdAt: PrismaClientType.SortOrder.desc },
+ take: 1,
+ },
};
const [
|