Skip to content

Commit

Permalink
fix: Use org logo for organization's teams
Browse files Browse the repository at this point in the history
  • Loading branch information
hariombalhara committed Dec 29, 2023
1 parent c4792c5 commit c489414
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 14 deletions.
22 changes: 22 additions & 0 deletions apps/web/components/ui/avatar/TeamAvatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getTeamAvatarUrl } from "@calcom/lib/getAvatarUrl";
import type { Team } from "@calcom/prisma/client";
import { Avatar } from "@calcom/ui";

type TeamAvatarProps = Omit<React.ComponentProps<typeof Avatar>, "alt" | "imageSrc"> & {
team: Pick<Team, "slug" | "name"> & {
organizationId?: number | null;
requestedSlug: string | null;
};
/**
* Useful when allowing the user to upload their own avatar and showing the avatar before it's uploaded
*/
previewSrc?: string | null;
};

/**
* It is aware of the user's organization to correctly show the avatar from the correct URL
*/
export function TeamAvatar(props: TeamAvatarProps) {
const { team, previewSrc = getTeamAvatarUrl(team), ...rest } = props;
return <Avatar {...rest} alt={team.name || "Nameless Team"} imageSrc={previewSrc} />;
}
16 changes: 16 additions & 0 deletions apps/web/pages/api/user/avatar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,22 @@ async function getIdentityData(req: NextApiRequest) {
avatar: getPlaceholderAvatar(org?.logo, org?.name),
};
}

