Update Profile Picture Default Stylings (#20589)

* refactor code for profile picture

* fix test

* minor fix
This commit is contained in:
Shrushti Polekar 2025-04-03 21:06:40 +05:30 committed by GitHub
parent 6b3fd3bf80
commit a23f2f9d18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 155 additions and 252 deletions

View File

@ -470,11 +470,15 @@ test.describe('User Profile Feed Interactions', () => {
'/api/v1/feed?type=Conversation&filterType=OWNER_OR_FOLLOWS&userId=*' '/api/v1/feed?type=Conversation&filterType=OWNER_OR_FOLLOWS&userId=*'
); );
await adminPage const avatar = adminPage
.locator('[data-testid="message-container"]') .locator('[data-testid="message-container"]')
.first() .first()
.locator('[data-testid="profile-avatar"]') .locator('[data-testid="profile-avatar"]');
.click();
await avatar.hover();
await adminPage.waitForSelector('.ant-popover-card');
await adminPage.getByTestId('user-name').nth(1).click();
await userDetailsResponse; await userDetailsResponse;
await userFeedResponse; await userFeedResponse;
const response = await userDetailsResponse; const response = await userDetailsResponse;

View File

@ -38,7 +38,7 @@ import { getUserPath } from '../../../utils/RouterUtils';
import searchClassBase from '../../../utils/SearchClassBase'; import searchClassBase from '../../../utils/SearchClassBase';
import EntityPopOverCard from '../../common/PopOverCard/EntityPopOverCard'; import EntityPopOverCard from '../../common/PopOverCard/EntityPopOverCard';
import UserPopOverCard from '../../common/PopOverCard/UserPopOverCard'; import UserPopOverCard from '../../common/PopOverCard/UserPopOverCard';
import ProfilePictureNew from '../../common/ProfilePicture/ProfilePictureNew'; import ProfilePicture from '../../common/ProfilePicture/ProfilePicture';
import FeedCardBodyNew from '../ActivityFeedCard/FeedCardBody/FeedCardBodyNew'; import FeedCardBodyNew from '../ActivityFeedCard/FeedCardBody/FeedCardBodyNew';
import FeedCardFooterNew from '../ActivityFeedCardV2/FeedCardFooter/FeedCardFooterNew'; import FeedCardFooterNew from '../ActivityFeedCardV2/FeedCardFooter/FeedCardFooterNew';
import ActivityFeedEditorNew from '../ActivityFeedEditor/ActivityFeedEditorNew'; import ActivityFeedEditorNew from '../ActivityFeedEditor/ActivityFeedEditorNew';
@ -203,12 +203,15 @@ const ActivityFeedCardNew = ({
'items-start': 'items-start':
showThread && feed.entityRef?.type === EntityType.CONTAINER, showThread && feed.entityRef?.type === EntityType.CONTAINER,
})}> })}>
<ProfilePictureNew <UserPopOverCard userName={feed.createdBy ?? ''}>
avatarType="outlined" <div className="d-flex items-center">
key={feed.id} <ProfilePicture
name={feed.createdBy ?? ''} key={feed.id}
size={showThread ? 40 : 32} name={feed.createdBy ?? ''}
/> width={showThread ? '40' : '32'}
/>
</div>
</UserPopOverCard>
<Space className="d-flex flex-col align-start gap-2" size={0}> <Space className="d-flex flex-col align-start gap-2" size={0}>
<Space <Space
className={classNames('d-flex align-center gap-2', { className={classNames('d-flex align-center gap-2', {
@ -307,12 +310,15 @@ const ActivityFeedCardNew = ({
) : ( ) : (
<div className="d-flex gap-2"> <div className="d-flex gap-2">
<div> <div>
<ProfilePictureNew <UserPopOverCard userName={getEntityName(currentUser)}>
avatarType="outlined" <div className="d-flex items-center">
key={feed.id} <ProfilePicture
name={getEntityName(currentUser)} key={feed.id}
size={32} name={getEntityName(currentUser)}
/> width="32"
/>
</div>
</UserPopOverCard>
</div> </div>
<Input <Input

View File

@ -34,7 +34,7 @@ import {
} from '../../../utils/FeedUtils'; } from '../../../utils/FeedUtils';
import { getUserPath } from '../../../utils/RouterUtils'; import { getUserPath } from '../../../utils/RouterUtils';
import UserPopOverCard from '../../common/PopOverCard/UserPopOverCard'; import UserPopOverCard from '../../common/PopOverCard/UserPopOverCard';
import ProfilePictureNew from '../../common/ProfilePicture/ProfilePictureNew'; import ProfilePicture from '../../common/ProfilePicture/ProfilePicture';
import RichTextEditorPreviewerV1 from '../../common/RichTextEditor/RichTextEditorPreviewerV1'; import RichTextEditorPreviewerV1 from '../../common/RichTextEditor/RichTextEditorPreviewerV1';
import FeedCardFooterNew from '../ActivityFeedCardV2/FeedCardFooter/FeedCardFooterNew'; import FeedCardFooterNew from '../ActivityFeedCardV2/FeedCardFooter/FeedCardFooterNew';
import ActivityFeedEditor from '../ActivityFeedEditor/ActivityFeedEditorNew'; import ActivityFeedEditor from '../ActivityFeedEditor/ActivityFeedEditorNew';
@ -137,12 +137,11 @@ const CommentCard = ({
onMouseEnter={() => setIsHovered(true)} onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}> onMouseLeave={() => setIsHovered(false)}>
<div className="profile-picture m-r-xs"> <div className="profile-picture m-r-xs">
<ProfilePictureNew <UserPopOverCard userName={feed.updatedBy ?? ''}>
avatarType="outlined" <div className="d-flex items-center">
key={feed.id} <ProfilePicture key={feed.id} name={feed.updatedBy!} width="32" />
name={feed.updatedBy!} </div>
size={32} </UserPopOverCard>
/>
</div> </div>
<div className="w-full"> <div className="w-full">
<div className="d-flex items-center gap-2 flex-wrap"> <div className="d-flex items-center gap-2 flex-wrap">

View File

@ -26,7 +26,8 @@ import { Owner } from '../../../../../generated/entity/feed/owner';
import { Thread } from '../../../../../generated/entity/feed/thread'; import { Thread } from '../../../../../generated/entity/feed/thread';
import { EntityReference } from '../../../../../generated/entity/type'; import { EntityReference } from '../../../../../generated/entity/type';
import { UserAvatarGroup } from '../../../../common/OwnerLabel/UserAvatarGroup.component'; import { UserAvatarGroup } from '../../../../common/OwnerLabel/UserAvatarGroup.component';
import ProfilePictureNew from '../../../../common/ProfilePicture/ProfilePictureNew'; import UserPopOverCard from '../../../../common/PopOverCard/UserPopOverCard';
import ProfilePicture from '../../../../common/ProfilePicture/ProfilePicture';
interface OwnersFeedProps { interface OwnersFeedProps {
feed: Thread; feed: Thread;
@ -69,21 +70,21 @@ function OwnersFeed({
{updatedOwner.length <= maxVisibleOwners ? ( {updatedOwner.length <= maxVisibleOwners ? (
<Row wrap align="middle"> <Row wrap align="middle">
{updatedOwner.map((owner: EntityReference) => ( {updatedOwner.map((owner: EntityReference) => (
<div <UserPopOverCard key={owner.id} userName={owner.name ?? ''}>
className={`owner-chip d-flex items-center ${ <div
showThread && 'bg-white' className={`owner-chip d-flex items-center ${
}`} showThread && 'bg-white'
key={owner.id}> }`}>
<ProfilePictureNew <ProfilePicture
displayName={owner.displayName} displayName={owner.displayName}
name={owner.name ?? ''} name={owner.name ?? ''}
size={24} width="24"
width="24" />
/> <Typography.Text className="owner-chip-text">
<Typography.Text className="owner-chip-text"> {owner.displayName}
{owner.displayName} </Typography.Text>
</Typography.Text> </div>
</div> </UserPopOverCard>
))} ))}
</Row> </Row>
) : ( ) : (
@ -111,21 +112,21 @@ function OwnersFeed({
{previousOwner.length <= maxVisibleOwners ? ( {previousOwner.length <= maxVisibleOwners ? (
<Row align="middle"> <Row align="middle">
{previousOwner.map((owner: EntityReference) => ( {previousOwner.map((owner: EntityReference) => (
<div <UserPopOverCard key={owner.id} userName={owner.name ?? ''}>
className={`owner-chip d-flex items-center ${ <div
showThread && 'bg-white' className={`owner-chip d-flex items-center ${
}`} showThread && 'bg-white'
key={owner.id}> }`}>
<ProfilePictureNew <ProfilePicture
displayName={owner.displayName} displayName={owner.displayName}
name={owner.name ?? ''} name={owner.name ?? ''}
size={24} width="24"
width="24" />
/> <Typography.Text className="owner-chip-text">
<Typography.Text className="owner-chip-text"> {owner.displayName}
{owner.displayName} </Typography.Text>
</Typography.Text> </div>
</div> </UserPopOverCard>
))} ))}
</Row> </Row>
) : ( ) : (

View File

@ -18,7 +18,8 @@ import React, { useCallback, useMemo } from 'react';
import { ReactComponent as ThreadIcon } from '../../../../assets/svg/ic-reply-2.svg'; import { ReactComponent as ThreadIcon } from '../../../../assets/svg/ic-reply-2.svg';
import { ReactionOperation } from '../../../../enums/reactions.enum'; import { ReactionOperation } from '../../../../enums/reactions.enum';
import { ReactionType } from '../../../../generated/type/reaction'; import { ReactionType } from '../../../../generated/type/reaction';
import ProfilePictureNew from '../../../common/ProfilePicture/ProfilePictureNew'; import UserPopOverCard from '../../../common/PopOverCard/UserPopOverCard';
import ProfilePicture from '../../../common/ProfilePicture/ProfilePicture';
import { useActivityFeedProvider } from '../../ActivityFeedProvider/ActivityFeedProvider'; import { useActivityFeedProvider } from '../../ActivityFeedProvider/ActivityFeedProvider';
import Reactions from '../../Reactions/Reactions'; import Reactions from '../../Reactions/Reactions';
import { FeedCardFooterProps } from './FeedCardFooter.interface'; import { FeedCardFooterProps } from './FeedCardFooter.interface';
@ -85,11 +86,11 @@ function FeedCardFooterNew({
}} }}
type="text" type="text"
onClick={isForFeedTab ? showReplies : undefined}> onClick={isForFeedTab ? showReplies : undefined}>
<ProfilePictureNew <UserPopOverCard userName={user}>
avatarType="outlined" <div className="d-flex items-center">
name={user} <ProfilePicture name={user} width="20" />
size={20} </div>
/> </UserPopOverCard>
</Button> </Button>
))} ))}
</Avatar.Group> </Avatar.Group>

View File

@ -361,7 +361,7 @@ const TaskFeedCard = ({
}`}> }`}>
<AssigneesIcon height={20} width={20} /> <AssigneesIcon height={20} width={20} />
<UserAvatarGroup <UserAvatarGroup
avatarSize={24} avatarSize="24"
className="p-t-05" className="p-t-05"
owners={feed?.task?.assignees} owners={feed?.task?.assignees}
/> />

View File

@ -126,7 +126,8 @@ import {
} from '../../../../utils/EntityUtils'; } from '../../../../utils/EntityUtils';
import { UserAvatarGroup } from '../../../common/OwnerLabel/UserAvatarGroup.component'; import { UserAvatarGroup } from '../../../common/OwnerLabel/UserAvatarGroup.component';
import EntityPopOverCard from '../../../common/PopOverCard/EntityPopOverCard'; import EntityPopOverCard from '../../../common/PopOverCard/EntityPopOverCard';
import ProfilePictureNew from '../../../common/ProfilePicture/ProfilePictureNew'; import UserPopOverCard from '../../../common/PopOverCard/UserPopOverCard';
import ProfilePicture from '../../../common/ProfilePicture/ProfilePicture';
import TaskTabIncidentManagerHeaderNew from '../TaskTabIncidentManagerHeader/TasktabIncidentManagerHeaderNew'; import TaskTabIncidentManagerHeaderNew from '../TaskTabIncidentManagerHeader/TasktabIncidentManagerHeaderNew';
import './task-tab-new.less'; import './task-tab-new.less';
import { TaskTabProps } from './TaskTab.interface'; import { TaskTabProps } from './TaskTab.interface';
@ -875,11 +876,11 @@ export const TaskTabNew = ({
</Typography.Text> </Typography.Text>
</Col> </Col>
<Col className="flex items-center gap-2" span={16}> <Col className="flex items-center gap-2" span={16}>
<ProfilePictureNew <UserPopOverCard userName={taskThread.createdBy ?? ''}>
avatarType="outlined" <div className="d-flex items-center">
name={taskThread.createdBy ?? ''} <ProfilePicture name={taskThread.createdBy ?? ''} width="24" />
width="24" </div>
/> </UserPopOverCard>
<Typography.Text>{taskThread.createdBy}</Typography.Text> <Typography.Text>{taskThread.createdBy}</Typography.Text>
</Col> </Col>
@ -939,18 +940,25 @@ export const TaskTabNew = ({
<Col className="flex items-center gap-2" span={16}> <Col className="flex items-center gap-2" span={16}>
{taskThread?.task?.assignees?.length === 1 ? ( {taskThread?.task?.assignees?.length === 1 ? (
<div className="d-flex items-center gap-2"> <div className="d-flex items-center gap-2">
<ProfilePictureNew <UserPopOverCard
avatarType="outlined" userName={
name={taskThread?.task?.assignees[0].displayName ?? ''} taskThread?.task?.assignees[0].displayName ?? ''
width="24" }>
/> <div className="d-flex items-center">
<ProfilePicture
name={
taskThread?.task?.assignees[0].displayName ?? ''
}
width="24"
/>
</div>
</UserPopOverCard>
<Typography.Text className="text-grey-body"> <Typography.Text className="text-grey-body">
{taskThread?.task?.assignees[0].displayName} {taskThread?.task?.assignees[0].displayName}
</Typography.Text> </Typography.Text>
</div> </div>
) : ( ) : (
<UserAvatarGroup <UserAvatarGroup
avatarSize={24}
className="p-t-05" className="p-t-05"
owners={taskThread?.task?.assignees} owners={taskThread?.task?.assignees}
/> />
@ -1114,12 +1122,15 @@ export const TaskTabNew = ({
taskThread?.task?.status === ThreadTaskStatus.Open && ( taskThread?.task?.status === ThreadTaskStatus.Open && (
<div className="d-flex gap-2"> <div className="d-flex gap-2">
<div className="profile-picture"> <div className="profile-picture">
<ProfilePictureNew <UserPopOverCard userName={getEntityName(currentUser)}>
avatarType="outlined" <div className="d-flex items-center">
key={taskThread.id} <ProfilePicture
name={getEntityName(currentUser)} key={taskThread.id}
size={32} name={getEntityName(currentUser)}
/> width="32"
/>
</div>
</UserPopOverCard>
</div> </div>
<Input <Input

View File

@ -28,7 +28,8 @@ import { getEntityName } from '../../../../utils/EntityUtils';
import { useActivityFeedProvider } from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider'; import { useActivityFeedProvider } from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
import { UserAvatarGroup } from '../../../common/OwnerLabel/UserAvatarGroup.component'; import { UserAvatarGroup } from '../../../common/OwnerLabel/UserAvatarGroup.component';
import ProfilePictureNew from '../../../common/ProfilePicture/ProfilePictureNew'; import UserPopOverCard from '../../../common/PopOverCard/UserPopOverCard';
import ProfilePicture from '../../../common/ProfilePicture/ProfilePicture';
import RichTextEditorPreviewerV1 from '../../../common/RichTextEditor/RichTextEditorPreviewerV1'; import RichTextEditorPreviewerV1 from '../../../common/RichTextEditor/RichTextEditorPreviewerV1';
import Severity from '../../../DataQuality/IncidentManager/Severity/Severity.component'; import Severity from '../../../DataQuality/IncidentManager/Severity/Severity.component';
import './task-tab-incident-manager-header.style.less'; import './task-tab-incident-manager-header.style.less';
@ -128,11 +129,11 @@ const TaskTabIncidentManagerHeaderNew = ({ thread }: { thread: Thread }) => {
</Typography.Text> </Typography.Text>
</Col> </Col>
<Col className="flex items-center gap-2" span={16}> <Col className="flex items-center gap-2" span={16}>
<ProfilePictureNew <UserPopOverCard userName={thread.createdBy ?? ''}>
avatarType="outlined" <div className="d-flex items-center">
name={thread.createdBy ?? ''} <ProfilePicture name={thread.createdBy ?? ''} width="24" />
width="24" </div>
/> </UserPopOverCard>
<Typography.Text>{thread.createdBy}</Typography.Text> <Typography.Text>{thread.createdBy}</Typography.Text>
</Col> </Col>
<Col className="flex items-center gap-2 text-grey-muted" span={8}> <Col className="flex items-center gap-2 text-grey-muted" span={8}>
@ -144,18 +145,21 @@ const TaskTabIncidentManagerHeaderNew = ({ thread }: { thread: Thread }) => {
<Col className="flex items-center gap-2" span={16}> <Col className="flex items-center gap-2" span={16}>
{thread?.task?.assignees?.length === 1 ? ( {thread?.task?.assignees?.length === 1 ? (
<div className="d-flex items-center gap-2"> <div className="d-flex items-center gap-2">
<ProfilePictureNew <UserPopOverCard
avatarType="outlined" userName={thread?.task?.assignees[0].displayName ?? ''}>
name={thread?.task?.assignees[0].displayName ?? ''} <div className="d-flex items-center">
width="24" <ProfilePicture
/> name={thread?.task?.assignees[0].displayName ?? ''}
width="24"
/>
</div>
</UserPopOverCard>
<Typography.Text className="text-grey-body"> <Typography.Text className="text-grey-body">
{thread?.task?.assignees[0].displayName} {thread?.task?.assignees[0].displayName}
</Typography.Text> </Typography.Text>
</div> </div>
) : ( ) : (
<UserAvatarGroup <UserAvatarGroup
avatarSize={24}
className="p-t-05" className="p-t-05"
owners={thread?.task?.assignees} owners={thread?.task?.assignees}
/> />

View File

@ -35,7 +35,8 @@ import { changePassword } from '../../rest/auth-API';
import { getEntityName } from '../../utils/EntityUtils'; import { getEntityName } from '../../utils/EntityUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils'; import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
import DeleteWidgetModal from '../common/DeleteWidget/DeleteWidgetModal'; import DeleteWidgetModal from '../common/DeleteWidget/DeleteWidgetModal';
import ProfilePictureNew from '../common/ProfilePicture/ProfilePictureNew'; import UserPopOverCard from '../common/PopOverCard/UserPopOverCard';
import ProfilePicture from '../common/ProfilePicture/ProfilePicture';
import { ProfileEditModal } from '../Modals/ProfileEditModal/ProfileEditModal'; import { ProfileEditModal } from '../Modals/ProfileEditModal/ProfileEditModal';
import ChangePasswordForm from '../Settings/Users/ChangePasswordForm'; import ChangePasswordForm from '../Settings/Users/ChangePasswordForm';
import './profile-details.less'; import './profile-details.less';
@ -237,12 +238,15 @@ const ProfileSectionUserDetailsCard = ({
</Popover> </Popover>
<div className="m-t-sm"> <div className="m-t-sm">
<ProfilePictureNew <UserPopOverCard userName={getEntityName(userData)}>
avatarType="outlined" <div className="d-flex items-center">
data-testid="replied-user" <ProfilePicture
name={getEntityName(userData)} data-testid="replied-user"
width="80" name={getEntityName(userData)}
/> width="80"
/>
</div>
</UserPopOverCard>
</div> </div>
<div> <div>
<p className="profile-details-title" data-testid="user-display-name"> <p className="profile-details-title" data-testid="user-display-name">

View File

@ -18,7 +18,8 @@ import React, { ReactNode, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ReactComponent as IconUser } from '../../../assets/svg/user.svg'; import { ReactComponent as IconUser } from '../../../assets/svg/user.svg';
import { EntityReference } from '../../../generated/entity/data/table'; import { EntityReference } from '../../../generated/entity/data/table';
import ProfilePictureNew from '../ProfilePicture/ProfilePictureNew'; import UserPopOverCard from '../PopOverCard/UserPopOverCard';
import ProfilePicture from '../ProfilePicture/ProfilePicture';
import './owner-label.less'; import './owner-label.less';
export const UserAvatarGroup = ({ export const UserAvatarGroup = ({
@ -29,7 +30,7 @@ export const UserAvatarGroup = ({
ownerDisplayName, ownerDisplayName,
placeHolder, placeHolder,
maxVisibleOwners = 2, maxVisibleOwners = 2,
avatarSize = 24, avatarSize = '24',
}: { }: {
owners?: EntityReference[]; owners?: EntityReference[];
className?: string; className?: string;
@ -43,7 +44,7 @@ export const UserAvatarGroup = ({
team: boolean; team: boolean;
}; };
tooltipText?: string; tooltipText?: string;
avatarSize?: number; avatarSize?: string;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -57,11 +58,15 @@ export const UserAvatarGroup = ({
key: owner.id, key: owner.id,
label: ( label: (
<div className="d-flex items-center gap-2"> <div className="d-flex items-center gap-2">
<ProfilePictureNew <UserPopOverCard userName={owner.displayName ?? ''}>
avatarType="outlined" <div className="d-flex items-center">
name={owner.displayName ?? ''} <ProfilePicture
size={avatarSize} displayName={owner.displayName ?? ''}
/> name={owner.displayName ?? ''}
width={avatarSize}
/>
</div>
</UserPopOverCard>
<Typography.Text>{owner.displayName}</Typography.Text> <Typography.Text>{owner.displayName}</Typography.Text>
</div> </div>
), ),
@ -79,19 +84,22 @@ export const UserAvatarGroup = ({
<Avatar.Group className="avatar-group"> <Avatar.Group className="avatar-group">
{visibleOwners.map((owner) => ( {visibleOwners.map((owner) => (
<div className="avatar-overlap" key={owner.id}> <div className="avatar-overlap" key={owner.id}>
<ProfilePictureNew <UserPopOverCard userName={owner.displayName ?? ''}>
avatarType="outlined" <div className="d-flex items-center">
displayName={owner.displayName ?? ''} <ProfilePicture
name={owner.name ?? ''} displayName={owner.displayName ?? ''}
size={avatarSize} name={owner.name ?? ''}
/> width={avatarSize}
/>
</div>
</UserPopOverCard>
</div> </div>
))} ))}
{remainingOwnersCount > 0 && ( {remainingOwnersCount > 0 && (
<Dropdown menu={remainingOwnersMenu} trigger={['click']}> <Dropdown menu={remainingOwnersMenu} trigger={['click']}>
<Avatar <Avatar
className="owner-count-avatar avatar-overlap" className="owner-count-avatar avatar-overlap"
size={avatarSize}> size={Number(avatarSize)}>
<span> <span>
{t('label.plus-symbol')} {t('label.plus-symbol')}
{remainingOwnersCount} {remainingOwnersCount}

View File

@ -206,7 +206,9 @@ const PopoverTitle = React.memo(
e.stopPropagation(); e.stopPropagation();
onTitleClickHandler(getUserPath(name)); onTitleClickHandler(getUserPath(name));
}}> }}>
<span className="font-medium m-r-xs">{displayName}</span> <span className="font-medium m-r-xs" data-testid="user-name">
{displayName}
</span>
</Button> </Button>
{displayName !== name ? ( {displayName !== name ? (
<span className="text-grey-muted">{name}</span> <span className="text-grey-muted">{name}</span>

View File

@ -45,7 +45,7 @@ const ProfilePicture = ({
height, height,
isTeam = false, isTeam = false,
size, size,
avatarType = 'solid', avatarType = 'outlined',
}: Props) => { }: Props) => {
const { permissions } = usePermissionProvider(); const { permissions } = usePermissionProvider();
const { color, character, backgroundColor } = getRandomColor( const { color, character, backgroundColor } = getRandomColor(

View File

@ -1,137 +0,0 @@
/*
* Copyright 2022 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Avatar, Tooltip } from 'antd';
import classNames from 'classnames';
import { parseInt } from 'lodash';
import { ImageShape } from 'Models';
import React, { useMemo } from 'react';
import { Link } from 'react-router-dom';
import { usePermissionProvider } from '../../../context/PermissionProvider/PermissionProvider';
import { ResourceEntity } from '../../../context/PermissionProvider/PermissionProvider.interface';
import { User } from '../../../generated/entity/teams/user';
import { useUserProfile } from '../../../hooks/user-profile/useUserProfile';
import { getRandomColor } from '../../../utils/CommonUtils';
import { userPermissions } from '../../../utils/PermissionsUtils';
import { getUserPath } from '../../../utils/RouterUtils';
import Loader from '../Loader/Loader';
import UserPopOverCard from '../PopOverCard/UserPopOverCard';
type UserData = Pick<User, 'name' | 'displayName'>;
interface Props extends UserData {
width?: string;
type?: ImageShape;
className?: string;
height?: string;
isTeam?: boolean;
size?: number | 'small' | 'default' | 'large';
avatarType?: 'solid' | 'outlined';
}
const ProfilePictureNew = ({
name,
displayName,
className = '',
type = 'circle',
width = '36',
height,
isTeam = false,
size,
avatarType = 'solid',
}: Props) => {
const { permissions } = usePermissionProvider();
const { color, character, backgroundColor } = getRandomColor(
displayName ?? name
);
const viewUserPermission = useMemo(() => {
return userPermissions.hasViewPermissions(ResourceEntity.USER, permissions);
}, [permissions]);
const [profileURL, isPicLoading] = useUserProfile({
permission: viewUserPermission,
name,
isTeam,
});
const getAvatarByName = () => {
const avatar = (
<Avatar
className={classNames('flex-center', className)}
data-testid="profile-avatar"
icon={character}
shape={type}
size={size ?? parseInt(width)}
style={{
color: avatarType === 'solid' ? 'default' : color,
backgroundColor: avatarType === 'solid' ? color : backgroundColor,
fontWeight: avatarType === 'solid' ? 400 : 500,
border: `0.5px solid ${avatarType === 'solid' ? 'default' : color}`,
}}
/>
);
return (
<UserPopOverCard userName={name ?? ''}>
<Link className="no-underline" to={getUserPath(name ?? '')}>
{avatar}
</Link>
</UserPopOverCard>
);
};
const getAvatarElement = () => {
return isPicLoading ? (
<div
className="d-inline-block relative"
style={{
height: typeof size === 'number' ? `${size}px` : height,
width: typeof size === 'number' ? `${size}px` : width,
}}>
{getAvatarByName()}
<div
className="absolute inset-0 opacity-60 bg-grey-4 rounded-full"
data-testid="loader-cntnr">
<Loader
className="absolute inset-0"
size="small"
style={{
height: typeof size === 'number' ? `${size}px` : `${+width}px`,
width: typeof size === 'number' ? `${size}px` : `${+width}px`,
}}
type="white"
/>
</div>
</div>
) : (
getAvatarByName()
);
};
return profileURL ? (
<Tooltip placement="top" title={displayName ?? name}>
<Avatar
className={className}
data-testid="profile-image"
shape={type}
size={size ?? parseInt(width)}
src={profileURL}
/>
</Tooltip>
) : (
getAvatarElement()
);
};
export default ProfilePictureNew;