feat: team admin: see connected apps of team members (#11036)

* added feature:team admin can see connected apps of members

* fixed the type error

* Update packages/lib/server/queries/teams/index.ts

* Minor fixes

---------

Co-authored-by: alannnc <alannnc@gmail.com>
This commit is contained in:
Abhijeet Singhandalannnc authored and GitHub committed 2023-08-31 01:20:31 +00:00
1 parent 25684f9040
commit 3204d0af34
2 files changed
+68 -13

No files matched your search

@@ -109,7 +109,22 @@ export default function MemberListItem(props: Props) {
const bookerUrl = useBookerUrl();
const bookerUrlWithoutProtocol = bookerUrl.replace(/^https?:\/\//, "");
const bookingLink = !!props.member.username && `${bookerUrlWithoutProtocol}/${props.member.username}`;
const isAdmin = props.team && ["ADMIN", "OWNER"].includes(props.team.membership?.role);
const appList = props.member.connectedApps.map(({ logo, name, externalId }) => {
return logo ? (
externalId ? (
<div className="ltr:mr-2 rtl:ml-2 ">
<Tooltip content={externalId}>
<img className="h-5 w-5" src={logo} alt={`${name} logo`} />
</Tooltip>
</div>
) : (
<div className="ltr:mr-2 rtl:ml-2">
<img className="h-5 w-5" src={logo} alt={`${name} logo`} />
</div>
)
) : null;
});
return (
<li className="divide-subtle divide-y px-5">
<div className="my-4 flex justify-between">
@@ -124,9 +139,9 @@ export default function MemberListItem(props: Props) {
<div className="ms-3 inline-block">
<div className="mb-1 flex">
<span className="text-default mr-1 text-sm font-bold leading-4">{name}</span>
<span className="text-default mr-2 text-sm font-bold leading-4">{name}</span>
{!props.member.accepted && <TeamPill color="orange" text={t("pending")} />}
{isAdmin && props.member.accepted && appList}
{props.member.role && <TeamRole role={props.member.role} />}
</div>
<div className="text-default flex items-center">
+50 -10
View File
@@ -1,8 +1,10 @@
import { Prisma } from "@prisma/client";
import { getAppFromSlug } from "@calcom/app-store/utils";
import { getCalendarCredentials, getConnectedCalendars } from "@calcom/core/CalendarManager";
import { getSlugOrRequestedSlug } from "@calcom/ee/organizations/lib/orgDomains";
import prisma, { baseEventTypeSelect } from "@calcom/prisma";
import { SchedulingType } from "@calcom/prisma/enums";
import { AppCategories, SchedulingType } from "@calcom/prisma/enums";
import { EventTypeMetaDataSchema, teamMetadataSchema } from "@calcom/prisma/zod-utils";
import { WEBAPP_URL } from "../../../constants";
@@ -22,6 +24,17 @@ export async function getTeamWithMembers(args: {
name: true,
id: true,
bio: true,
destinationCalendar: {
select: {
externalId: true,
},
},
selectedCalendars: true,
credentials: {
include: {
app: true,
},
},
});
const teamSelect = Prisma.validator<Prisma.TeamSelect>()({
id: true,
@@ -111,16 +124,43 @@ export async function getTeamWithMembers(args: {
});
if (!team) return null;
const members = team.members.map((obj) => {
return {
...obj.user,
role: obj.role,
accepted: obj.accepted,
disableImpersonation: obj.disableImpersonation,
avatar: `${WEBAPP_URL}/${obj.user.username}/avatar.png`,
};
});
const members = await Promise.all(
team.members.map(async (obj) => {
const calendarCredentials = getCalendarCredentials(obj.user.credentials);
const { connectedCalendars } = await getConnectedCalendars(
calendarCredentials,
obj.user.selectedCalendars,
obj.user.destinationCalendar?.externalId
);
const connectedApps = obj.user.credentials
.map(({ app, id }) => {
const appMetaData = getAppFromSlug(app?.slug);
if (app?.categories.includes(AppCategories.calendar)) {
const externalId = connectedCalendars.find((cal) => cal.credentialId == id)?.primary?.email;
return { name: appMetaData?.name, logo: appMetaData?.logo, slug: appMetaData?.slug, externalId };
}
return { name: appMetaData?.name, logo: appMetaData?.logo, slug: appMetaData?.slug };
})
.sort((a, b) => (a.slug ?? "").localeCompare(b.slug ?? ""));
// Prevent credentials from leaking to frontend
const {
credentials: _credentials,
destinationCalendar: _destinationCalendar,
selectedCalendars: _selectedCalendars,
...rest
} = {
...obj.user,
role: obj.role,
accepted: obj.accepted,
disableImpersonation: obj.disableImpersonation,
avatar: `${WEBAPP_URL}/${obj.user.username}/avatar.png`,
connectedApps,
};
return rest;
})
);
const eventTypes = team.eventTypes.map((eventType) => ({
...eventType,
metadata: EventTypeMetaDataSchema.parse(eventType.metadata),