Compare commits
1 Commits
main
...
12-29-fix_
Author | SHA1 | Date | |
---|---|---|---|
ea3cd09be0 |
|
@ -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} />;
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -30,7 +30,6 @@ import type { RouterOutputs } from "@calcom/trpc/react";
|
|||
import { trpc, TRPCClientError } from "@calcom/trpc/react";
|
||||
import {
|
||||
Alert,
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
|
@ -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"];
|
||||
|
@ -83,6 +84,7 @@ interface EventTypeListHeadingProps {
|
|||
membershipCount: number;
|
||||
teamId?: number | null;
|
||||
bookerUrl: string;
|
||||
organizationId: number | null;
|
||||
}
|
||||
|
||||
type EventTypeGroup = EventTypeGroups[number];
|
||||
|
@ -693,6 +695,7 @@ export const EventTypeList = ({
|
|||
|
||||
const EventTypeListHeading = ({
|
||||
profile,
|
||||
organizationId,
|
||||
membershipCount,
|
||||
teamId,
|
||||
bookerUrl,
|
||||
|
@ -711,13 +714,32 @@ const EventTypeListHeading = ({
|
|||
|
||||
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`}
|
||||
{!teamId ? (
|
||||
<UserAvatar
|
||||
href="/settings/my-account/profile"
|
||||
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 || "",
|
||||
// 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.
|
||||
slug: profile.slug?.replace(/^team\//, "") || null,
|
||||
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"}
|
||||
|
@ -861,6 +883,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}
|
||||
|
|
|
@ -5,7 +5,6 @@ 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";
|
||||
|
@ -13,7 +12,6 @@ import { MembershipRole } from "@calcom/prisma/enums";
|
|||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
|
@ -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";
|
||||
|
||||
|
@ -97,10 +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)}
|
||||
alt="Team Logo"
|
||||
team={{
|
||||
name: team.name,
|
||||
slug: team.slug,
|
||||
organizationId: team.parentId,
|
||||
requestedSlug: team.requestedSlug || null,
|
||||
}}
|
||||
className="inline-flex justify-center"
|
||||
/>
|
||||
<div className="ms-3 inline-block truncate">
|
||||
|
|
|
@ -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}` : ""}`;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -285,6 +289,7 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) =>
|
|||
organizationId: team.parentId,
|
||||
}),
|
||||
name: team.name,
|
||||
requestedSlug: team.metadata?.requestedSlug ?? null,
|
||||
slug,
|
||||
},
|
||||
metadata: {
|
||||
|
|
|
@ -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}`),
|
||||
}));
|
||||
|
|
Loading…
Reference in New Issue
Block a user