From 100136aae7b7c413bc2a07ed697eb142d16c22c9 Mon Sep 17 00:00:00 2001 From: Vijay Date: Tue, 6 Aug 2024 17:16:24 +0530 Subject: [PATCH] fix: #15857 add managed by tooltip (#15861) * managed by tooltip * updated for team admin * updated for typechecks and naming * for multiple admins * update to show team link when more than 2 admins * nit/chore : variable naming * improvement --------- Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Co-authored-by: unknown --- apps/web/public/static/locales/en/common.json | 1 + .../components/EventTypeDescription.tsx | 51 +++++++++++++-- .../lib/event-types/getEventTypesByViewer.ts | 63 +++++++++++-------- packages/lib/server/eventTypeSelect.ts | 22 +++++++ 4 files changed, 105 insertions(+), 32 deletions(-) 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, + }, + }, + }, + }, + }, + }, });