Skip to content

Commit 8e67594

Browse files
chore: change access overview to lists in accordions (#9535)
https://linear.app/unleash/issue/2-3343/accordions-not-a-must-have https://linear.app/unleash/issue/2-3345/indicator-of-how-many-permissions Changes our Access Overview from tables to lists in accordions. Also includes the total permissions in the accordion summary. Looking at the designs it seems like lists would make the most sense, both visually and in terms of semantics. This will also allow us to group the permissions both visually and semantically in a future task. ![image](https://github.com/user-attachments/assets/0692b4f3-0fc5-482c-b963-c731bf5113f5) ### Update Also improved our project permissions label. ![image](https://github.com/user-attachments/assets/cbb2c298-1f85-4a78-b3ff-3140c567f756) ![image](https://github.com/user-attachments/assets/f3d5c623-4013-4a47-a4b1-5af2e63cb01e) --------- Co-authored-by: Gastón Fournier <[email protected]>
1 parent 872162e commit 8e67594

File tree

4 files changed

+174
-97
lines changed

4 files changed

+174
-97
lines changed

frontend/src/component/admin/users/AccessOverview/AccessOverview.tsx

+23-16
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { PageContent } from 'component/common/PageContent/PageContent';
22
import { PageHeader } from 'component/common/PageHeader/PageHeader';
33
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
44
import useUserInfo from 'hooks/api/getters/useUserInfo/useUserInfo';
5-
import { AccessOverviewTable } from './AccessOverviewTable';
65
import { styled, useMediaQuery } from '@mui/material';
76
import { useEffect, useState } from 'react';
87
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
@@ -12,6 +11,7 @@ import { StringParam, useQueryParams } from 'use-query-params';
1211
import useProjects from 'hooks/api/getters/useProjects/useProjects';
1312
import { AccessOverviewSelect } from './AccessOverviewSelect';
1413
import { useUserAccessOverview } from 'hooks/api/getters/useUserAccessOverview/useUserAccessOverview';
14+
import { AccessOverviewAccordion } from './AccessOverviewAccordion/AccessOverviewAccordion';
1515

1616
const StyledActionsContainer = styled('div')(({ theme }) => ({
1717
display: 'flex',
@@ -24,8 +24,10 @@ const StyledActionsContainer = styled('div')(({ theme }) => ({
2424
},
2525
}));
2626

27-
const StyledTitle = styled('h2')(({ theme }) => ({
28-
margin: theme.spacing(2, 0),
27+
const StyledAccessOverviewContainer = styled('div')(({ theme }) => ({
28+
display: 'flex',
29+
flexDirection: 'column',
30+
gap: theme.spacing(2),
2931
}));
3032

3133
export const AccessOverview = () => {
@@ -104,19 +106,24 @@ export const AccessOverview = () => {
104106
</PageHeader>
105107
}
106108
>
107-
<StyledTitle>
108-
Root permissions for role {rootRole?.name}
109-
</StyledTitle>
110-
<AccessOverviewTable permissions={overview?.root ?? []} />
111-
<StyledTitle>
112-
Project permissions for project {project} with project roles [
113-
{projectRoles?.map((role: any) => role.name).join(', ')}]
114-
</StyledTitle>
115-
<AccessOverviewTable permissions={overview?.project ?? []} />
116-
<StyledTitle>
117-
Environment permissions for environment {environment}
118-
</StyledTitle>
119-
<AccessOverviewTable permissions={overview?.environment ?? []} />
109+
<StyledAccessOverviewContainer>
110+
<AccessOverviewAccordion permissions={overview?.root ?? []}>
111+
Root permissions for role {rootRole?.name}
112+
</AccessOverviewAccordion>
113+
<AccessOverviewAccordion permissions={overview?.project ?? []}>
114+
Project permissions
115+
{project
116+
? ` for project ${project}${projectRoles?.length ? ` with project role${projectRoles.length !== 1 ? 's' : ''} ${projectRoles?.map((role: any) => role.name).join(', ')}` : ''}`
117+
: ''}
118+
</AccessOverviewAccordion>
119+
{environment && (
120+
<AccessOverviewAccordion
121+
permissions={overview?.environment ?? []}
122+
>
123+
Environment permissions for {environment}
124+
</AccessOverviewAccordion>
125+
)}
126+
</StyledAccessOverviewContainer>
120127
</PageContent>
121128
);
122129
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import ExpandMore from '@mui/icons-material/ExpandMore';
2+
import {
3+
Accordion,
4+
AccordionDetails,
5+
AccordionSummary,
6+
styled,
7+
} from '@mui/material';
8+
import type { IAccessOverviewPermission } from 'interfaces/permissions';
9+
import { AccessOverviewList } from './AccessOverviewList';
10+
11+
const StyledAccordion = styled(Accordion)(({ theme }) => ({
12+
border: `1px solid ${theme.palette.divider}`,
13+
borderRadius: theme.shape.borderRadiusLarge,
14+
overflow: 'hidden',
15+
boxShadow: 'none',
16+
margin: 0,
17+
'&:before': {
18+
display: 'none',
19+
},
20+
}));
21+
22+
const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
23+
backgroundColor: theme.palette.background.elevation1,
24+
'& .MuiAccordionSummary-content': {
25+
justifyContent: 'space-between',
26+
alignItems: 'center',
27+
minHeight: '30px',
28+
},
29+
}));
30+
31+
const StyledTitleContainer = styled('div')(({ theme }) => ({
32+
display: 'flex',
33+
alignItems: 'start',
34+
flexDirection: 'column',
35+
gap: theme.spacing(0.5),
36+
}));
37+
38+
const StyledTitle = styled('span')(({ theme }) => ({
39+
fontWeight: theme.fontWeight.bold,
40+
}));
41+
42+
const StyledSecondaryLabel = styled('span')(({ theme }) => ({
43+
color: theme.palette.text.secondary,
44+
fontSize: theme.fontSizes.smallBody,
45+
marginRight: theme.spacing(1),
46+
}));
47+
48+
const StyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({
49+
padding: 0,
50+
}));
51+
52+
interface IAccessAccordionProps {
53+
permissions: IAccessOverviewPermission[];
54+
children: React.ReactNode;
55+
}
56+
57+
export const AccessOverviewAccordion = ({
58+
permissions,
59+
children,
60+
}: IAccessAccordionProps) => (
61+
<StyledAccordion>
62+
<StyledAccordionSummary expandIcon={<ExpandMore />}>
63+
<StyledTitleContainer>
64+
<StyledTitle>{children}</StyledTitle>
65+
</StyledTitleContainer>
66+
<StyledSecondaryLabel>
67+
{
68+
permissions.filter(({ hasPermission }) => hasPermission)
69+
.length
70+
}
71+
/{permissions.length} permissions
72+
</StyledSecondaryLabel>
73+
</StyledAccordionSummary>
74+
<StyledAccordionDetails>
75+
<AccessOverviewList permissions={permissions} />
76+
</StyledAccordionDetails>
77+
</StyledAccordion>
78+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import Check from '@mui/icons-material/Check';
2+
import Close from '@mui/icons-material/Close';
3+
import { Box, styled } from '@mui/material';
4+
import type { IAccessOverviewPermission } from 'interfaces/permissions';
5+
6+
const StyledList = styled('ul')(({ theme }) => ({
7+
listStyle: 'none',
8+
padding: 0,
9+
margin: 0,
10+
fontSize: theme.fontSizes.smallBody,
11+
'& li': {
12+
display: 'flex',
13+
justifyContent: 'space-between',
14+
padding: theme.spacing(2),
15+
'&:not(:last-child)': {
16+
borderBottom: `1px solid ${theme.palette.divider}`,
17+
},
18+
},
19+
}));
20+
21+
const StyledPermissionStatus = styled('div', {
22+
shouldForwardProp: (prop) => prop !== 'hasPermission',
23+
})<{ hasPermission: boolean }>(({ theme, hasPermission }) => ({
24+
display: 'flex',
25+
gap: theme.spacing(1),
26+
alignItems: 'center',
27+
width: theme.spacing(17.5),
28+
color: hasPermission
29+
? theme.palette.text.primary
30+
: theme.palette.text.secondary,
31+
'& > svg': {
32+
color: hasPermission
33+
? theme.palette.success.main
34+
: theme.palette.error.main,
35+
},
36+
}));
37+
38+
export const AccessOverviewList = ({
39+
permissions,
40+
}: {
41+
permissions: IAccessOverviewPermission[];
42+
}) => {
43+
return (
44+
<Box sx={{ maxHeight: 500, overflow: 'auto' }}>
45+
<StyledList>
46+
{permissions.map((permission) => (
47+
<li key={permission.name}>
48+
<div>{permission.displayName}</div>
49+
<PermissionStatus
50+
hasPermission={permission.hasPermission}
51+
/>
52+
</li>
53+
))}
54+
</StyledList>
55+
</Box>
56+
);
57+
};
58+
59+
const PermissionStatus = ({ hasPermission }: { hasPermission: boolean }) => (
60+
<StyledPermissionStatus hasPermission={hasPermission}>
61+
{hasPermission ? (
62+
<>
63+
<Check />
64+
Has permission
65+
</>
66+
) : (
67+
<>
68+
<Close />
69+
No permission
70+
</>
71+
)}
72+
</StyledPermissionStatus>
73+
);

frontend/src/component/admin/users/AccessOverview/AccessOverviewTable.tsx

-81
This file was deleted.

0 commit comments

Comments
 (0)