diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json
index 9a80c01752..ef6b5cabaf 100644
--- a/apps/web/public/static/locales/en/common.json
+++ b/apps/web/public/static/locales/en/common.json
@@ -2528,5 +2528,6 @@
"outlook_connect_atom_already_connected_label": "Connected Outlook Calendar",
"outlook_connect_atom_loading_label": "Checking Outlook Calendar",
"booking_question_response_variables": "Booking question response variables",
+ "managed_by_teamAdmins": "Managed by {{teamAdmins}}",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
diff --git a/packages/features/eventtypes/components/EventTypeDescription.tsx b/packages/features/eventtypes/components/EventTypeDescription.tsx
index afe8c59c13..f5c2aeaf12 100644
--- a/packages/features/eventtypes/components/EventTypeDescription.tsx
+++ b/packages/features/eventtypes/components/EventTypeDescription.tsx
@@ -1,4 +1,5 @@
import type { Prisma } from "@prisma/client";
+import Link from "next/link";
import { useMemo } from "react";
import type { z } from "zod";
@@ -10,7 +11,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { baseEventTypeSelect } from "@calcom/prisma";
import { SchedulingType } from "@calcom/prisma/enums";
import type { EventTypeModel } from "@calcom/prisma/zod";
-import { Badge } from "@calcom/ui";
+import { Badge, Tooltip } from "@calcom/ui";
export type EventTypeDescriptionProps = {
eventType: Pick<
@@ -19,6 +20,7 @@ export type EventTypeDescriptionProps = {
> & {
descriptionAsSafeHTML?: string | null;
recurringEvent: Prisma.JsonValue;
+ managedBy?: { teamId: number | undefined; admins: string[] };
};
className?: string;
shortenDescription?: boolean;
@@ -40,6 +42,32 @@ export const EventTypeDescription = ({
const paymentAppData = getPaymentAppData(eventType);
+ const showManagedBadge = eventType.metadata?.managedEventConfig && !isPublic;
+ const numberOfAdmins = eventType.managedBy?.admins.length || 0;
+ let managedByToolTipContent;
+
+ if (numberOfAdmins > 0) {
+ managedByToolTipContent = t("managed_by_teamAdmins", {
+ teamAdmins: eventType.managedBy?.admins?.slice(0, 2).join(", "),
+ });
+ if (numberOfAdmins > 2) {
+ managedByToolTipContent = (
+ <>
+ {`${managedByToolTipContent} ${t("and")} `}
+ {!!eventType.managedBy?.teamId ? (
+
+ {`${numberOfAdmins - 2} ${t("more")}`}
+
+ ) : (
+ {`${numberOfAdmins - 2} ${t("more")}`}
+ )}
+ >
+ );
+ }
+ }
+
return (
<>
@@ -78,11 +106,22 @@ export const EventTypeDescription = ({
)}
- {eventType.metadata?.managedEventConfig && !isPublic && (
-
- {t("managed")}
-
- )}
+ {showManagedBadge &&
+ (numberOfAdmins === 0 ? (
+
+
+ {t("managed")}
+
+
+ ) : (
+
+
+
+ {t("managed")}
+
+
+
+ ))}
{recurringEvent?.count && recurringEvent.count > 0 && (
diff --git a/packages/lib/event-types/getEventTypesByViewer.ts b/packages/lib/event-types/getEventTypesByViewer.ts
index 189f262102..6a9c4981db 100644
--- a/packages/lib/event-types/getEventTypesByViewer.ts
+++ b/packages/lib/event-types/getEventTypesByViewer.ts
@@ -105,32 +105,43 @@ export const getEventTypesByViewer = async (user: User, filters?: Filters, forRo
type UserEventTypes = (typeof profileEventTypes)[number];
- const mapEventType = async (eventType: UserEventTypes) => ({
- ...eventType,
- safeDescription: eventType?.description ? markdownToSafeHTML(eventType.description) : undefined,
- users: await Promise.all(
- (!!eventType?.hosts?.length ? eventType?.hosts.map((host) => host.user) : eventType.users).map(
- async (u) =>
- await UserRepository.enrichUserWithItsProfile({
- user: u,
- })
- )
- ),
- metadata: eventType.metadata ? EventTypeMetaDataSchema.parse(eventType.metadata) : null,
- children: await Promise.all(
- (eventType.children || []).map(async (c) => ({
- ...c,
- users: await Promise.all(
- c.users.map(
- async (u) =>
- await UserRepository.enrichUserWithItsProfile({
- user: u,
- })
- )
- ),
- }))
- ),
- });
+ const mapEventType = async (eventType: UserEventTypes) => {
+ const { parent, ...rest } = eventType;
+ return {
+ ...rest,
+ safeDescription: eventType?.description ? markdownToSafeHTML(eventType.description) : undefined,
+ users: await Promise.all(
+ (!!eventType?.hosts?.length ? eventType?.hosts.map((host) => host.user) : eventType.users).map(
+ async (u) =>
+ await UserRepository.enrichUserWithItsProfile({
+ user: u,
+ })
+ )
+ ),
+ metadata: eventType.metadata ? EventTypeMetaDataSchema.parse(eventType.metadata) : null,
+ children: await Promise.all(
+ (eventType.children || []).map(async (c) => ({
+ ...c,
+ users: await Promise.all(
+ c.users.map(
+ async (u) =>
+ await UserRepository.enrichUserWithItsProfile({
+ user: u,
+ })
+ )
+ ),
+ }))
+ ),
+ managedBy: {
+ teamId: parent?.team?.id,
+ admins:
+ parent?.team?.members
+ .filter((member) => member.role === MembershipRole.ADMIN && member.accepted === true)
+ .map((member) => member.user?.name || member.user?.username || member.user?.email)
+ .filter((name) => !!name) || [],
+ },
+ };
+ };
const userEventTypes = (await Promise.all(profileEventTypes.map(mapEventType))).filter((eventType) => {
const isAChildEvent = eventType.parentId;
diff --git a/packages/lib/server/eventTypeSelect.ts b/packages/lib/server/eventTypeSelect.ts
index a0174cab0d..2e039ca490 100644
--- a/packages/lib/server/eventTypeSelect.ts
+++ b/packages/lib/server/eventTypeSelect.ts
@@ -50,4 +50,26 @@ export const eventTypeSelect = Prisma.validator()({
secondaryEmailId: true,
bookingLimits: true,
durationLimits: true,
+ parent: {
+ select: {
+ team: {
+ select: {
+ id: true,
+ members: {
+ select: {
+ user: {
+ select: {
+ name: true,
+ username: true,
+ email: true,
+ },
+ },
+ role: true,
+ accepted: true,
+ },
+ },
+ },
+ },
+ },
+ },
});