Skip to content

Commit 8ab24fd

Browse files
authored
1 parent 1b7f91c commit 8ab24fd

File tree

2 files changed

+135
-97
lines changed

2 files changed

+135
-97
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,145 @@
1-
import Delete from '@mui/icons-material/Delete';
2-
import Edit from '@mui/icons-material/Edit';
3-
import Key from '@mui/icons-material/Key';
4-
import Lock from '@mui/icons-material/Lock';
5-
import LockReset from '@mui/icons-material/LockReset';
6-
import { Box, styled } from '@mui/material';
7-
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
8-
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
9-
import { ADMIN } from 'component/providers/AccessProvider/permissions';
10-
import type { VFC } from 'react';
1+
import MoreVertIcon from '@mui/icons-material/MoreVert';
2+
import {
3+
IconButton,
4+
ListItemText,
5+
MenuItem,
6+
MenuList,
7+
Popover,
8+
styled,
9+
Tooltip,
10+
Typography,
11+
} from '@mui/material';
12+
import { useState } from 'react';
1113

12-
const StyledBox = styled(Box)(() => ({
14+
const StyledActions = styled('div')(({ theme }) => ({
1315
display: 'flex',
1416
justifyContent: 'center',
17+
alignItems: 'center',
18+
margin: theme.spacing(-1),
19+
marginLeft: theme.spacing(-0.5),
20+
}));
21+
22+
const StyledPopover = styled(Popover)(({ theme }) => ({
23+
borderRadius: theme.shape.borderRadiusLarge,
24+
padding: theme.spacing(1, 1.5),
1525
}));
1626

1727
interface IUsersActionsCellProps {
18-
onEdit: (event: React.SyntheticEvent) => void;
19-
onViewAccess?: (event: React.SyntheticEvent) => void;
20-
onChangePassword: (event: React.SyntheticEvent) => void;
21-
onResetPassword: (event: React.SyntheticEvent) => void;
22-
onDelete: (event: React.SyntheticEvent) => void;
28+
onEdit: () => void;
29+
onViewAccess?: () => void;
30+
onChangePassword: () => void;
31+
onResetPassword: () => void;
32+
onDelete: () => void;
2333
isScimUser?: boolean;
34+
userId: number;
2435
}
2536

26-
export const UsersActionsCell: VFC<IUsersActionsCellProps> = ({
37+
export const UsersActionsCell = ({
2738
onEdit,
2839
onViewAccess,
2940
onChangePassword,
3041
onResetPassword,
3142
onDelete,
3243
isScimUser,
33-
}) => {
34-
const scimTooltip =
35-
'This user is managed by your SCIM provider and cannot be changed manually';
44+
userId,
45+
}: IUsersActionsCellProps) => {
46+
const [anchorEl, setAnchorEl] = useState<Element | null>(null);
47+
48+
const open = Boolean(anchorEl);
49+
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
50+
setAnchorEl(event.currentTarget);
51+
};
52+
const handleClose = () => {
53+
setAnchorEl(null);
54+
};
55+
56+
const id = `user-${userId}-actions`;
57+
const menuId = `${id}-menu`;
3658

3759
return (
38-
<StyledBox>
39-
<PermissionIconButton
40-
data-loading
41-
onClick={onEdit}
42-
permission={ADMIN}
43-
tooltipProps={{
44-
title: isScimUser ? scimTooltip : 'Edit user',
45-
}}
46-
disabled={isScimUser}
60+
<StyledActions>
61+
<Tooltip title='User actions' arrow describeChild>
62+
<IconButton
63+
id={id}
64+
aria-controls={open ? 'actions-menu' : undefined}
65+
aria-haspopup='true'
66+
aria-expanded={open ? 'true' : undefined}
67+
onClick={handleClick}
68+
type='button'
69+
size='small'
70+
>
71+
<MoreVertIcon />
72+
</IconButton>
73+
</Tooltip>
74+
<StyledPopover
75+
id={menuId}
76+
anchorEl={anchorEl}
77+
open={open}
78+
onClose={handleClose}
79+
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
80+
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
81+
disableScrollLock={true}
4782
>
48-
<Edit />
49-
</PermissionIconButton>
50-
51-
<ConditionallyRender
52-
condition={Boolean(onViewAccess)}
53-
show={
54-
<PermissionIconButton
55-
data-loading
56-
onClick={onViewAccess!}
57-
permission={ADMIN}
58-
tooltipProps={{
59-
title: 'Access matrix',
83+
<MenuList aria-labelledby={id}>
84+
<UserAction onClick={onEdit} isScimUser={isScimUser}>
85+
Edit user
86+
</UserAction>
87+
{onViewAccess && (
88+
<UserAction onClick={onViewAccess}>
89+
Access overview
90+
</UserAction>
91+
)}
92+
<UserAction
93+
onClick={() => {
94+
onChangePassword();
95+
handleClose();
96+
}}
97+
isScimUser={isScimUser}
98+
>
99+
Change password
100+
</UserAction>
101+
<UserAction
102+
onClick={() => {
103+
onResetPassword();
104+
handleClose();
60105
}}
106+
isScimUser={isScimUser}
61107
>
62-
<Key />
63-
</PermissionIconButton>
64-
}
65-
/>
108+
Reset password
109+
</UserAction>
110+
<UserAction
111+
onClick={() => {
112+
onDelete();
113+
handleClose();
114+
}}
115+
>
116+
Remove user
117+
</UserAction>
118+
</MenuList>
119+
</StyledPopover>
120+
</StyledActions>
121+
);
122+
};
66123

67-
<PermissionIconButton
68-
data-loading
69-
onClick={onChangePassword}
70-
permission={ADMIN}
71-
tooltipProps={{
72-
title: isScimUser ? scimTooltip : 'Change password',
73-
}}
74-
disabled={isScimUser}
75-
>
76-
<Lock />
77-
</PermissionIconButton>
78-
<PermissionIconButton
79-
data-loading
80-
onClick={onResetPassword}
81-
permission={ADMIN}
82-
tooltipProps={{
83-
title: isScimUser ? scimTooltip : 'Reset password',
84-
}}
85-
disabled={isScimUser}
86-
>
87-
<LockReset />
88-
</PermissionIconButton>
89-
<PermissionIconButton
90-
data-loading
91-
onClick={onDelete}
92-
permission={ADMIN}
93-
tooltipProps={{
94-
title: 'Remove user',
95-
}}
96-
>
97-
<Delete />
98-
</PermissionIconButton>
99-
</StyledBox>
124+
interface IUserActionProps {
125+
onClick: () => void;
126+
isScimUser?: boolean;
127+
children: React.ReactNode;
128+
}
129+
130+
const UserAction = ({ onClick, isScimUser, children }: IUserActionProps) => {
131+
const scimTooltip =
132+
'This user is managed by your SCIM provider and cannot be changed manually';
133+
134+
return (
135+
<Tooltip title={isScimUser ? scimTooltip : ''} arrow placement='left'>
136+
<div>
137+
<MenuItem onClick={onClick} disabled={isScimUser}>
138+
<ListItemText>
139+
<Typography variant='body2'>{children}</Typography>
140+
</ListItemText>
141+
</MenuItem>
142+
</div>
143+
</Tooltip>
100144
);
101145
};

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

+13-19
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type React from 'react';
21
import { useMemo, useState } from 'react';
32
import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
43
import ChangePassword from './ChangePassword/ChangePassword';
@@ -82,23 +81,17 @@ const UsersList = () => {
8281
setDelUser(undefined);
8382
};
8483

85-
const openDelDialog =
86-
(user: IUser) => (e: React.SyntheticEvent<Element, Event>) => {
87-
e.preventDefault();
88-
setDelDialog(true);
89-
setDelUser(user);
90-
};
91-
const openPwDialog =
92-
(user: IUser) => (e: React.SyntheticEvent<Element, Event>) => {
93-
e.preventDefault();
94-
setPwDialog({ open: true, user });
95-
};
84+
const openDelDialog = (user: IUser) => () => {
85+
setDelDialog(true);
86+
setDelUser(user);
87+
};
88+
const openPwDialog = (user: IUser) => () => {
89+
setPwDialog({ open: true, user });
90+
};
9691

97-
const openResetPwDialog =
98-
(user: IUser) => (e: React.SyntheticEvent<Element, Event>) => {
99-
e.preventDefault();
100-
setResetPwDialog({ open: true, user });
101-
};
92+
const openResetPwDialog = (user: IUser) => () => {
93+
setResetPwDialog({ open: true, user });
94+
};
10295

10396
const closePwDialog = () => {
10497
setPwDialog({ open: false });
@@ -215,7 +208,7 @@ const UsersList = () => {
215208
sortType: 'boolean',
216209
},
217210
{
218-
Header: 'Actions',
211+
Header: '',
219212
id: 'Actions',
220213
align: 'center',
221214
Cell: ({
@@ -238,9 +231,10 @@ const UsersList = () => {
238231
onResetPassword={openResetPwDialog(user)}
239232
onDelete={openDelDialog(user)}
240233
isScimUser={scimEnabled && Boolean(user.scimId)}
234+
userId={user.id}
241235
/>
242236
),
243-
width: userAccessUIEnabled ? 240 : 200,
237+
width: 80,
244238
disableSortBy: true,
245239
},
246240
// Always hidden -- for search

0 commit comments

Comments
 (0)