Skip to content

Commit

Permalink
chore: group cards new design
Browse files Browse the repository at this point in the history
  • Loading branch information
nunogois committed Jan 2, 2025
1 parent 54d6bd5 commit 51c3847
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 136 deletions.
285 changes: 169 additions & 116 deletions frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { styled, Tooltip } from '@mui/material';
import { Box, Card, styled } from '@mui/material';
import type { IGroup } from 'interfaces/group';
import { Link, useNavigate } from 'react-router-dom';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
Expand All @@ -7,88 +7,119 @@ import { GroupCardActions } from './GroupCardActions/GroupCardActions';
import TopicOutlinedIcon from '@mui/icons-material/TopicOutlined';
import { RoleBadge } from 'component/common/RoleBadge/RoleBadge';
import { useScimSettings } from 'hooks/api/getters/useScimSettings/useScimSettings';
import { AvatarGroup } from 'component/common/AvatarGroup/AvatarGroup';

const StyledLink = styled(Link)(({ theme }) => ({
import {
AvatarComponent,
AvatarGroup,
} from 'component/common/AvatarGroup/AvatarGroup';
import GroupsIcon from '@mui/icons-material/GroupsOutlined';
import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
import { Highlighter } from 'component/common/Highlighter/Highlighter';
import { Truncator } from 'component/common/Truncator/Truncator';
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';

const StyledCardLink = styled(Link)(({ theme }) => ({
color: 'inherit',
textDecoration: 'none',
color: theme.palette.text.primary,
border: 'none',
padding: '0',
background: 'transparent',
fontFamily: theme.typography.fontFamily,
pointer: 'cursor',
}));

const StyledGroupCard = styled('aside')(({ theme }) => ({
padding: theme.spacing(2.5),
height: '100%',
border: `1px solid ${theme.palette.divider}`,
borderRadius: theme.shape.borderRadiusLarge,
boxShadow: theme.boxShadows.card,
const StyledCard = styled(Card)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
[theme.breakpoints.up('md')]: {
padding: theme.spacing(4),
justifyContent: 'space-between',
height: '100%',
boxShadow: 'none',
border: `1px solid ${theme.palette.divider}`,
[theme.breakpoints.down('sm')]: {
justifyContent: 'center',
},
transition: 'background-color 0.2s ease-in-out',
backgroundColor: theme.palette.background.default,
'&:hover': {
transition: 'background-color 0.2s ease-in-out',
backgroundColor: theme.palette.neutral.light,
},
borderRadius: theme.shape.borderRadiusMedium,
}));

const StyledRow = styled('div')(() => ({
const StyledCardBody = styled(Box)(({ theme }) => ({
padding: theme.spacing(2),
display: 'flex',
flexFlow: 'column',
height: '100%',
position: 'relative',
}));

const StyledCardBodyHeader = styled('div')(({ theme }) => ({
display: 'flex',
gap: theme.spacing(1),
width: '100%',
alignItems: 'center',
justifyContent: 'space-between',
}));

const StyledTitleRow = styled(StyledRow)(() => ({
alignItems: 'flex-start',
const StyledCardIconContainer = styled(Box)(({ theme }) => ({
display: 'grid',
placeItems: 'center',
padding: theme.spacing(0.5),
alignSelf: 'baseline',
backgroundColor: theme.palette.secondary.light,
color: theme.palette.primary.main,
borderRadius: theme.shape.borderRadiusMedium,
'& > svg': {
height: theme.spacing(2),
width: theme.spacing(2),
},
}));

const StyledBottomRow = styled(StyledRow)(({ theme }) => ({
marginTop: 'auto',
alignItems: 'flex-end',
gap: theme.spacing(1),
const StyledCardTitle = styled('h3')(({ theme }) => ({
margin: 0,
marginRight: 'auto',
fontWeight: theme.typography.fontWeightRegular,
fontSize: theme.typography.body1.fontSize,
lineHeight: '1.2',
}));

const StyledHeaderTitle = styled('h2')(({ theme }) => ({
fontSize: theme.fontSizes.mainHeader,
fontWeight: theme.fontWeight.medium,
const StyledCardDescription = styled('p')(({ theme }) => ({
color: theme.palette.text.secondary,
fontSize: theme.fontSizes.smallBody,
marginTop: theme.spacing(2),
}));

const StyledHeaderActions = styled('div')(({ theme }) => ({
const StyledCardFooter = styled(Box)(({ theme }) => ({
padding: theme.spacing(0, 2),
display: 'flex',
background: theme.palette.envAccordion.expanded,
boxShadow: theme.boxShadows.accordionFooter,
alignItems: 'center',
color: theme.palette.text.secondary,
fontSize: theme.fontSizes.smallBody,
justifyContent: 'space-between',
borderTop: `1px solid ${theme.palette.divider}`,
minHeight: theme.spacing(6.25),
}));

const StyledDescription = styled('p')(({ theme }) => ({
const StyledCardFooterSpan = styled('span')(({ theme }) => ({
fontSize: theme.fontSizes.smallerBody,
color: theme.palette.text.secondary,
fontSize: theme.fontSizes.smallBody,
marginTop: theme.spacing(1),
marginBottom: theme.spacing(4),
textWrap: 'nowrap',
}));

const StyledCounterDescription = styled('span')(({ theme }) => ({
color: theme.palette.text.secondary,
marginLeft: theme.spacing(1),
const StyledAvatarComponent = styled(AvatarComponent)(({ theme }) => ({
height: theme.spacing(2.5),
width: theme.spacing(2.5),
marginLeft: theme.spacing(-0.75),
}));

const ProjectBadgeContainer = styled('div')(({ theme }) => ({
maxWidth: '50%',
const StyledProjectsTooltip = styled(Box)(({ theme }) => ({
display: 'flex',
justifyContent: 'flex-end',
gap: theme.spacing(0.5),
flexWrap: 'wrap',
}));

const InfoBadgeDescription = styled('span')(({ theme }) => ({
display: 'flex',
color: theme.palette.text.secondary,
alignItems: 'center',
flexDirection: 'column',
gap: theme.spacing(1),
fontSize: theme.fontSizes.smallBody,
}));

const ProjectNameBadge = styled(Badge)({
wordBreak: 'break-word',
const StyledProjectBadge = styled(Badge)({
cursor: 'pointer',
});

interface IGroupCardProps {
Expand All @@ -104,91 +135,113 @@ export const GroupCard = ({
}: IGroupCardProps) => {
const navigate = useNavigate();

const { searchQuery } = useSearchHighlightContext();

const {
settings: { enabled: scimEnabled },
} = useScimSettings();
const isScimGroup = scimEnabled && Boolean(group.scimId);

return (
<>
<StyledLink key={group.id} to={`/admin/groups/${group.id}`}>
<StyledGroupCard>
<StyledTitleRow>
<StyledHeaderTitle>{group.name}</StyledHeaderTitle>
<StyledHeaderActions>
<StyledCardLink to={`/admin/groups/${group.id}`}>
<StyledCard>
<StyledCardBody>
<StyledCardBodyHeader>
<StyledCardIconContainer>
<GroupsIcon />
</StyledCardIconContainer>
<Truncator
title={group.name}
arrow
component={StyledCardTitle}
>
<Highlighter search={searchQuery}>
{group.name}
</Highlighter>
</Truncator>
<ConditionallyRender
condition={Boolean(group.rootRole)}
show={
<RoleBadge
roleId={group.rootRole!}
hideIcon
/>
}
/>
<GroupCardActions
groupId={group.id}
onEditUsers={() => onEditUsers(group)}
onRemove={() => onRemoveGroup(group)}
isScimGroup={isScimGroup}
/>
</StyledHeaderActions>
</StyledTitleRow>
<ConditionallyRender
condition={Boolean(group.rootRole)}
show={
<InfoBadgeDescription>
<p>Root role:</p>
<RoleBadge roleId={group.rootRole!} />
</InfoBadgeDescription>
}
/>

<StyledDescription>{group.description}</StyledDescription>
<StyledBottomRow>
</StyledCardBodyHeader>
<ConditionallyRender
condition={group.users?.length > 0}
show={<AvatarGroup users={group.users} />}
condition={Boolean(group.description)}
show={
<Truncator
lines={2}
title={group.description}
arrow
component={StyledCardDescription}
>
<Highlighter search={searchQuery}>
{group.description}
</Highlighter>
</Truncator>
}
/>
</StyledCardBody>
<StyledCardFooter>
<ConditionallyRender
condition={group.users.length > 0}
show={
<AvatarGroup
users={group.users}
AvatarComponent={StyledAvatarComponent}
/>
}
elseShow={
<StyledCounterDescription>
This group has no users.
</StyledCounterDescription>
<StyledCardFooterSpan>
This group has no users
</StyledCardFooterSpan>
}
/>
<ProjectBadgeContainer>
<ConditionallyRender
condition={group.projects.length > 0}
show={group.projects.map((project) => (
<Tooltip
key={project}
title='View project'
arrow
placement='bottom-end'
describeChild
>
<ProjectNameBadge
onClick={(e) => {
e.preventDefault();
navigate(
`/projects/${project}/settings/access`,
);
}}
color='secondary'
icon={<TopicOutlinedIcon />}
>
{project}
</ProjectNameBadge>
</Tooltip>
))}
elseShow={
<ConditionallyRender
condition={!group.rootRole}
show={
<Tooltip
title='This group is not used in any project'
arrow
describeChild
>
<Badge>Not used</Badge>
</Tooltip>
}
/>
}
/>
</ProjectBadgeContainer>
</StyledBottomRow>
</StyledGroupCard>
</StyledLink>
<ConditionallyRender
condition={group.projects.length > 0}
show={
<TooltipLink
component='span'
tooltip={
<StyledProjectsTooltip>
{group.projects.map((project) => (
<StyledProjectBadge
key={project}
onClick={(e) => {
e.preventDefault();
navigate(
`/projects/${project}/settings/access`,
);
}}
color='secondary'
icon={<TopicOutlinedIcon />}
>
{project}
</StyledProjectBadge>
))}
</StyledProjectsTooltip>
}
>
<StyledCardFooterSpan>
{group.projects.length} project
{group.projects.length !== 1 && 's'}
</StyledCardFooterSpan>
</TooltipLink>
}
/>
</StyledCardFooter>
</StyledCard>
</StyledCardLink>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ import { Link } from 'react-router-dom';
import { scimGroupTooltip } from 'component/admin/groups/group-constants';

const StyledActions = styled('div')(({ theme }) => ({
margin: theme.spacing(-1),
marginLeft: theme.spacing(-0.5),
display: 'flex',
justifyContent: 'center',
transform: 'translate3d(8px, -6px, 0)',
alignItems: 'center',
}));

const StyledPopover = styled(Popover)(({ theme }) => ({
Expand Down Expand Up @@ -51,7 +53,7 @@ export const GroupCardActions: FC<IGroupCardActions> = ({
setAnchorEl(null);
};

const id = `feature-${groupId}-actions`;
const id = `group-${groupId}-actions`;
const menuId = `${id}-menu`;

return (
Expand All @@ -74,6 +76,7 @@ export const GroupCardActions: FC<IGroupCardActions> = ({
aria-expanded={open ? 'true' : undefined}
onClick={handleClick}
type='button'
size='small'
>
<MoreVert />
</IconButton>
Expand Down
Loading

0 comments on commit 51c3847

Please sign in to comment.