// If just orgId is specified, we return the org avatar
if (orgId) {
const org = await prisma.team.findUnique({
where: {
id: orgId,
},
});

return {
org: org?.slug,
name: org?.name,
email: null,
avatar: getPlaceholderAvatar(org?.logo, org?.name),
};
}
}

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
Expand Down
42 changes: 34 additions & 8 deletions apps/web/pages/event-types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc, TRPCClientError } from "@calcom/trpc/react";
import {
Alert,
Avatar,
Badge,
Button,
ButtonGroup,
Expand Down Expand Up @@ -72,6 +71,8 @@ import useMeQuery from "@lib/hooks/useMeQuery";

import PageWrapper from "@components/PageWrapper";
import SkeletonLoader from "@components/eventtype/SkeletonLoader";
import { TeamAvatar } from "@components/ui/avatar/TeamAvatar";
import { UserAvatar } from "@components/ui/avatar/UserAvatar";
import { UserAvatarGroup } from "@components/ui/avatar/UserAvatarGroup";

type EventTypeGroups = RouterOutputs["viewer"]["eventTypes"]["getByViewer"]["eventTypeGroups"];
Expand All @@ -83,6 +84,7 @@ interface EventTypeListHeadingProps {
membershipCount: number;
teamId?: number | null;
bookerUrl: string;
organizationId: number | null;
}

type EventTypeGroup = EventTypeGroups[number];
Expand Down Expand Up @@ -693,6 +695,7 @@ export const EventTypeList = ({

const EventTypeListHeading = ({
profile,
organizationId,
membershipCount,
teamId,
bookerUrl,
Expand All @@ -709,15 +712,37 @@ const EventTypeListHeading = ({
},
});

// I think profile.slug shouldn't contain team/ prefix for a team because that's a path and not a slug
// But we need to handle it for now instead of changing at the source to avoid side effects at other places.
const userOrTeamSlug = profile.slug?.replace(/^team\//, "");

Check warning on line 717 in apps/web/pages/event-types/index.tsx

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

apps/web/pages/event-types/index.tsx#L717

[@typescript-eslint/no-unused-vars] 'userOrTeamSlug' is assigned a value but never used. Allowed unused vars must match /^_/u.

return (
<div className="mb-4 flex items-center space-x-2">
<Avatar
alt={profile?.name || ""}
href={teamId ? `/settings/teams/${teamId}/profile` : "/settings/my-account/profile"}
imageSrc={`${bookerUrl}${teamId ? "/team" : ""}/${profile.slug}/avatar.png`}
size="md"
className="mt-1 inline-flex justify-center"
/>
{!teamId ? (
<UserAvatar
href="/settings/my-account/profile"
// imageSrc={`${bookerUrl}${teamId ? "/team" : ""}/${userOrTeamSlug}/avatar.png`}
user={{
name: profile.name,
username: profile.slug,
organizationId,
}}
size="md"
className="mt-1 inline-flex justify-center"
/>
) : (
<TeamAvatar
href={`/settings/teams/${teamId}/profile`}
team={{
name: profile.name || "",
slug: profile.slug.replace(/^team\//, ""),
organizationId,
requestedSlug: profile.requestedSlug || null,
}}
size="md"
className="mt-1 inline-flex justify-center"
/>
)}
<div>
<Link
href={teamId ? `/settings/teams/${teamId}/profile` : "/settings/my-account/profile"}
Expand Down Expand Up @@ -861,6 +886,7 @@ const Main = ({
data-testid={`slug-${group.profile.slug}`}
key={group.profile.slug}>
<EventTypeListHeading
organizationId={group.organizationId}
profile={group.profile}
membershipCount={group.metadata.membershipCount}
teamId={group.teamId}
Expand Down
13 changes: 9 additions & 4 deletions packages/features/ee/teams/components/TeamListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import { useState } from "react";
import InviteLinkSettingsModal from "@calcom/ee/teams/components/InviteLinkSettingsModal";
import MemberInvitationModal from "@calcom/ee/teams/components/MemberInvitationModal";
import classNames from "@calcom/lib/classNames";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { getTeamUrlSync } from "@calcom/lib/getBookerUrl/client";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { MembershipRole } from "@calcom/prisma/enums";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import {
Avatar,
Badge,
Button,
ButtonGroup,
Expand Down Expand Up @@ -42,6 +40,8 @@ import {
X,
} from "@calcom/ui/components/icon";

import { TeamAvatar } from "@components/ui/avatar/TeamAvatar";

import { useOrgBranding } from "../../organizations/context/provider";
import { TeamRole } from "./TeamPill";

Expand Down Expand Up @@ -97,9 +97,14 @@ export default function TeamListItem(props: Props) {

const teamInfo = (
<div className="item-center flex px-5 py-5">
<Avatar
<TeamAvatar
size="md"
imageSrc={getPlaceholderAvatar(team?.logo, team?.name as string)}
team={{
name: team.name,
slug: team.slug,
organizationId: team.parentId,
requestedSlug: team.requestedSlug,
}}
alt="Team Logo"
className="inline-flex justify-center"
/>
Expand Down
5 changes: 5 additions & 0 deletions packages/lib/getAvatarUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export function getTeamAvatarUrl(
if (team.logoUrl) {
return team.logoUrl;
}

if (team.organizationId) {
// For an organization, all it's teams have the same logo as the organization
return `${WEBAPP_URL}/api/user/avatar?orgId=${team.organizationId}`;
}
const slug = team.slug ?? team.requestedSlug;
return `${WEBAPP_URL}/team/${slug}/avatar.png${team.organizationId ? `?orgId=${team.organizationId}` : ""}`;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,12 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) =>
type EventTypeGroup = {
teamId?: number | null;
parentId?: number | null;
organizationId: number | null;
bookerUrl: string;
membershipRole?: MembershipRole | null;
profile: {
slug: (typeof user)["username"];
requestedSlug?: string | null;
name: (typeof user)["name"];
image: string;
};
Expand All @@ -212,6 +214,7 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) =>
teamId: null,
bookerUrl,
membershipRole: null,
organizationId: user.organizationId,
profile: {
slug: user.username,
name: user.name,
Expand Down Expand Up @@ -272,6 +275,7 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) =>
}
return {
teamId: team.id,
organizationId: team.parentId,
parentId: team.parentId,
bookerUrl: getBookerBaseUrlSync(team.parent?.slug ?? null),
membershipRole:
Expand All @@ -285,6 +289,7 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) =>
organizationId: team.parentId,
}),
name: team.name,
requestedSlug: team.metadata?.requestedSlug ?? null,
slug,
},
metadata: {
Expand Down
15 changes: 13 additions & 2 deletions packages/trpc/server/routers/viewer/teams/list.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,25 @@ export const listHandler = async ({ ctx }: ListOptions) => {
});

return memberships
.filter((mmship) => {
.map((mmship) => {
const metadata = teamMetadataSchema.parse(mmship.team.metadata);
return !metadata?.isOrganization;
mmship.team.metadata = metadata;
return {
...mmship,
team: {
...mmship.team,
metadata,
},
};
})
.filter((mmship) => {
return !mmship.team.metadata?.isOrganization;
})
.map(({ team: { inviteTokens, ..._team }, ...membership }) => ({
role: membership.role,
accepted: membership.accepted,
..._team,
requestedSlug: _team.metadata?.requestedSlug,
/** To prevent breaking we only return non-email attached token here, if we have one */
inviteToken: inviteTokens.find((token) => token.identifier === `invite-link-for-teamId-${_team.id}`),
}));
Expand Down

0 comments on commit c489414

Please sign in to comment.