mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
chore: users actions menu (#9525)
https://linear.app/unleash/issue/2-3342/new-entrance-point-create-dot-dot-dot-menu-instead-of-icons Adds a new users actions menu. Should this change be behind a flag? I'm leaning towards no, but if you think otherwise let me know. ### Previous  ### After  ### If user is SCIM-managed 
This commit is contained in:
parent
1b7f91cd4b
commit
8ab24fd3bf
@ -1,101 +1,145 @@
|
||||
import Delete from '@mui/icons-material/Delete';
|
||||
import Edit from '@mui/icons-material/Edit';
|
||||
import Key from '@mui/icons-material/Key';
|
||||
import Lock from '@mui/icons-material/Lock';
|
||||
import LockReset from '@mui/icons-material/LockReset';
|
||||
import { Box, styled } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import type { VFC } from 'react';
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import {
|
||||
IconButton,
|
||||
ListItemText,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Popover,
|
||||
styled,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
|
||||
const StyledBox = styled(Box)(() => ({
|
||||
const StyledActions = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
margin: theme.spacing(-1),
|
||||
marginLeft: theme.spacing(-0.5),
|
||||
}));
|
||||
|
||||
const StyledPopover = styled(Popover)(({ theme }) => ({
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
padding: theme.spacing(1, 1.5),
|
||||
}));
|
||||
|
||||
interface IUsersActionsCellProps {
|
||||
onEdit: (event: React.SyntheticEvent) => void;
|
||||
onViewAccess?: (event: React.SyntheticEvent) => void;
|
||||
onChangePassword: (event: React.SyntheticEvent) => void;
|
||||
onResetPassword: (event: React.SyntheticEvent) => void;
|
||||
onDelete: (event: React.SyntheticEvent) => void;
|
||||
onEdit: () => void;
|
||||
onViewAccess?: () => void;
|
||||
onChangePassword: () => void;
|
||||
onResetPassword: () => void;
|
||||
onDelete: () => void;
|
||||
isScimUser?: boolean;
|
||||
userId: number;
|
||||
}
|
||||
|
||||
export const UsersActionsCell: VFC<IUsersActionsCellProps> = ({
|
||||
export const UsersActionsCell = ({
|
||||
onEdit,
|
||||
onViewAccess,
|
||||
onChangePassword,
|
||||
onResetPassword,
|
||||
onDelete,
|
||||
isScimUser,
|
||||
}) => {
|
||||
userId,
|
||||
}: IUsersActionsCellProps) => {
|
||||
const [anchorEl, setAnchorEl] = useState<Element | null>(null);
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const id = `user-${userId}-actions`;
|
||||
const menuId = `${id}-menu`;
|
||||
|
||||
return (
|
||||
<StyledActions>
|
||||
<Tooltip title='User actions' arrow describeChild>
|
||||
<IconButton
|
||||
id={id}
|
||||
aria-controls={open ? 'actions-menu' : undefined}
|
||||
aria-haspopup='true'
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
onClick={handleClick}
|
||||
type='button'
|
||||
size='small'
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<StyledPopover
|
||||
id={menuId}
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||
disableScrollLock={true}
|
||||
>
|
||||
<MenuList aria-labelledby={id}>
|
||||
<UserAction onClick={onEdit} isScimUser={isScimUser}>
|
||||
Edit user
|
||||
</UserAction>
|
||||
{onViewAccess && (
|
||||
<UserAction onClick={onViewAccess}>
|
||||
Access overview
|
||||
</UserAction>
|
||||
)}
|
||||
<UserAction
|
||||
onClick={() => {
|
||||
onChangePassword();
|
||||
handleClose();
|
||||
}}
|
||||
isScimUser={isScimUser}
|
||||
>
|
||||
Change password
|
||||
</UserAction>
|
||||
<UserAction
|
||||
onClick={() => {
|
||||
onResetPassword();
|
||||
handleClose();
|
||||
}}
|
||||
isScimUser={isScimUser}
|
||||
>
|
||||
Reset password
|
||||
</UserAction>
|
||||
<UserAction
|
||||
onClick={() => {
|
||||
onDelete();
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
Remove user
|
||||
</UserAction>
|
||||
</MenuList>
|
||||
</StyledPopover>
|
||||
</StyledActions>
|
||||
);
|
||||
};
|
||||
|
||||
interface IUserActionProps {
|
||||
onClick: () => void;
|
||||
isScimUser?: boolean;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const UserAction = ({ onClick, isScimUser, children }: IUserActionProps) => {
|
||||
const scimTooltip =
|
||||
'This user is managed by your SCIM provider and cannot be changed manually';
|
||||
|
||||
return (
|
||||
<StyledBox>
|
||||
<PermissionIconButton
|
||||
data-loading
|
||||
onClick={onEdit}
|
||||
permission={ADMIN}
|
||||
tooltipProps={{
|
||||
title: isScimUser ? scimTooltip : 'Edit user',
|
||||
}}
|
||||
disabled={isScimUser}
|
||||
>
|
||||
<Edit />
|
||||
</PermissionIconButton>
|
||||
|
||||
<ConditionallyRender
|
||||
condition={Boolean(onViewAccess)}
|
||||
show={
|
||||
<PermissionIconButton
|
||||
data-loading
|
||||
onClick={onViewAccess!}
|
||||
permission={ADMIN}
|
||||
tooltipProps={{
|
||||
title: 'Access matrix',
|
||||
}}
|
||||
>
|
||||
<Key />
|
||||
</PermissionIconButton>
|
||||
}
|
||||
/>
|
||||
|
||||
<PermissionIconButton
|
||||
data-loading
|
||||
onClick={onChangePassword}
|
||||
permission={ADMIN}
|
||||
tooltipProps={{
|
||||
title: isScimUser ? scimTooltip : 'Change password',
|
||||
}}
|
||||
disabled={isScimUser}
|
||||
>
|
||||
<Lock />
|
||||
</PermissionIconButton>
|
||||
<PermissionIconButton
|
||||
data-loading
|
||||
onClick={onResetPassword}
|
||||
permission={ADMIN}
|
||||
tooltipProps={{
|
||||
title: isScimUser ? scimTooltip : 'Reset password',
|
||||
}}
|
||||
disabled={isScimUser}
|
||||
>
|
||||
<LockReset />
|
||||
</PermissionIconButton>
|
||||
<PermissionIconButton
|
||||
data-loading
|
||||
onClick={onDelete}
|
||||
permission={ADMIN}
|
||||
tooltipProps={{
|
||||
title: 'Remove user',
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</PermissionIconButton>
|
||||
</StyledBox>
|
||||
<Tooltip title={isScimUser ? scimTooltip : ''} arrow placement='left'>
|
||||
<div>
|
||||
<MenuItem onClick={onClick} disabled={isScimUser}>
|
||||
<ListItemText>
|
||||
<Typography variant='body2'>{children}</Typography>
|
||||
</ListItemText>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type React from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
|
||||
import ChangePassword from './ChangePassword/ChangePassword';
|
||||
@ -82,23 +81,17 @@ const UsersList = () => {
|
||||
setDelUser(undefined);
|
||||
};
|
||||
|
||||
const openDelDialog =
|
||||
(user: IUser) => (e: React.SyntheticEvent<Element, Event>) => {
|
||||
e.preventDefault();
|
||||
setDelDialog(true);
|
||||
setDelUser(user);
|
||||
};
|
||||
const openPwDialog =
|
||||
(user: IUser) => (e: React.SyntheticEvent<Element, Event>) => {
|
||||
e.preventDefault();
|
||||
setPwDialog({ open: true, user });
|
||||
};
|
||||
const openDelDialog = (user: IUser) => () => {
|
||||
setDelDialog(true);
|
||||
setDelUser(user);
|
||||
};
|
||||
const openPwDialog = (user: IUser) => () => {
|
||||
setPwDialog({ open: true, user });
|
||||
};
|
||||
|
||||
const openResetPwDialog =
|
||||
(user: IUser) => (e: React.SyntheticEvent<Element, Event>) => {
|
||||
e.preventDefault();
|
||||
setResetPwDialog({ open: true, user });
|
||||
};
|
||||
const openResetPwDialog = (user: IUser) => () => {
|
||||
setResetPwDialog({ open: true, user });
|
||||
};
|
||||
|
||||
const closePwDialog = () => {
|
||||
setPwDialog({ open: false });
|
||||
@ -215,7 +208,7 @@ const UsersList = () => {
|
||||
sortType: 'boolean',
|
||||
},
|
||||
{
|
||||
Header: 'Actions',
|
||||
Header: '',
|
||||
id: 'Actions',
|
||||
align: 'center',
|
||||
Cell: ({
|
||||
@ -238,9 +231,10 @@ const UsersList = () => {
|
||||
onResetPassword={openResetPwDialog(user)}
|
||||
onDelete={openDelDialog(user)}
|
||||
isScimUser={scimEnabled && Boolean(user.scimId)}
|
||||
userId={user.id}
|
||||
/>
|
||||
),
|
||||
width: userAccessUIEnabled ? 240 : 200,
|
||||
width: 80,
|
||||
disableSortBy: true,
|
||||
},
|
||||
// Always hidden -- for search
|
||||
|
Loading…
Reference in New Issue
Block a user