refactor: migrate web Avatar to @makeplane/propel Avatar (#9724)

* refactor: migrate web Avatar to @makeplane/propel Avatar

Replace @plane/ui Avatar and AvatarGroup with the published Propel
components across apps/web. 41 files, 51 Avatar sites, 4 group sites.

Propel's size scale is @plane/ui's shifted one step, so every enum maps
with no pixel change: sm(16)->2xs, md(20)->xs, base(24)->sm, lg(28)->md.
Unsized avatars took the old md default and become xs.

Notable mapping decisions:

- name -> alt plus fallback={name?.[0]?.toUpperCase()}, preserving the
  initial that @plane/ui derived internally.
- Six numeric px sizes mapped to the nearest enum step. Five are exact;
  the notification card avatar moves 42px -> xl (40px).
- Ten classNames removed, as published Propel takes no className. Seven
  were already redundant with Propel's base class (shrink-0, bg-layer-1);
  the remaining three overrode initials typography, which Propel now sets
  per size.
- shape="circle" and fallback colour props dropped; Propel is always
  circular and picks initials colours from a fixed palette.

AvatarGroup needs app chrome. @plane/ui defaulted to max={2}, rendering
two avatars plus a +N counter, while Propel's AvatarGroup renders every
child. No call site passed max explicitly, so all four relied on that
default and would have rendered entire member lists. The new
common/avatar-group-overflow.tsx restores the cap around Propel's group.
ButtonAvatars' size prop is narrowed to AvatarGroupSize accordingly;
no caller passes size.

Tooltips are intentionally not migrated. @plane/ui Avatar wrapped itself
in a Tooltip defaulting to on, and Propel has no built-in tooltip, so
roughly 29 direct sites plus the ButtonAvatars subtree lose a tooltip.
ButtonAvatars additionally inverted the flag (showTooltip={!showTooltip}),
so its callers passing false were the ones showing tooltips. Restoring
these is deliberately deferred to a follow-up.

* refactor: use Propel Avatar for AvatarGroup overflow count

Replace the custom CSS +N counter with a Propel Avatar fallback,
matching the Avatar Group overflow pattern. Size still comes from
the group context.
This commit is contained in:
Anmol Singh Bhatia
2026-09-01 17:45:42 +05:30
committed by GitHub
parent fd68e7d919
commit 12eb6015de
41 changed files with 328 additions and 172 deletions

View File

@@ -10,13 +10,14 @@ import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import useSWR from "swr";
import { UserRound } from "lucide-react";
import { Avatar } from "@makeplane/propel/components/avatar";
import { useTranslation } from "@plane/i18n";
import { Logo } from "@plane/propel/emoji-icon-picker";
import { ProjectIcon } from "@plane/propel/icons";
// plane package imports
import type { AnalyticsTableDataMap, WorkItemInsightColumns } from "@plane/types";
// plane web components
import { Avatar } from "@plane/ui";
import { getFileURL } from "@plane/utils";
// hooks
import { useAnalytics } from "@/hooks/store/use-analytics";
@@ -111,10 +112,10 @@ const WorkItemsInsightTable = observer(function WorkItemsInsightTable() {
<div className="flex items-center gap-2">
{row.original.avatar_url && row.original.avatar_url !== "" ? (
<Avatar
name={row.original.display_name}
alt={row.original.display_name}
fallback={row.original.display_name?.[0]?.toUpperCase()}
src={getFileURL(row.original.avatar_url)}
size={24}
shape="circle"
size="sm"
/>
) : (
<div className="flex h-4 w-4 flex-shrink-0 items-center justify-center overflow-hidden rounded-full bg-layer-1 capitalize">

View File

@@ -9,6 +9,7 @@ import { useCallback, useEffect, useState } from "react";
import { observer } from "mobx-react";
import { usePathname } from "next/navigation";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import type { EditorRefApi } from "@plane/editor";
import { useHashScroll } from "@plane/hooks";
@@ -22,7 +23,6 @@ import { LiteTextEditor } from "@/components/editor/lite-text";
import { CommentReactions } from "../comment-reaction";
import { CommentCardEditForm } from "./edit-form";
import { EmojiReactionButton, EmojiReactionPicker } from "@plane/propel/emoji-reaction";
import { Avatar } from "@plane/ui";
import { useMember } from "@/hooks/store/use-member";
export type TCommentCardDisplayProps = {
@@ -118,7 +118,7 @@ export const CommentCardDisplay = observer(function CommentCardDisplay(props: TC
</div>
)}
<div className="relative mb-3 flex w-full items-center gap-2">
<Avatar size="sm" name={displayName} src={getFileURL(avatarUrl)} className="shrink-0" />
<Avatar alt={displayName} fallback={displayName?.[0]?.toUpperCase()} size="2xs" src={getFileURL(avatarUrl)} />
<div className="flex flex-1 flex-wrap items-center gap-1">
<div className="text-caption-sm-medium">{displayName}</div>
<div className="text-caption-sm-regular text-tertiary">

View File

@@ -7,7 +7,7 @@
import { observer } from "mobx-react";
import { CloseIcon } from "@plane/propel/icons";
// plane ui
import { Avatar } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
// helpers
import { getFileURL } from "@plane/utils";
// types
@@ -36,10 +36,10 @@ export const AppliedMembersFilters = observer(function AppliedMembersFilters(pro
return (
<div key={memberId} className="flex items-center gap-1 rounded-sm bg-layer-1 px-1.5 py-1 text-11">
<Avatar
name={memberDetails.display_name}
alt={memberDetails.display_name}
fallback={memberDetails.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails.avatar_url)}
showTooltip={false}
size={"sm"}
size="2xs"
/>
<span className="normal-case">{memberDetails.display_name}</span>
{editable && (

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import React from "react";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { AvatarGroup, type AvatarGroupSize } from "@makeplane/propel/components/avatar-group";
type Props = {
children: React.ReactNode;
/**
* Shared size for every avatar in the stack.
*/
size: AvatarGroupSize;
/**
* The maximum number of avatars to display. If the number of children exceeds this value, the
* additional avatars are replaced by a count of the remaining avatars.
* @default 2
*/
max?: number;
};
/**
* An overlapping avatar stack that collapses the tail into a `+N` counter.
*
* Propel's `AvatarGroup` renders every child it is given, so the capping behaviour that
* `@plane/ui`'s `AvatarGroup` provided through `max` lives here as app chrome. The overflow
* indicator is a Propel `Avatar` with a `fallback`, matching the Avatar Group overflow pattern.
*/
export function AvatarGroupOverflow(props: Props) {
const { children, size, max = 2 } = props;
const avatars = React.Children.toArray(children);
const totalAvatars = avatars.length;
// If there is only one avatar past the limit, render it instead of a "+1" counter.
const maxAvatarsToRender = totalAvatars <= max + 1 ? max + 1 : max;
const overflowCount = totalAvatars - max;
return (
<AvatarGroup size={size}>
{avatars.slice(0, maxAvatarsToRender)}
{maxAvatarsToRender < totalAvatars && (
<Avatar alt={`${overflowCount} more members`} fallback={`+${overflowCount}`} />
)}
</AvatarGroup>
);
}

View File

@@ -8,7 +8,8 @@ import { useMemo, useState } from "react";
import { sortBy } from "lodash-es";
import { observer } from "mobx-react";
// ui
import { Avatar, Loader } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
import { Loader } from "@plane/ui";
// components
import { getFileURL } from "@plane/utils";
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
@@ -78,10 +79,10 @@ export const FilterCreatedBy = observer(function FilterCreatedBy(props: Props) {
onClick={() => handleUpdate(member.id)}
icon={
<Avatar
name={member.display_name}
alt={member.display_name}
fallback={member.display_name?.[0]?.toUpperCase()}
src={getFileURL(member.avatar_url)}
showTooltip={false}
size="md"
size="xs"
/>
}
title={currentUser?.id === member.id ? "You" : member?.display_name}

View File

@@ -6,9 +6,10 @@
import { observer } from "mobx-react";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { useTranslation } from "@plane/i18n";
import type { TDescriptionVersion } from "@plane/types";
import { Avatar, CustomMenu } from "@plane/ui";
import { CustomMenu } from "@plane/ui";
import { calculateTimeAgo, getFileURL } from "@plane/utils";
// hooks
import { useMember } from "@/hooks/store/use-member";
@@ -31,8 +32,9 @@ export const DescriptionVersionsDropdownItem = observer(function DescriptionVers
<CustomMenu.MenuItem key={version.id} className="flex items-center gap-1" onClick={() => onClick(version.id)}>
<span className="flex-shrink-0">
<Avatar
name={versionCreator?.display_name ?? t("common.deactivated_user")}
size="sm"
alt={versionCreator?.display_name ?? t("common.deactivated_user")}
fallback={(versionCreator?.display_name ?? t("common.deactivated_user"))?.[0]?.toUpperCase()}
size="2xs"
src={getFileURL(versionCreator?.avatar_url ?? "")}
/>
</span>

View File

@@ -7,6 +7,7 @@
import { useCallback, useRef } from "react";
import { observer } from "mobx-react";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import type { EditorRefApi } from "@plane/editor";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
@@ -14,7 +15,7 @@ import { CopyIcon, ChevronLeftIcon, ChevronRightIcon } from "@plane/propel/icons
import { setToast, TOAST_TYPE } from "@plane/propel/toast";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import type { TDescriptionVersion } from "@plane/types";
import { Avatar, EModalPosition, EModalWidth, Loader, ModalCore } from "@plane/ui";
import { EModalPosition, EModalWidth, Loader, ModalCore } from "@plane/ui";
import { calculateTimeAgo, cn, getFileURL } from "@plane/utils";
// components
import { RichTextEditor } from "@/components/editor/rich-text";
@@ -85,9 +86,10 @@ export const DescriptionVersionsModal = observer(function DescriptionVersionsMod
{t("description_versions.edited_by")}
<span className="flex-shrink-0">
<Avatar
size="sm"
alt={versionCreator?.display_name}
fallback={versionCreator?.display_name?.[0]?.toUpperCase()}
size="2xs"
src={getFileURL(versionCreator?.avatar_url ?? "")}
name={versionCreator?.display_name}
/>
</span>
</p>

View File

@@ -6,8 +6,8 @@
import { observer } from "mobx-react";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { useTranslation } from "@plane/i18n";
import { Avatar } from "@plane/ui";
import { getFileURL } from "@plane/utils";
// assets
import emptyMembers from "@/app/assets/empty-state/empty_members.svg?url";
@@ -43,7 +43,12 @@ export const AssigneeStatComponent = observer(function AssigneeStatComponent(pro
key={assignee?.id}
title={
<div className="flex items-center gap-2">
<Avatar name={assignee?.title ?? undefined} src={getFileURL(assignee?.avatar_url ?? "")} />
<Avatar
alt={assignee?.title ?? undefined}
fallback={assignee?.title?.[0]?.toUpperCase()}
src={getFileURL(assignee?.avatar_url ?? "")}
size="xs"
/>
<span>{assignee?.title ?? ""}</span>
</div>
}

View File

@@ -12,6 +12,7 @@ import { CalendarCheck } from "lucide-react";
// headless ui
import { Tab } from "@headlessui/react";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { useTranslation } from "@plane/i18n";
import { PriorityIcon } from "@plane/propel/icons";
import { Tooltip } from "@makeplane/propel/components/tooltip";
@@ -19,7 +20,7 @@ import type { TWorkItemFilterCondition } from "@plane/shared-state";
import type { ICycle } from "@plane/types";
import { EIssuesStoreType } from "@plane/types";
// ui
import { Loader, Avatar } from "@plane/ui";
import { Loader } from "@plane/ui";
import { cn, renderFormattedDate, renderFormattedDateWithoutYear, getFileURL } from "@plane/utils";
// assets
import darkAssigneeAsset from "@/app/assets/empty-state/active-cycle/assignee-dark.webp?url";
@@ -273,8 +274,10 @@ export const ActiveCycleStats = observer(function ActiveCycleStats(props: Active
title={
<div className="flex items-center gap-2">
<Avatar
name={assignee?.display_name ?? undefined}
alt={assignee?.display_name ?? undefined}
fallback={assignee?.display_name?.[0]?.toUpperCase()}
src={getFileURL(assignee?.avatar_url ?? "")}
size="xs"
/>
<span>{assignee.display_name}</span>

View File

@@ -11,13 +11,15 @@ import { SquareUser } from "lucide-react";
// plane types
import { EEstimateSystem } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Avatar } from "@makeplane/propel/components/avatar";
import { MembersPropertyIcon, WorkItemsIcon } from "@plane/propel/icons";
import type { ICycle } from "@plane/types";
// plane ui
import { Avatar, AvatarGroup, TextArea } from "@plane/ui";
import { TextArea } from "@plane/ui";
// helpers
import { getFileURL } from "@plane/utils";
// hooks
import { AvatarGroupOverflow } from "@/components/common/avatar-group-overflow";
import { useProjectEstimates } from "@/hooks/store/estimates";
import { useMember } from "@/hooks/store/use-member";
// plane web constants
@@ -83,7 +85,12 @@ export const CycleSidebarDetails = observer(function CycleSidebarDetails(props:
</div>
<div className="flex w-3/5 items-center rounded-xs">
<div className="flex items-center gap-2.5">
<Avatar name={cycleOwnerDetails?.display_name} src={getFileURL(cycleOwnerDetails?.avatar_url ?? "")} />
<Avatar
alt={cycleOwnerDetails?.display_name}
fallback={cycleOwnerDetails?.display_name?.[0]?.toUpperCase()}
src={getFileURL(cycleOwnerDetails?.avatar_url ?? "")}
size="xs"
/>
<span className="text-13 text-secondary">{cycleOwnerDetails?.display_name}</span>
</div>
</div>
@@ -98,19 +105,19 @@ export const CycleSidebarDetails = observer(function CycleSidebarDetails(props:
<div className="flex items-center gap-2.5">
{cycleDetails?.assignee_ids && cycleDetails.assignee_ids.length > 0 ? (
<>
<AvatarGroup showTooltip>
<AvatarGroupOverflow size="xs">
{cycleDetails.assignee_ids.map((member) => {
const memberDetails = getUserDetails(member);
return (
<Avatar
key={memberDetails?.id}
name={memberDetails?.display_name ?? ""}
alt={memberDetails?.display_name ?? ""}
fallback={memberDetails?.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails?.avatar_url ?? "")}
showTooltip={false}
/>
);
})}
</AvatarGroup>
</AvatarGroupOverflow>
</>
) : (
<span className="px-1.5 text-13 text-tertiary">{t("no_assignee")}</span>

View File

@@ -14,13 +14,15 @@ import { Eye, CalendarDays } from "lucide-react";
import { EUserPermissions, EUserPermissionsLevel, IS_FAVORITE_MENU_OPEN } from "@plane/constants";
import { useLocalStorage } from "@plane/hooks";
import { useTranslation } from "@plane/i18n";
import { Avatar } from "@makeplane/propel/components/avatar";
import { TransferIcon, WorkItemsIcon, MembersPropertyIcon } from "@plane/propel/icons";
import { setPromiseToast } from "@plane/propel/toast";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import type { ICycle, TCycleGroups } from "@plane/types";
import { Avatar, AvatarGroup, FavoriteStar } from "@plane/ui";
import { FavoriteStar } from "@plane/ui";
import { getDate, getFileURL, generateQueryParams } from "@plane/utils";
// components
import { AvatarGroupOverflow } from "@/components/common/avatar-group-overflow";
import { DateRangeDropdown } from "@/components/dropdowns/date-range";
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
import { MergedDateDisplay } from "@/components/dropdowns/merged-date";
@@ -266,14 +268,19 @@ export const CycleListItemAction = observer(function CycleListItemAction(props:
<Tooltip label={`${cycleDetails.assignee_ids?.length} Members`} layout="stacked" disabled={isMobile}>
<div className="flex w-min cursor-default items-center justify-center">
{cycleDetails.assignee_ids && cycleDetails.assignee_ids?.length > 0 ? (
<AvatarGroup showTooltip={false}>
<AvatarGroupOverflow size="xs">
{cycleDetails.assignee_ids?.map((assignee_id) => {
const member = getUserDetails(assignee_id);
return (
<Avatar key={member?.id} name={member?.display_name} src={getFileURL(member?.avatar_url ?? "")} />
<Avatar
key={member?.id}
alt={member?.display_name}
fallback={member?.display_name?.[0]?.toUpperCase()}
src={getFileURL(member?.avatar_url ?? "")}
/>
);
})}
</AvatarGroup>
</AvatarGroupOverflow>
) : (
<MembersPropertyIcon className="h-4 w-4 text-tertiary" />
)}

View File

@@ -6,38 +6,46 @@
import { observer } from "mobx-react";
import type { LucideIcon } from "lucide-react";
import { Avatar } from "@makeplane/propel/components/avatar";
import type { AvatarGroupSize } from "@makeplane/propel/components/avatar-group";
import { MembersPropertyIcon } from "@plane/propel/icons";
// plane ui
import { Avatar, AvatarGroup } from "@plane/ui";
import { cn, getFileURL } from "@plane/utils";
// plane utils
// helpers
// hooks
import { AvatarGroupOverflow } from "@/components/common/avatar-group-overflow";
import { useMember } from "@/hooks/store/use-member";
type AvatarProps = {
showTooltip: boolean;
userIds: string | string[] | null;
icon?: LucideIcon;
size?: "sm" | "md" | "base" | "lg" | number;
size?: AvatarGroupSize;
};
export const ButtonAvatars = observer(function ButtonAvatars(props: AvatarProps) {
const { showTooltip, userIds, icon: Icon, size = "md" } = props;
const { userIds, icon: Icon, size = "xs" } = props;
// store hooks
const { getUserDetails } = useMember();
if (Array.isArray(userIds)) {
if (userIds.length > 0)
return (
<AvatarGroup size={size} showTooltip={!showTooltip}>
<AvatarGroupOverflow size={size}>
{userIds.map((userId) => {
const userDetails = getUserDetails(userId);
if (!userDetails) return;
return <Avatar key={userId} src={getFileURL(userDetails.avatar_url)} name={userDetails.display_name} />;
return (
<Avatar
key={userId}
src={getFileURL(userDetails.avatar_url)}
alt={userDetails.display_name}
fallback={userDetails.display_name?.[0]?.toUpperCase()}
/>
);
})}
</AvatarGroup>
</AvatarGroupOverflow>
);
} else {
if (userIds) {
@@ -45,9 +53,9 @@ export const ButtonAvatars = observer(function ButtonAvatars(props: AvatarProps)
return (
<Avatar
src={getFileURL(userDetails?.avatar_url ?? "")}
name={userDetails?.display_name}
alt={userDetails?.display_name}
fallback={userDetails?.display_name?.[0]?.toUpperCase()}
size={size}
showTooltip={!showTooltip}
/>
);
}

View File

@@ -12,11 +12,11 @@ import { createPortal } from "react-dom";
import { usePopper } from "react-popper";
import { Combobox } from "@headlessui/react";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { useTranslation } from "@plane/i18n";
import { CheckIcon, SearchIcon, SuspendedUserIcon } from "@plane/propel/icons";
import { EPillSize, EPillVariant, Pill } from "@plane/propel/pill";
import type { IUserLite } from "@plane/types";
import { Avatar } from "@plane/ui";
import { cn, getFileURL, sortByCurrentUserThenSelected } from "@plane/utils";
// hooks
import { useMember } from "@/hooks/store/use-member";
@@ -102,7 +102,12 @@ export const MemberOptions = observer(function MemberOptions(props: Props) {
{isUserSuspended(userId, workspaceSlug?.toString()) ? (
<SuspendedUserIcon className="h-3.5 w-3.5 text-placeholder" />
) : (
<Avatar name={userDetails?.display_name} src={getFileURL(userDetails?.avatar_url ?? "")} />
<Avatar
alt={userDetails?.display_name}
fallback={userDetails?.display_name?.[0]?.toUpperCase()}
src={getFileURL(userDetails?.avatar_url ?? "")}
size="xs"
/>
)}
</div>
<span

View File

@@ -8,9 +8,9 @@ import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { Link } from "react-router";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { ROLE } from "@plane/constants";
import { Popover } from "@plane/propel/popover";
import { Avatar } from "@plane/ui";
import { cn, getFileURL } from "@plane/utils";
// hooks
import { useMember } from "@/hooks/store/use-member";
@@ -63,11 +63,10 @@ export const EditorUserMention = observer(function EditorUserMention(props: Prop
<div className="flex items-center gap-3">
<div className="grid size-10 flex-shrink-0 place-items-center">
<Avatar
alt={userDetails?.display_name}
fallback={userDetails?.display_name?.[0]?.toUpperCase()}
src={getFileURL(userDetails?.avatar_url ?? "")}
name={userDetails?.display_name}
size={40}
className="text-18"
showTooltip={false}
size="xl"
/>
</div>
<div>

View File

@@ -5,11 +5,11 @@
*/
import { useRouter } from "next/navigation";
import { Avatar } from "@makeplane/propel/components/avatar";
import { Logo } from "@plane/propel/emoji-icon-picker";
import { PageIcon } from "@plane/propel/icons";
// plane import
import type { TActivityEntityData, TPageEntityData } from "@plane/types";
import { Avatar } from "@plane/ui";
import { calculateTimeAgo, getFileURL, getPageName } from "@plane/utils";
import { ListItem } from "@/components/core/list";
// hooks
@@ -65,7 +65,12 @@ export function RecentPage(props: BlockProps) {
}
quickActionElement={
<div className="flex gap-4">
<Avatar src={getFileURL(ownerDetails?.avatar_url ?? "")} name={ownerDetails?.display_name} />
<Avatar
alt={ownerDetails?.display_name}
fallback={ownerDetails?.display_name?.[0]?.toUpperCase()}
src={getFileURL(ownerDetails?.avatar_url ?? "")}
size="xs"
/>
</div>
}
parentRef={ref}

View File

@@ -7,10 +7,9 @@
import { observer } from "mobx-react";
// plane types
import { Avatar } from "@makeplane/propel/components/avatar";
import { CloseIcon } from "@plane/propel/icons";
import type { TInboxIssueFilterMemberKeys } from "@plane/types";
// plane ui
import { Avatar } from "@plane/ui";
// helpers
import { getFileURL } from "@plane/utils";
// hooks
@@ -49,10 +48,10 @@ export const InboxIssueAppliedFiltersMember = observer(function InboxIssueApplie
<div key={value} className="relative flex items-center gap-1 rounded-sm bg-layer-1 p-1 text-11">
<div className="relative flex flex-shrink-0 items-center justify-center overflow-hidden">
<Avatar
name={optionDetail.display_name}
alt={optionDetail.display_name}
fallback={optionDetail.display_name?.[0]?.toUpperCase()}
src={getFileURL(optionDetail.avatar_url)}
showTooltip={false}
size="sm"
size="2xs"
/>
</div>
<div className="truncate text-11">{optionDetail?.display_name}</div>

View File

@@ -8,9 +8,10 @@ import { useMemo, useState } from "react";
import { sortBy } from "lodash-es";
import { observer } from "mobx-react";
// plane types
import { Avatar } from "@makeplane/propel/components/avatar";
import type { TInboxIssueFilterMemberKeys } from "@plane/types";
// plane ui
import { Avatar, Loader } from "@plane/ui";
import { Loader } from "@plane/ui";
// components
import { getFileURL } from "@plane/utils";
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
@@ -86,10 +87,10 @@ export const FilterMember = observer(function FilterMember(props: Props) {
onClick={() => handleInboxIssueFilters(filterKey, handleFilterValue(member.id))}
icon={
<Avatar
name={member.display_name}
alt={member.display_name}
fallback={member.display_name?.[0]?.toUpperCase()}
src={getFileURL(member.avatar_url)}
showTooltip={false}
size="md"
size="xs"
/>
}
title={currentUser?.id === member.id ? "You" : member?.display_name}

View File

@@ -9,10 +9,11 @@ import { observer } from "mobx-react";
import Link from "next/link";
import { useSearchParams } from "next/navigation";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { PriorityIcon } from "@plane/propel/icons";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import { Row, Avatar } from "@plane/ui";
import { cn, renderFormattedDate, getFileURL } from "@plane/utils";
import { Row } from "@plane/ui";
import { cn, renderFormattedDate } from "@plane/utils";
// components
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
// hooks
@@ -123,7 +124,7 @@ export const InboxIssueListItem = observer(function InboxIssueListItem(props: In
</div>
{/* created by */}
{createdByDetails && createdByDetails.email?.includes("intake@plane.so") ? (
<Avatar src={getFileURL("")} name={"Plane"} size="md" showTooltip />
<Avatar alt="Plane" fallback="P" size="xs" />
) : createdByDetails ? (
<ButtonAvatars showTooltip={false} userIds={createdByDetails?.id} />
) : null}

View File

@@ -5,9 +5,8 @@
*/
import { observer } from "mobx-react";
import { Avatar } from "@makeplane/propel/components/avatar";
import { CloseIcon } from "@plane/propel/icons";
// plane ui
import { Avatar } from "@plane/ui";
// helpers
import { getFileURL } from "@plane/utils";
// hooks
@@ -36,10 +35,10 @@ export const AppliedMembersFilters = observer(function AppliedMembersFilters(pro
return (
<div key={memberId} className="flex items-center gap-1 rounded-sm bg-layer-1 p-1 text-11">
<Avatar
name={memberDetails.display_name}
alt={memberDetails.display_name}
fallback={memberDetails.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails.avatar_url)}
showTooltip={false}
size={"sm"}
size="2xs"
/>
<span className="normal-case">{memberDetails.display_name}</span>
{editable && (

View File

@@ -8,7 +8,8 @@ import { useMemo, useState } from "react";
import { sortBy } from "lodash-es";
import { observer } from "mobx-react";
// plane ui
import { Avatar, Loader } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
import { Loader } from "@plane/ui";
// components
import { getFileURL } from "@plane/utils";
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
@@ -78,10 +79,10 @@ export const FilterAssignees = observer(function FilterAssignees(props: Props) {
onClick={() => handleUpdate(member.id)}
icon={
<Avatar
name={member.display_name}
alt={member.display_name}
fallback={member.display_name?.[0]?.toUpperCase()}
src={getFileURL(member.avatar_url)}
showTooltip={false}
size="md"
size="xs"
/>
}
title={currentUser?.id === member.id ? "You" : member?.display_name}

View File

@@ -8,7 +8,8 @@ import { useMemo, useState } from "react";
import { sortBy } from "lodash-es";
import { observer } from "mobx-react";
// plane ui
import { Avatar, Loader } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
import { Loader } from "@plane/ui";
// components
import { getFileURL } from "@plane/utils";
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
@@ -76,7 +77,14 @@ export const FilterCreatedBy = observer(function FilterCreatedBy(props: Props) {
key={`created-by-${member.id}`}
isChecked={appliedFilters?.includes(member.id) ? true : false}
onClick={() => handleUpdate(member.id)}
icon={<Avatar name={member.display_name} src={getFileURL(member.avatar_url)} size="md" />}
icon={
<Avatar
alt={member.display_name}
fallback={member.display_name?.[0]?.toUpperCase()}
src={getFileURL(member.avatar_url)}
size="xs"
/>
}
title={currentUser?.id === member.id ? "You" : member?.display_name}
/>
);

View File

@@ -8,7 +8,8 @@ import { useMemo, useState } from "react";
import { sortBy } from "lodash-es";
import { observer } from "mobx-react";
// plane ui
import { Loader, Avatar } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
import { Loader } from "@plane/ui";
// components
import { getFileURL } from "@plane/utils";
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
@@ -78,10 +79,10 @@ export const FilterMentions = observer(function FilterMentions(props: Props) {
onClick={() => handleUpdate(member.id)}
icon={
<Avatar
name={member?.display_name}
alt={member?.display_name}
fallback={member?.display_name?.[0]?.toUpperCase()}
src={getFileURL(member?.avatar_url)}
showTooltip={false}
size={"md"}
size="xs"
/>
}
title={currentUser?.id === member.id ? "You" : member?.display_name}

View File

@@ -12,6 +12,7 @@ import scrollIntoView from "smooth-scroll-into-view-if-needed";
import type { FC } from "react";
import { CalendarDays, LayersIcon, Paperclip } from "lucide-react";
// plane types
import { Avatar } from "@makeplane/propel/components/avatar";
import { EIconSize, ISSUE_PRIORITIES, STATE_GROUPS } from "@plane/constants";
import { Logo } from "@plane/propel/emoji-icon-picker";
import type { ISvgIcons } from "@plane/propel/icons";
@@ -47,7 +48,7 @@ import type {
} from "@plane/types";
import { EIssuesStoreType } from "@plane/types";
// plane ui
import { Avatar } from "@plane/ui";
import { renderFormattedDate, getFileURL } from "@plane/utils";
// store
import { store } from "@/lib/store-context";
@@ -314,12 +315,19 @@ const getAssigneeColumns = ({ isWorkspaceLevel, projectId }: TGetColumns): IGrou
assigneeColumns.push({
id: memberId,
name: member?.display_name || "",
icon: <Avatar name={member?.display_name} src={getFileURL(member?.avatar_url ?? "")} size="md" />,
icon: (
<Avatar
alt={member?.display_name}
fallback={member?.display_name?.[0]?.toUpperCase()}
src={getFileURL(member?.avatar_url ?? "")}
size="xs"
/>
),
payload: { assignee_ids: [memberId] },
});
});
if (includeNone) {
assigneeColumns.push({ id: "None", name: "None", icon: <Avatar size="md" />, payload: {} });
assigneeColumns.push({ id: "None", name: "None", icon: <Avatar size="xs" />, payload: {} });
}
return assigneeColumns;
@@ -337,7 +345,14 @@ const getCreatedByColumns = (): IGroupByColumn[] | undefined => {
return {
id: memberId,
name: member?.display_name || "",
icon: <Avatar name={member?.display_name} src={getFileURL(member?.avatar_url ?? "")} size="md" />,
icon: (
<Avatar
alt={member?.display_name}
fallback={member?.display_name?.[0]?.toUpperCase()}
src={getFileURL(member?.avatar_url ?? "")}
size="xs"
/>
),
payload: {},
};
});

View File

@@ -5,9 +5,8 @@
*/
import { observer } from "mobx-react";
import { Avatar } from "@makeplane/propel/components/avatar";
import { CloseIcon } from "@plane/propel/icons";
// plane ui
import { Avatar } from "@plane/ui";
// helpers
import { getFileURL } from "@plane/utils";
// hooks
@@ -36,10 +35,10 @@ export const AppliedMembersFilters = observer(function AppliedMembersFilters(pro
return (
<div key={memberId} className="flex items-center gap-1 rounded-sm bg-layer-1 p-1 text-11">
<Avatar
name={memberDetails.display_name}
alt={memberDetails.display_name}
fallback={memberDetails.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails.avatar_url)}
showTooltip={false}
size={"sm"}
size="2xs"
/>
<span className="normal-case">{memberDetails.display_name}</span>
{editable && (

View File

@@ -8,7 +8,8 @@ import { useMemo, useState } from "react";
import { sortBy } from "lodash-es";
import { observer } from "mobx-react";
// plane ui
import { Avatar, Loader } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
import { Loader } from "@plane/ui";
// components
import { getFileURL } from "@plane/utils";
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
@@ -78,10 +79,10 @@ export const FilterLead = observer(function FilterLead(props: Props) {
onClick={() => handleUpdate(member.id)}
icon={
<Avatar
name={member.display_name}
alt={member.display_name}
fallback={member.display_name?.[0]?.toUpperCase()}
src={getFileURL(member.avatar_url)}
showTooltip={false}
size="md"
size="xs"
/>
}
title={currentUser?.id === member.id ? "You" : member?.display_name}

View File

@@ -8,7 +8,8 @@ import { useMemo, useState } from "react";
import { sortBy } from "lodash-es";
import { observer } from "mobx-react";
// plane ui
import { Avatar, Loader } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
import { Loader } from "@plane/ui";
// components
import { getFileURL } from "@plane/utils";
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
@@ -78,10 +79,10 @@ export const FilterMembers = observer(function FilterMembers(props: Props) {
onClick={() => handleUpdate(member.id)}
icon={
<Avatar
name={member.display_name}
alt={member.display_name}
fallback={member.display_name?.[0]?.toUpperCase()}
src={getFileURL(member.avatar_url)}
showTooltip={false}
size="md"
size="xs"
/>
}
title={currentUser?.id === member.id ? "You" : member?.display_name}

View File

@@ -7,9 +7,10 @@
import { observer } from "mobx-react";
import { Earth, Info, Minus } from "lucide-react";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { LockIcon } from "@plane/propel/icons";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import { Avatar, FavoriteStar } from "@plane/ui";
import { FavoriteStar } from "@plane/ui";
import { renderFormattedDate, getFileURL } from "@plane/utils";
// hooks
import { useMember } from "@/hooks/store/use-member";
@@ -44,7 +45,12 @@ export const BlockItemAction = observer(function BlockItemAction(props: Props) {
{/* page details */}
<div className="cursor-default">
<Tooltip label={`Owned by: ${ownerDetails?.display_name ?? ""}`} layout="stacked">
<Avatar src={getFileURL(ownerDetails?.avatar_url ?? "")} name={ownerDetails?.display_name} />
<Avatar
alt={ownerDetails?.display_name}
fallback={ownerDetails?.display_name?.[0]?.toUpperCase()}
src={getFileURL(ownerDetails?.avatar_url ?? "")}
size="xs"
/>
</Tooltip>
</div>
<div className="cursor-default text-tertiary">

View File

@@ -8,8 +8,8 @@ import { observer } from "mobx-react";
import Link from "next/link";
import { useParams } from "next/navigation";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { useTranslation } from "@plane/i18n";
import { Avatar } from "@plane/ui";
import { calculateTimeAgoShort, getFileURL, renderFormattedDate } from "@plane/utils";
// hooks
import { useMember } from "@/hooks/store/use-member";
@@ -40,10 +40,10 @@ export const PageNavigationPaneInfoTabActorsInfo = observer(function PageNavigat
<div className="mt-2 flex items-center justify-between gap-2 text-13 font-medium">
<Link href={`/${workspaceSlug?.toString()}/profile/${page.updated_by}`} className="flex items-center gap-1">
<Avatar
alt={editorInformation?.display_name}
fallback={editorInformation?.display_name?.[0]?.toUpperCase()}
src={getFileURL(editorInformation?.avatar_url ?? "")}
name={editorInformation?.display_name}
className="flex-shrink-0"
size="sm"
size="2xs"
/>
<span>{editorInformation?.display_name ?? t("common.deactivated_user")}</span>
</Link>
@@ -57,10 +57,10 @@ export const PageNavigationPaneInfoTabActorsInfo = observer(function PageNavigat
<div className="mt-2 flex items-center justify-between gap-2 text-13 font-medium">
<Link href={`/${workspaceSlug?.toString()}/profile/${page.created_by}`} className="flex items-center gap-1">
<Avatar
alt={creatorInformation?.display_name}
fallback={creatorInformation?.display_name?.[0]?.toUpperCase()}
src={getFileURL(creatorInformation?.avatar_url ?? "")}
name={creatorInformation?.display_name}
className="flex-shrink-0"
size="sm"
size="2xs"
/>
<span>{creatorInformation?.display_name ?? t("common.deactivated_user")}</span>
</Link>

View File

@@ -10,9 +10,9 @@ import Link from "next/link";
import { useSearchParams } from "next/navigation";
import useSWR from "swr";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { useTranslation } from "@plane/i18n";
import type { TPageVersion } from "@plane/types";
import { Avatar } from "@plane/ui";
import { cn, getFileURL, renderFormattedDate, renderFormattedTime } from "@plane/utils";
// components
import type { TPageRootHandlers } from "@/components/pages/editor/page-root";
@@ -62,10 +62,10 @@ const VersionHistoryItem = observer(function VersionHistoryItem(props: VersionHi
</p>
<p className="mt-1 flex items-center gap-1">
<Avatar
size="sm"
alt={versionCreator?.display_name}
fallback={versionCreator?.display_name?.[0]?.toUpperCase()}
size="2xs"
src={getFileURL(versionCreator?.avatar_url ?? "")}
name={versionCreator?.display_name}
className="shrink-0"
/>
<span>{versionCreator?.display_name ?? t("common.deactivated_user")}</span>
</p>

View File

@@ -7,7 +7,8 @@
import { Command } from "cmdk";
import { observer } from "mobx-react";
// plane imports
import { Avatar } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
import { getFileURL } from "@plane/utils";
// hooks
import { useMember } from "@/hooks/store/use-member";
@@ -37,10 +38,10 @@ export const PowerKMembersMenu = observer(function PowerKMembersMenu(props: Prop
key={memberId}
iconNode={
<Avatar
name={memberDetails?.display_name}
alt={memberDetails?.display_name}
fallback={memberDetails?.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails?.avatar_url ?? "")}
showTooltip={false}
className="shrink-0"
size="xs"
/>
}
isSelected={value.includes(memberId)}

View File

@@ -5,9 +5,8 @@
*/
import { observer } from "mobx-react";
import { Avatar } from "@makeplane/propel/components/avatar";
import { CloseIcon } from "@plane/propel/icons";
// ui
import { Avatar } from "@plane/ui";
// helpers
import { getFileURL } from "@plane/utils";
// types
@@ -36,10 +35,10 @@ export const AppliedMembersFilters = observer(function AppliedMembersFilters(pro
return (
<div key={memberId} className="flex items-center gap-1 rounded-sm bg-layer-1 px-1.5 py-1 text-11">
<Avatar
name={memberDetails.display_name}
alt={memberDetails.display_name}
fallback={memberDetails.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails.avatar_url)}
showTooltip={false}
size={"sm"}
size="2xs"
/>
<span className="normal-case">{memberDetails.display_name}</span>
{editable && (

View File

@@ -12,6 +12,7 @@ import { ArchiveRestoreIcon, Settings, UserPlus } from "lucide-react";
// plane imports
import { EUserPermissions, EUserPermissionsLevel, IS_FAVORITE_MENU_OPEN } from "@plane/constants";
import { useLocalStorage } from "@plane/hooks";
import { Avatar } from "@makeplane/propel/components/avatar";
import { Button } from "@plane/propel/button";
import { Logo } from "@plane/propel/emoji-icon-picker";
import { LinkIcon, LockIcon, NewTabIcon, TrashIcon, CheckIcon } from "@plane/propel/icons";
@@ -19,7 +20,7 @@ import { setPromiseToast, setToast, TOAST_TYPE } from "@plane/propel/toast";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import type { IProject } from "@plane/types";
import type { TContextMenuItem } from "@plane/ui";
import { Avatar, AvatarGroup, ContextMenu, FavoriteStar } from "@plane/ui";
import { ContextMenu, FavoriteStar } from "@plane/ui";
import { copyUrlToClipboard, cn, getFileURL, renderFormattedDate } from "@plane/utils";
// components
// hooks
@@ -29,6 +30,7 @@ import { useUserPermissions } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
import { usePlatformOS } from "@/hooks/use-platform-os";
// local imports
import { AvatarGroupOverflow } from "@/components/common/avatar-group-overflow";
import { CoverImage } from "@/components/common/cover-image";
import { DeleteProjectModal } from "./delete-project-modal";
import { JoinProjectModal } from "./join-project-modal";
@@ -285,15 +287,20 @@ export const ProjectCard = observer(function ProjectCard(props: Props) {
>
{projectMembersIds && projectMembersIds.length > 0 ? (
<div className="flex cursor-pointer items-center gap-2 text-secondary">
<AvatarGroup showTooltip={false}>
<AvatarGroupOverflow size="xs">
{projectMembersIds.map((memberId) => {
const member = getUserDetails(memberId);
if (!member) return null;
return (
<Avatar key={member.id} name={member.display_name} src={getFileURL(member.avatar_url)} />
<Avatar
key={member.id}
alt={member.display_name}
fallback={member.display_name?.[0]?.toUpperCase()}
src={getFileURL(member.avatar_url)}
/>
);
})}
</AvatarGroup>
</AvatarGroupOverflow>
</div>
) : (
<span className="text-13 text-placeholder italic">No Member Yet</span>

View File

@@ -8,7 +8,8 @@ import { useMemo, useState } from "react";
import { sortBy } from "lodash-es";
import { observer } from "mobx-react";
// plane ui
import { Avatar, Loader } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
import { Loader } from "@plane/ui";
// components
import { getFileURL } from "@plane/utils";
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
@@ -78,10 +79,10 @@ export const FilterLead = observer(function FilterLead(props: Props) {
onClick={() => handleUpdate(member.id)}
icon={
<Avatar
name={member.display_name}
alt={member.display_name}
fallback={member.display_name?.[0]?.toUpperCase()}
src={getFileURL(member.avatar_url)}
showTooltip={false}
size="md"
size="xs"
/>
}
title={currentUser?.id === member.id ? "You" : member?.display_name}

View File

@@ -8,7 +8,8 @@ import { useMemo, useState } from "react";
import { sortBy } from "lodash-es";
import { observer } from "mobx-react";
// plane ui
import { Avatar, Loader } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
import { Loader } from "@plane/ui";
// components
import { getFileURL } from "@plane/utils";
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
@@ -78,10 +79,10 @@ export const FilterMembers = observer(function FilterMembers(props: Props) {
onClick={() => handleUpdate(member.id)}
icon={
<Avatar
name={member.display_name}
alt={member.display_name}
fallback={member.display_name?.[0]?.toUpperCase()}
src={getFileURL(member.avatar_url)}
showTooltip={false}
size="md"
size="xs"
/>
}
title={currentUser?.id === member.id ? "You" : member?.display_name}

View File

@@ -8,9 +8,10 @@ import React from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { Ban } from "lucide-react";
import { Avatar } from "@makeplane/propel/components/avatar";
import { EUserProjectRoles } from "@plane/types";
// plane ui
import { Avatar, CustomSearchSelect } from "@plane/ui";
import { CustomSearchSelect } from "@plane/ui";
// helpers
import { getFileURL } from "@plane/utils";
// hooks
@@ -44,7 +45,12 @@ export const MemberSelect = observer(function MemberSelect(props: Props) {
query: `${memberDetails?.member.display_name}`,
content: (
<div className="flex items-center gap-2">
<Avatar name={memberDetails?.member.display_name} src={getFileURL(memberDetails?.member.avatar_url)} />
<Avatar
alt={memberDetails?.member.display_name}
fallback={memberDetails?.member.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails?.member.avatar_url)}
size="xs"
/>
{memberDetails?.member.display_name}
</div>
),
@@ -65,7 +71,12 @@ export const MemberSelect = observer(function MemberSelect(props: Props) {
label={
<div className="flex h-3.5 items-center gap-2">
{selectedOption && (
<Avatar name={selectedOption.member?.display_name} src={getFileURL(selectedOption.member?.avatar_url)} />
<Avatar
alt={selectedOption.member?.display_name}
fallback={selectedOption.member?.display_name?.[0]?.toUpperCase()}
src={getFileURL(selectedOption.member?.avatar_url)}
size="xs"
/>
)}
{selectedOption ? (
selectedOption.member?.display_name

View File

@@ -8,12 +8,13 @@ import React, { useEffect } from "react";
import { observer } from "mobx-react";
import { useForm, Controller, useFieldArray } from "react-hook-form";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { ROLE, EUserPermissions } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { PlusIcon, CloseIcon, ChevronDownIcon } from "@plane/propel/icons";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Avatar, CustomSelect, CustomSearchSelect, EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
import { CustomSelect, CustomSearchSelect, EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
// helpers
import { getFileURL } from "@plane/utils";
// hooks
@@ -140,7 +141,12 @@ export const SendProjectInvitationModal = observer(function SendProjectInvitatio
content: (
<div className="flex w-full items-center gap-2">
<div className="shrink-0 pt-0.5">
<Avatar name={memberDetails?.member.display_name} src={getFileURL(memberDetails?.member.avatar_url)} />
<Avatar
alt={memberDetails?.member.display_name}
fallback={memberDetails?.member.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails?.member.avatar_url)}
size="xs"
/>
</div>
<div className="truncate">
{memberDetails?.member.display_name} (
@@ -200,8 +206,10 @@ export const SendProjectInvitationModal = observer(function SendProjectInvitatio
{value && value !== "" ? (
<div className="flex items-center gap-2">
<Avatar
name={selectedMember?.member.display_name}
alt={selectedMember?.member.display_name}
fallback={selectedMember?.member.display_name?.[0]?.toUpperCase()}
src={getFileURL(selectedMember?.member.avatar_url ?? "")}
size="xs"
/>
{selectedMember?.member.display_name}
</div>

View File

@@ -6,10 +6,10 @@
import { observer } from "mobx-react";
// plane imports
import { Avatar } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
import { getFileURL } from "@plane/utils";
// hooks
import { useUser } from "@/hooks/store/user";
import { getFileURL } from "@plane/utils";
export const ProfileSettingsSidebarHeader = observer(function ProfileSettingsSidebarHeader() {
// store hooks
@@ -19,11 +19,10 @@ export const ProfileSettingsSidebarHeader = observer(function ProfileSettingsSid
<div className="flex shrink-0 items-center gap-2">
<div className="shrink-0">
<Avatar
alt={currentUser?.display_name}
fallback={currentUser?.display_name?.[0]?.toUpperCase()}
src={getFileURL(currentUser?.avatar_url ?? "")}
name={currentUser?.display_name}
size={32}
shape="circle"
className="text-16"
size="lg"
/>
</div>
<div className="truncate">

View File

@@ -8,7 +8,8 @@ import { useState } from "react";
import { observer } from "mobx-react";
import { Clock } from "lucide-react";
// plane imports
import { Avatar, Row } from "@plane/ui";
import { Avatar } from "@makeplane/propel/components/avatar";
import { Row } from "@plane/ui";
import { cn, calculateTimeAgo, renderFormattedDate, renderFormattedTime, getFileURL } from "@plane/utils";
// hooks
import { useWorkspaceNotifications } from "@/hooks/store/notifications";
@@ -87,11 +88,11 @@ export const NotificationItem = observer(function NotificationItem(props: TNotif
<div className="relative flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-layer-1">
{notificationTriggeredBy && (
<Avatar
name={notificationTriggeredBy.display_name || notificationTriggeredBy?.first_name}
alt={notificationTriggeredBy.display_name || notificationTriggeredBy?.first_name}
fallback={(notificationTriggeredBy.display_name ||
notificationTriggeredBy?.first_name)?.[0]?.toUpperCase()}
src={getFileURL(notificationTriggeredBy.avatar_url)}
size={42}
shape="circle"
className="bg-layer-1 text-body-sm-medium"
size="xl"
/>
)}
</div>

View File

@@ -9,10 +9,11 @@ import { observer } from "mobx-react";
import { useRouter } from "next/navigation";
import { LogOut, Settings, Settings2 } from "lucide-react";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { GOD_MODE_URL } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Avatar, CustomMenu } from "@plane/ui";
import { CustomMenu } from "@plane/ui";
import { getFileURL } from "@plane/utils";
// components
import { CoverImage } from "@/components/common/cover-image";
@@ -62,10 +63,10 @@ export const UserMenuRoot = observer(function UserMenuRoot() {
item={{
icon: (
<Avatar
name={currentUser?.display_name}
alt={currentUser?.display_name}
fallback={currentUser?.display_name?.[0]?.toUpperCase()}
src={getFileURL(currentUser?.avatar_url ?? "")}
size={20}
shape="circle"
size="xs"
/>
),
isActive: isUserMenuOpen,
@@ -91,11 +92,10 @@ export const UserMenuRoot = observer(function UserMenuRoot() {
<div className="flex flex-col items-center gap-y-2">
<div>
<Avatar
name={currentUser?.display_name}
alt={currentUser?.display_name}
fallback={currentUser?.display_name?.[0]?.toUpperCase()}
src={getFileURL(currentUser?.avatar_url ?? "")}
size={40}
shape="circle"
className="text-18 font-medium"
size="xl"
/>
</div>
<div className="text-center">

View File

@@ -6,11 +6,10 @@
import { useCallback } from "react";
// plane editor
import { Avatar } from "@makeplane/propel/components/avatar";
import type { TMentionSection, TMentionSuggestion } from "@plane/editor";
// plane types
import type { TSearchEntities, TSearchEntityRequestPayload, TSearchResponse, TUserSearchResponse } from "@plane/types";
// plane ui
import { Avatar } from "@plane/ui";
// helpers
import { getFileURL } from "@plane/utils";
// plane web hooks
@@ -47,9 +46,10 @@ export const useEditorMention = (args: TArgs) => {
const items: TMentionSuggestion[] = (response as TUserSearchResponse[]).map((user) => ({
icon: (
<Avatar
className="flex-shrink-0"
alt={user.member__display_name}
fallback={user.member__display_name?.[0]?.toUpperCase()}
src={getFileURL(user.member__avatar_url)}
name={user.member__display_name}
size="xs"
/>
),
id: user.member__id,

View File

@@ -7,6 +7,7 @@
import { useCallback, useMemo } from "react";
import { AtSign, Briefcase } from "lucide-react";
// plane imports
import { Avatar } from "@makeplane/propel/components/avatar";
import { Logo } from "@plane/propel/emoji-icon-picker";
import {
CalendarLayoutIcon,
@@ -33,7 +34,7 @@ import type {
IProject,
TWorkItemFilterProperty,
} from "@plane/types";
import { Avatar } from "@plane/ui";
import {
getAssigneeFilterConfig,
getCreatedAtFilterConfig,
@@ -222,10 +223,10 @@ export const useWorkItemFiltersConfig = (props: TUseWorkItemFiltersConfigProps):
members: members ?? [],
getOptionIcon: (memberDetails) => (
<Avatar
name={memberDetails.display_name}
alt={memberDetails.display_name}
fallback={memberDetails.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails.avatar_url)}
showTooltip={false}
size="sm"
size="2xs"
/>
),
...operatorConfigs,
@@ -242,10 +243,10 @@ export const useWorkItemFiltersConfig = (props: TUseWorkItemFiltersConfigProps):
members: members ?? [],
getOptionIcon: (memberDetails) => (
<Avatar
name={memberDetails.display_name}
alt={memberDetails.display_name}
fallback={memberDetails.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails.avatar_url)}
showTooltip={false}
size="sm"
size="2xs"
/>
),
...operatorConfigs,
@@ -262,10 +263,10 @@ export const useWorkItemFiltersConfig = (props: TUseWorkItemFiltersConfigProps):
members: members ?? [],
getOptionIcon: (memberDetails) => (
<Avatar
name={memberDetails.display_name}
alt={memberDetails.display_name}
fallback={memberDetails.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails.avatar_url)}
showTooltip={false}
size="sm"
size="2xs"
/>
),
...operatorConfigs,
@@ -282,10 +283,10 @@ export const useWorkItemFiltersConfig = (props: TUseWorkItemFiltersConfigProps):
members: members ?? [],
getOptionIcon: (memberDetails) => (
<Avatar
name={memberDetails.display_name}
alt={memberDetails.display_name}
fallback={memberDetails.display_name?.[0]?.toUpperCase()}
src={getFileURL(memberDetails.avatar_url)}
showTooltip={false}
size="sm"
size="2xs"
/>
),
...operatorConfigs,