mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-05 17:53:12 +02:00
refactor: create user avatar component, clean up (#1151)
* refactor: create user avatar component, clean up * fix: small changes regarding the new badge component
This commit is contained in:
parent
f86bd16c7e
commit
61c0d6f0a1
@ -1,6 +1,5 @@
|
|||||||
import { useEffect, useMemo, useState, VFC } from 'react';
|
import { useEffect, useMemo, useState, VFC } from 'react';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
|
||||||
Button,
|
Button,
|
||||||
IconButton,
|
IconButton,
|
||||||
styled,
|
styled,
|
||||||
@ -37,12 +36,7 @@ import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
|
|||||||
import { AddGroupUser } from './AddGroupUser/AddGroupUser';
|
import { AddGroupUser } from './AddGroupUser/AddGroupUser';
|
||||||
import { EditGroupUser } from './EditGroupUser/EditGroupUser';
|
import { EditGroupUser } from './EditGroupUser/EditGroupUser';
|
||||||
import { RemoveGroupUser } from './RemoveGroupUser/RemoveGroupUser';
|
import { RemoveGroupUser } from './RemoveGroupUser/RemoveGroupUser';
|
||||||
|
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
||||||
const StyledAvatar = styled(Avatar)(({ theme }) => ({
|
|
||||||
width: theme.spacing(4),
|
|
||||||
height: theme.spacing(4),
|
|
||||||
margin: 'auto',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledEdit = styled(Edit)(({ theme }) => ({
|
const StyledEdit = styled(Edit)(({ theme }) => ({
|
||||||
fontSize: theme.fontSizes.mainHeader,
|
fontSize: theme.fontSizes.mainHeader,
|
||||||
@ -87,14 +81,7 @@ export const Group: VFC = () => {
|
|||||||
accessor: 'imageUrl',
|
accessor: 'imageUrl',
|
||||||
Cell: ({ row: { original: user } }: any) => (
|
Cell: ({ row: { original: user } }: any) => (
|
||||||
<TextCell>
|
<TextCell>
|
||||||
<StyledAvatar
|
<UserAvatar user={user} />
|
||||||
data-loading
|
|
||||||
alt="Gravatar"
|
|
||||||
src={user.imageUrl}
|
|
||||||
title={`${
|
|
||||||
user.name || user.email || user.username
|
|
||||||
} (id: ${user.id})`}
|
|
||||||
/>
|
|
||||||
</TextCell>
|
</TextCell>
|
||||||
),
|
),
|
||||||
maxWidth: 85,
|
maxWidth: 85,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo, VFC } from 'react';
|
import { useMemo, VFC } from 'react';
|
||||||
import { Avatar, IconButton, styled, Tooltip } from '@mui/material';
|
import { IconButton, Tooltip } from '@mui/material';
|
||||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||||
import { IGroupUser } from 'interfaces/group';
|
import { IGroupUser } from 'interfaces/group';
|
||||||
import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
|
import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
|
||||||
@ -10,12 +10,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
|||||||
import { VirtualizedTable } from 'component/common/Table';
|
import { VirtualizedTable } from 'component/common/Table';
|
||||||
import { useFlexLayout, useSortBy, useTable } from 'react-table';
|
import { useFlexLayout, useSortBy, useTable } from 'react-table';
|
||||||
import { sortTypes } from 'utils/sortTypes';
|
import { sortTypes } from 'utils/sortTypes';
|
||||||
|
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
||||||
const StyledAvatar = styled(Avatar)(({ theme }) => ({
|
|
||||||
width: theme.spacing(4),
|
|
||||||
height: theme.spacing(4),
|
|
||||||
margin: 'auto',
|
|
||||||
}));
|
|
||||||
|
|
||||||
interface IGroupFormUsersTableProps {
|
interface IGroupFormUsersTableProps {
|
||||||
users: IGroupUser[];
|
users: IGroupUser[];
|
||||||
@ -33,14 +28,7 @@ export const GroupFormUsersTable: VFC<IGroupFormUsersTableProps> = ({
|
|||||||
accessor: 'imageUrl',
|
accessor: 'imageUrl',
|
||||||
Cell: ({ row: { original: user } }: any) => (
|
Cell: ({ row: { original: user } }: any) => (
|
||||||
<TextCell>
|
<TextCell>
|
||||||
<StyledAvatar
|
<UserAvatar user={user} />
|
||||||
data-loading
|
|
||||||
alt="Gravatar"
|
|
||||||
src={user.imageUrl}
|
|
||||||
title={`${
|
|
||||||
user.name || user.email || user.username
|
|
||||||
} (id: ${user.id})`}
|
|
||||||
/>
|
|
||||||
</TextCell>
|
</TextCell>
|
||||||
),
|
),
|
||||||
maxWidth: 85,
|
maxWidth: 85,
|
||||||
|
@ -7,6 +7,7 @@ import { Badge } from 'component/common/Badge/Badge';
|
|||||||
import { GroupCardActions } from './GroupCardActions/GroupCardActions';
|
import { GroupCardActions } from './GroupCardActions/GroupCardActions';
|
||||||
import { RemoveGroup } from 'component/admin/groups/RemoveGroup/RemoveGroup';
|
import { RemoveGroup } from 'component/admin/groups/RemoveGroup/RemoveGroup';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import TopicOutlinedIcon from '@mui/icons-material/TopicOutlined';
|
||||||
|
|
||||||
const StyledLink = styled(Link)(({ theme }) => ({
|
const StyledLink = styled(Link)(({ theme }) => ({
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
@ -93,6 +94,7 @@ export const GroupCard = ({ group }: IGroupCardProps) => {
|
|||||||
show={group.projects.map(project => (
|
show={group.projects.map(project => (
|
||||||
<Badge
|
<Badge
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
icon={<TopicOutlinedIcon />}
|
||||||
sx={{ marginRight: 0.5 }}
|
sx={{ marginRight: 0.5 }}
|
||||||
>
|
>
|
||||||
{project}
|
{project}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Avatar, Badge, styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { IGroupUser, Role } from 'interfaces/group';
|
import { IGroupUser, Role } from 'interfaces/group';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import StarIcon from '@mui/icons-material/Star';
|
|
||||||
import { GroupPopover } from './GroupPopover/GroupPopover';
|
import { GroupPopover } from './GroupPopover/GroupPopover';
|
||||||
|
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
||||||
|
|
||||||
const StyledAvatars = styled('div')(({ theme }) => ({
|
const StyledAvatars = styled('div')(({ theme }) => ({
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
@ -12,25 +12,8 @@ const StyledAvatars = styled('div')(({ theme }) => ({
|
|||||||
marginLeft: theme.spacing(1),
|
marginLeft: theme.spacing(1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledAvatar = styled(Avatar)(({ theme }) => ({
|
const StyledAvatar = styled(UserAvatar)(({ theme }) => ({
|
||||||
width: theme.spacing(4),
|
outline: `${theme.spacing(0.25)} solid ${theme.palette.background.paper}`,
|
||||||
height: theme.spacing(4),
|
|
||||||
outline: `2px solid ${theme.palette.background.paper}`,
|
|
||||||
marginLeft: theme.spacing(-1),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledAvatarMore = styled(StyledAvatar)(({ theme }) => ({
|
|
||||||
backgroundColor: theme.palette.secondary.light,
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
fontSize: theme.fontSizes.smallerBody,
|
|
||||||
fontWeight: theme.fontWeight.bold,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledStar = styled(StarIcon)(({ theme }) => ({
|
|
||||||
color: theme.palette.warning.main,
|
|
||||||
backgroundColor: theme.palette.background.paper,
|
|
||||||
borderRadius: theme.shape.borderRadiusExtraLarge,
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
marginLeft: theme.spacing(-1),
|
marginLeft: theme.spacing(-1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -60,50 +43,22 @@ export const GroupCardAvatars = ({ users }: IGroupCardAvatarsProps) => {
|
|||||||
return (
|
return (
|
||||||
<StyledAvatars>
|
<StyledAvatars>
|
||||||
{shownUsers.map(user => (
|
{shownUsers.map(user => (
|
||||||
<ConditionallyRender
|
<StyledAvatar
|
||||||
key={user.id}
|
user={user}
|
||||||
condition={user.role === Role.Member}
|
star={user.role === Role.Owner}
|
||||||
show={
|
onMouseEnter={event => {
|
||||||
<StyledAvatar
|
onPopoverOpen(event);
|
||||||
data-loading
|
setPopupUser(user);
|
||||||
alt="Gravatar"
|
}}
|
||||||
src={user.imageUrl}
|
onMouseLeave={onPopoverClose}
|
||||||
onMouseEnter={event => {
|
|
||||||
onPopoverOpen(event);
|
|
||||||
setPopupUser(user);
|
|
||||||
}}
|
|
||||||
onMouseLeave={onPopoverClose}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
elseShow={
|
|
||||||
<Badge
|
|
||||||
overlap="circular"
|
|
||||||
anchorOrigin={{
|
|
||||||
vertical: 'top',
|
|
||||||
horizontal: 'left',
|
|
||||||
}}
|
|
||||||
badgeContent={<StyledStar />}
|
|
||||||
>
|
|
||||||
<StyledAvatar
|
|
||||||
data-loading
|
|
||||||
alt="Gravatar"
|
|
||||||
src={user.imageUrl}
|
|
||||||
onMouseEnter={event => {
|
|
||||||
onPopoverOpen(event);
|
|
||||||
setPopupUser(user);
|
|
||||||
}}
|
|
||||||
onMouseLeave={onPopoverClose}
|
|
||||||
/>
|
|
||||||
</Badge>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={users.length > 9}
|
condition={users.length > 9}
|
||||||
show={
|
show={
|
||||||
<StyledAvatarMore>
|
<StyledAvatar>
|
||||||
+{users.length - shownUsers.length}
|
+{users.length - shownUsers.length}
|
||||||
</StyledAvatarMore>
|
</StyledAvatar>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<GroupPopover
|
<GroupPopover
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Badge, Popover, styled } from '@mui/material';
|
import { Badge, Popover, styled } from '@mui/material';
|
||||||
import { IGroupUser, Role } from 'interfaces/group';
|
import { IGroupUser, Role } from 'interfaces/group';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
|
||||||
import { Badge as StyledBadge } from 'component/common/Badge/Badge';
|
import { Badge as StyledBadge } from 'component/common/Badge/Badge';
|
||||||
import StarIcon from '@mui/icons-material/Star';
|
import StarIcon from '@mui/icons-material/Star';
|
||||||
|
|
||||||
@ -76,7 +75,7 @@ export const GroupPopover = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<StyledName>{user?.name}</StyledName>
|
<StyledName>{user?.name || user?.username}</StyledName>
|
||||||
<div>{user?.email}</div>
|
<div>{user?.email}</div>
|
||||||
</StyledPopover>
|
</StyledPopover>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { Avatar, TextField, Typography } from '@mui/material';
|
import { styled, TextField, Typography } from '@mui/material';
|
||||||
import { trim } from 'component/common/util';
|
import { trim } from 'component/common/util';
|
||||||
import { modalStyles } from 'component/admin/users/util';
|
import { modalStyles } from 'component/admin/users/util';
|
||||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||||
@ -11,6 +11,13 @@ import { useThemeStyles } from 'themes/themeStyles';
|
|||||||
import PasswordMatcher from 'component/user/common/ResetPasswordForm/PasswordMatcher/PasswordMatcher';
|
import PasswordMatcher from 'component/user/common/ResetPasswordForm/PasswordMatcher/PasswordMatcher';
|
||||||
import { IUser } from 'interfaces/user';
|
import { IUser } from 'interfaces/user';
|
||||||
import useAdminUsersApi from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi';
|
import useAdminUsersApi from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi';
|
||||||
|
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
||||||
|
|
||||||
|
const StyledUserAvatar = styled(UserAvatar)(({ theme }) => ({
|
||||||
|
width: theme.spacing(5),
|
||||||
|
height: theme.spacing(5),
|
||||||
|
margin: 0,
|
||||||
|
}));
|
||||||
|
|
||||||
interface IChangePasswordProps {
|
interface IChangePasswordProps {
|
||||||
showDialog: boolean;
|
showDialog: boolean;
|
||||||
@ -85,14 +92,7 @@ const ChangePassword = ({
|
|||||||
Changing password for user
|
Changing password for user
|
||||||
</Typography>
|
</Typography>
|
||||||
<div className={themeStyles.flexRow}>
|
<div className={themeStyles.flexRow}>
|
||||||
<Avatar
|
<StyledUserAvatar user={user} variant="rounded" />
|
||||||
variant="rounded"
|
|
||||||
alt="Gravatar"
|
|
||||||
src={user.imageUrl}
|
|
||||||
title={`${
|
|
||||||
user.name || user.email || user.username
|
|
||||||
} (id: ${user.id})`}
|
|
||||||
/>
|
|
||||||
<Typography
|
<Typography
|
||||||
variant="subtitle1"
|
variant="subtitle1"
|
||||||
style={{ marginLeft: '1rem' }}
|
style={{ marginLeft: '1rem' }}
|
||||||
|
@ -2,11 +2,18 @@ import React from 'react';
|
|||||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { REMOVE_USER_ERROR } from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi';
|
import { REMOVE_USER_ERROR } from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi';
|
||||||
import { Alert } from '@mui/material';
|
import { Alert, styled } from '@mui/material';
|
||||||
import useLoading from 'hooks/useLoading';
|
import useLoading from 'hooks/useLoading';
|
||||||
import { Avatar, Typography } from '@mui/material';
|
import { Typography } from '@mui/material';
|
||||||
import { useThemeStyles } from 'themes/themeStyles';
|
import { useThemeStyles } from 'themes/themeStyles';
|
||||||
import { IUser } from 'interfaces/user';
|
import { IUser } from 'interfaces/user';
|
||||||
|
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
||||||
|
|
||||||
|
const StyledUserAvatar = styled(UserAvatar)(({ theme }) => ({
|
||||||
|
width: theme.spacing(5),
|
||||||
|
height: theme.spacing(5),
|
||||||
|
margin: 0,
|
||||||
|
}));
|
||||||
|
|
||||||
interface IDeleteUserProps {
|
interface IDeleteUserProps {
|
||||||
showDialog: boolean;
|
showDialog: boolean;
|
||||||
@ -51,14 +58,7 @@ const DeleteUser = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<div data-loading className={themeStyles.flexRow}>
|
<div data-loading className={themeStyles.flexRow}>
|
||||||
<Avatar
|
<StyledUserAvatar user={user} variant="rounded" />
|
||||||
variant="rounded"
|
|
||||||
alt="Gravatar"
|
|
||||||
src={user.imageUrl}
|
|
||||||
title={`${
|
|
||||||
user.name || user.email || user.username
|
|
||||||
} (id: ${user.id})`}
|
|
||||||
/>
|
|
||||||
<Typography
|
<Typography
|
||||||
variant="subtitle1"
|
variant="subtitle1"
|
||||||
style={{ marginLeft: '1rem' }}
|
style={{ marginLeft: '1rem' }}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable no-alert */
|
|
||||||
import React, { useState, useEffect, useMemo } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
@ -21,7 +20,7 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
|||||||
import { useUsersPlan } from 'hooks/useUsersPlan';
|
import { useUsersPlan } from 'hooks/useUsersPlan';
|
||||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||||
import { Avatar, Button, styled, useMediaQuery } from '@mui/material';
|
import { Button, useMediaQuery } from '@mui/material';
|
||||||
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||||
import { UserTypeCell } from './UserTypeCell/UserTypeCell';
|
import { UserTypeCell } from './UserTypeCell/UserTypeCell';
|
||||||
import { useGlobalFilter, useSortBy, useTable } from 'react-table';
|
import { useGlobalFilter, useSortBy, useTable } from 'react-table';
|
||||||
@ -34,12 +33,7 @@ import theme from 'themes/theme';
|
|||||||
import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell';
|
import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell';
|
||||||
import { UsersActionsCell } from './UsersActionsCell/UsersActionsCell';
|
import { UsersActionsCell } from './UsersActionsCell/UsersActionsCell';
|
||||||
import { Search } from 'component/common/Search/Search';
|
import { Search } from 'component/common/Search/Search';
|
||||||
|
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
||||||
const StyledAvatar = styled(Avatar)(({ theme }) => ({
|
|
||||||
width: theme.spacing(4),
|
|
||||||
height: theme.spacing(4),
|
|
||||||
margin: 'auto',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const UsersList = () => {
|
const UsersList = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -125,14 +119,7 @@ const UsersList = () => {
|
|||||||
accessor: 'imageUrl',
|
accessor: 'imageUrl',
|
||||||
Cell: ({ row: { original: user } }: any) => (
|
Cell: ({ row: { original: user } }: any) => (
|
||||||
<TextCell>
|
<TextCell>
|
||||||
<StyledAvatar
|
<UserAvatar user={user} />
|
||||||
data-loading
|
|
||||||
alt="Gravatar"
|
|
||||||
src={user.imageUrl}
|
|
||||||
title={`${
|
|
||||||
user.name || user.email || user.username
|
|
||||||
} (id: ${user.id})`}
|
|
||||||
/>
|
|
||||||
</TextCell>
|
</TextCell>
|
||||||
),
|
),
|
||||||
disableGlobalFilter: true,
|
disableGlobalFilter: true,
|
||||||
@ -212,7 +199,7 @@ const UsersList = () => {
|
|||||||
setHiddenColumns,
|
setHiddenColumns,
|
||||||
} = useTable(
|
} = useTable(
|
||||||
{
|
{
|
||||||
columns: columns as any[], // TODO: fix after `react-table` v8 update
|
columns: columns,
|
||||||
data,
|
data,
|
||||||
initialState,
|
initialState,
|
||||||
sortTypes,
|
sortTypes,
|
||||||
|
@ -24,12 +24,14 @@ interface IBadgeIconProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const StyledBadge = styled('div')<IBadgeProps>(
|
const StyledBadge = styled('div')<IBadgeProps>(
|
||||||
({ theme, color = 'neutral' }) => ({
|
({ theme, color = 'neutral', icon }) => ({
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
padding: theme.spacing(0.5, 1),
|
padding: theme.spacing(icon ? 0.375 : 0.625, 1),
|
||||||
borderRadius: theme.shape.borderRadius,
|
borderRadius: theme.shape.borderRadius,
|
||||||
fontSize: theme.fontSizes.smallerBody,
|
fontSize: theme.fontSizes.smallerBody,
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
lineHeight: 1,
|
||||||
backgroundColor: theme.palette[color].light,
|
backgroundColor: theme.palette[color].light,
|
||||||
color: theme.palette[color].dark,
|
color: theme.palette[color].dark,
|
||||||
border: `1px solid ${theme.palette[color].border}`,
|
border: `1px solid ${theme.palette[color].border}`,
|
||||||
@ -58,6 +60,7 @@ export const Badge: FC<IBadgeProps> = forwardRef(
|
|||||||
) => (
|
) => (
|
||||||
<StyledBadge
|
<StyledBadge
|
||||||
color={color}
|
color={color}
|
||||||
|
icon={icon}
|
||||||
className={className}
|
className={className}
|
||||||
sx={sx}
|
sx={sx}
|
||||||
{...props}
|
{...props}
|
||||||
|
115
frontend/src/component/common/UserAvatar/UserAvatar.tsx
Normal file
115
frontend/src/component/common/UserAvatar/UserAvatar.tsx
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
AvatarProps,
|
||||||
|
Badge,
|
||||||
|
styled,
|
||||||
|
SxProps,
|
||||||
|
Theme,
|
||||||
|
} from '@mui/material';
|
||||||
|
import { IUser } from 'interfaces/user';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import StarIcon from '@mui/icons-material/Star';
|
||||||
|
|
||||||
|
const StyledAvatar = styled(Avatar)(({ theme }) => ({
|
||||||
|
width: theme.spacing(4),
|
||||||
|
height: theme.spacing(4),
|
||||||
|
margin: 'auto',
|
||||||
|
backgroundColor: theme.palette.secondary.light,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
fontSize: theme.fontSizes.smallerBody,
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledStar = styled(StarIcon)(({ theme }) => ({
|
||||||
|
color: theme.palette.warning.main,
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
borderRadius: theme.shape.borderRadiusExtraLarge,
|
||||||
|
fontSize: theme.fontSizes.smallBody,
|
||||||
|
marginLeft: theme.spacing(-1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface IUserAvatarProps extends AvatarProps {
|
||||||
|
user?: IUser;
|
||||||
|
star?: boolean;
|
||||||
|
src?: string;
|
||||||
|
title?: string;
|
||||||
|
onMouseEnter?: (event: any) => void;
|
||||||
|
onMouseLeave?: () => void;
|
||||||
|
className?: string;
|
||||||
|
sx?: SxProps<Theme>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserAvatar: FC<IUserAvatarProps> = ({
|
||||||
|
user,
|
||||||
|
star,
|
||||||
|
src,
|
||||||
|
title,
|
||||||
|
onMouseEnter,
|
||||||
|
onMouseLeave,
|
||||||
|
className,
|
||||||
|
sx,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
if (!title && !onMouseEnter && user) {
|
||||||
|
title = `${user?.name || user?.email || user?.username} (id: ${
|
||||||
|
user?.id
|
||||||
|
})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!src && user) {
|
||||||
|
src = user?.imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fallback;
|
||||||
|
if (!children && user) {
|
||||||
|
fallback = user?.name || user?.email || user?.username;
|
||||||
|
if (fallback && fallback.includes(' ')) {
|
||||||
|
fallback = `${fallback.split(' ')[0][0]}${
|
||||||
|
fallback.split(' ')[1][0]
|
||||||
|
}`.toUpperCase();
|
||||||
|
} else if (fallback) {
|
||||||
|
fallback = fallback[0].toUpperCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const avatar = (
|
||||||
|
<StyledAvatar
|
||||||
|
className={className}
|
||||||
|
sx={sx}
|
||||||
|
{...props}
|
||||||
|
data-loading
|
||||||
|
alt="Gravatar"
|
||||||
|
src={src}
|
||||||
|
title={title}
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(fallback)}
|
||||||
|
show={fallback}
|
||||||
|
elseShow={children}
|
||||||
|
/>
|
||||||
|
</StyledAvatar>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(star)}
|
||||||
|
show={
|
||||||
|
<Badge
|
||||||
|
overlap="circular"
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: 'top',
|
||||||
|
horizontal: 'left',
|
||||||
|
}}
|
||||||
|
badgeContent={<StyledStar />}
|
||||||
|
>
|
||||||
|
{avatar}
|
||||||
|
</Badge>
|
||||||
|
}
|
||||||
|
elseShow={avatar}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useMemo, useState, VFC } from 'react';
|
import { useEffect, useMemo, useState, VFC } from 'react';
|
||||||
import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table';
|
import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table';
|
||||||
import { VirtualizedTable, TablePlaceholder } from 'component/common/Table';
|
import { VirtualizedTable, TablePlaceholder } from 'component/common/Table';
|
||||||
import { Avatar, Button, styled, useMediaQuery, useTheme } from '@mui/material';
|
import { Button, useMediaQuery, useTheme } from '@mui/material';
|
||||||
import { Delete, Edit } from '@mui/icons-material';
|
import { Delete, Edit } from '@mui/icons-material';
|
||||||
import { sortTypes } from 'utils/sortTypes';
|
import { sortTypes } from 'utils/sortTypes';
|
||||||
import useProjectAccess, {
|
import useProjectAccess, {
|
||||||
@ -32,16 +32,7 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
|||||||
import { IUser } from 'interfaces/user';
|
import { IUser } from 'interfaces/user';
|
||||||
import { IGroup } from 'interfaces/group';
|
import { IGroup } from 'interfaces/group';
|
||||||
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
||||||
|
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
||||||
const StyledAvatar = styled(Avatar)(({ theme }) => ({
|
|
||||||
width: theme.spacing(4),
|
|
||||||
height: theme.spacing(4),
|
|
||||||
margin: 'auto',
|
|
||||||
backgroundColor: theme.palette.secondary.light,
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
fontWeight: theme.fontWeight.bold,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export type PageQueryType = Partial<
|
export type PageQueryType = Partial<
|
||||||
Record<'sort' | 'order' | 'search', string>
|
Record<'sort' | 'order' | 'search', string>
|
||||||
@ -106,18 +97,9 @@ export const ProjectAccessTable: VFC = () => {
|
|||||||
accessor: 'imageUrl',
|
accessor: 'imageUrl',
|
||||||
Cell: ({ row: { original: row } }: any) => (
|
Cell: ({ row: { original: row } }: any) => (
|
||||||
<TextCell>
|
<TextCell>
|
||||||
<StyledAvatar
|
<UserAvatar user={row.entity}>
|
||||||
data-loading
|
|
||||||
alt="Gravatar"
|
|
||||||
src={row.entity.imageUrl}
|
|
||||||
title={`${
|
|
||||||
row.entity.name ||
|
|
||||||
row.entity.email ||
|
|
||||||
row.entity.username
|
|
||||||
} (id: ${row.entity.id})`}
|
|
||||||
>
|
|
||||||
{row.entity.users?.length}
|
{row.entity.users?.length}
|
||||||
</StyledAvatar>
|
</UserAvatar>
|
||||||
</TextCell>
|
</TextCell>
|
||||||
),
|
),
|
||||||
maxWidth: 85,
|
maxWidth: 85,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Delete, Edit } from '@mui/icons-material';
|
import { Delete, Edit } from '@mui/icons-material';
|
||||||
import { Avatar, styled, useMediaQuery, useTheme } from '@mui/material';
|
import { styled, useMediaQuery, useTheme } from '@mui/material';
|
||||||
import { GroupUserRoleCell } from 'component/admin/groups/GroupUserRoleCell/GroupUserRoleCell';
|
import { GroupUserRoleCell } from 'component/admin/groups/GroupUserRoleCell/GroupUserRoleCell';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
@ -13,6 +13,7 @@ import { HighlightCell } from 'component/common/Table/cells/HighlightCell/Highli
|
|||||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||||
import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell';
|
import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell';
|
||||||
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||||
|
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
||||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
||||||
import { useSearch } from 'hooks/useSearch';
|
import { useSearch } from 'hooks/useSearch';
|
||||||
import { IGroup, IGroupUser } from 'interfaces/group';
|
import { IGroup, IGroupUser } from 'interfaces/group';
|
||||||
@ -20,12 +21,6 @@ import { VFC, useState } from 'react';
|
|||||||
import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table';
|
import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table';
|
||||||
import { sortTypes } from 'utils/sortTypes';
|
import { sortTypes } from 'utils/sortTypes';
|
||||||
|
|
||||||
const StyledAvatar = styled(Avatar)(({ theme }) => ({
|
|
||||||
width: theme.spacing(4),
|
|
||||||
height: theme.spacing(4),
|
|
||||||
margin: 'auto',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledPageContent = styled(PageContent)(({ theme }) => ({
|
const StyledPageContent = styled(PageContent)(({ theme }) => ({
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
padding: theme.spacing(7.5, 6),
|
padding: theme.spacing(7.5, 6),
|
||||||
@ -54,14 +49,7 @@ const columns = [
|
|||||||
accessor: 'imageUrl',
|
accessor: 'imageUrl',
|
||||||
Cell: ({ row: { original: user } }: any) => (
|
Cell: ({ row: { original: user } }: any) => (
|
||||||
<TextCell>
|
<TextCell>
|
||||||
<StyledAvatar
|
<UserAvatar user={user} />
|
||||||
data-loading
|
|
||||||
alt="Gravatar"
|
|
||||||
src={user.imageUrl}
|
|
||||||
title={`${user.name || user.email || user.username} (id: ${
|
|
||||||
user.id
|
|
||||||
})`}
|
|
||||||
/>
|
|
||||||
</TextCell>
|
</TextCell>
|
||||||
),
|
),
|
||||||
maxWidth: 85,
|
maxWidth: 85,
|
||||||
|
Loading…
Reference in New Issue
Block a user