mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
feat: convert environment actions to a popover menu, add clone option (#2214)
* feat: convert environment actions to a popover menu, add clone option * add cloneEnviroment feature flag, hide the clone option behind it * fix: update snap
This commit is contained in:
parent
a1dc8339aa
commit
d696863a51
@ -1,9 +1,5 @@
|
||||
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
|
||||
import {
|
||||
DELETE_ENVIRONMENT,
|
||||
UPDATE_ENVIRONMENT,
|
||||
} from 'component/providers/AccessProvider/permissions';
|
||||
import { Edit, Delete } from '@mui/icons-material';
|
||||
import { UPDATE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useState } from 'react';
|
||||
import { IEnvironment } from 'interfaces/environments';
|
||||
@ -15,7 +11,7 @@ import useProjectRolePermissions from 'hooks/api/getters/useProjectRolePermissio
|
||||
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import useToast from 'hooks/useToast';
|
||||
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import { EnvironmentActionCellPopover } from './EnvironmentActionCellPopover/EnvironmentActionCellPopover';
|
||||
|
||||
interface IEnvironmentTableActionsProps {
|
||||
environment: IEnvironment;
|
||||
@ -103,32 +99,12 @@ export const EnvironmentActionCell = ({
|
||||
onClick={() => setToggleModal(true)}
|
||||
/>
|
||||
<ActionCell.Divider />
|
||||
<PermissionIconButton
|
||||
permission={UPDATE_ENVIRONMENT}
|
||||
disabled={environment.protected}
|
||||
size="large"
|
||||
tooltipProps={{
|
||||
title: environment.protected
|
||||
? 'You cannot edit protected environment'
|
||||
: 'Edit environment',
|
||||
}}
|
||||
onClick={() => navigate(`/environments/${environment.name}`)}
|
||||
>
|
||||
<Edit />
|
||||
</PermissionIconButton>
|
||||
<PermissionIconButton
|
||||
permission={DELETE_ENVIRONMENT}
|
||||
disabled={environment.protected}
|
||||
size="large"
|
||||
tooltipProps={{
|
||||
title: environment.protected
|
||||
? 'You cannot delete protected environment'
|
||||
: 'Delete environment',
|
||||
}}
|
||||
onClick={() => setDeleteModal(true)}
|
||||
>
|
||||
<Delete />
|
||||
</PermissionIconButton>
|
||||
<EnvironmentActionCellPopover
|
||||
environment={environment}
|
||||
onEdit={() => navigate(`/environments/${environment.name}`)}
|
||||
onClone={() => console.log('TODO: CLONE')}
|
||||
onDelete={() => setDeleteModal(true)}
|
||||
/>
|
||||
<EnvironmentDeleteConfirm
|
||||
env={environment}
|
||||
setDeldialogue={setDeleteModal}
|
||||
|
@ -0,0 +1,154 @@
|
||||
import {
|
||||
IconButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Popover,
|
||||
styled,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import { useState } from 'react';
|
||||
import { IEnvironment } from 'interfaces/environments';
|
||||
import { PermissionHOC } from 'component/common/PermissionHOC/PermissionHOC';
|
||||
import {
|
||||
ADMIN,
|
||||
DELETE_ENVIRONMENT,
|
||||
UPDATE_ENVIRONMENT,
|
||||
} from 'component/providers/AccessProvider/permissions';
|
||||
import { Delete, Edit, AddToPhotos as CopyIcon } from '@mui/icons-material';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
const StyledMenuList = styled(MenuList)(({ theme }) => ({
|
||||
padding: theme.spacing(1),
|
||||
}));
|
||||
|
||||
const StyledMenuItem = styled(MenuItem)(({ theme }) => ({
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
}));
|
||||
|
||||
interface IEnvironmentActionCellPopoverProps {
|
||||
environment: IEnvironment;
|
||||
onEdit: () => void;
|
||||
onClone: () => void;
|
||||
onDelete: () => void;
|
||||
}
|
||||
|
||||
export const EnvironmentActionCellPopover = ({
|
||||
environment,
|
||||
onEdit,
|
||||
onClone,
|
||||
onDelete,
|
||||
}: IEnvironmentActionCellPopoverProps) => {
|
||||
const { uiConfig } = useUiConfig();
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const id = `environment-${environment.name}-actions`;
|
||||
const menuId = `${id}-menu`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip title="Environment actions" arrow describeChild>
|
||||
<IconButton
|
||||
id={id}
|
||||
aria-controls={open ? menuId : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
onClick={handleClick}
|
||||
type="button"
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Popover
|
||||
id={menuId}
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||
disableScrollLock={true}
|
||||
>
|
||||
<StyledMenuList aria-labelledby={id}>
|
||||
<PermissionHOC permission={UPDATE_ENVIRONMENT}>
|
||||
{({ hasAccess }) => (
|
||||
<StyledMenuItem
|
||||
onClick={() => {
|
||||
onEdit();
|
||||
handleClose();
|
||||
}}
|
||||
disabled={!hasAccess || environment.protected}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<Edit />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
<Typography variant="body2">
|
||||
Edit
|
||||
</Typography>
|
||||
</ListItemText>
|
||||
</StyledMenuItem>
|
||||
)}
|
||||
</PermissionHOC>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(uiConfig.flags.cloneEnvironment)}
|
||||
show={
|
||||
<PermissionHOC permission={ADMIN}>
|
||||
{({ hasAccess }) => (
|
||||
<StyledMenuItem
|
||||
onClick={() => {
|
||||
onClone();
|
||||
handleClose();
|
||||
}}
|
||||
disabled={!hasAccess}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<CopyIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
<Typography variant="body2">
|
||||
Clone
|
||||
</Typography>
|
||||
</ListItemText>
|
||||
</StyledMenuItem>
|
||||
)}
|
||||
</PermissionHOC>
|
||||
}
|
||||
/>
|
||||
<PermissionHOC permission={DELETE_ENVIRONMENT}>
|
||||
{({ hasAccess }) => (
|
||||
<StyledMenuItem
|
||||
onClick={() => {
|
||||
onDelete();
|
||||
handleClose();
|
||||
}}
|
||||
disabled={!hasAccess || environment.protected}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<Delete />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
<Typography variant="body2">
|
||||
Delete
|
||||
</Typography>
|
||||
</ListItemText>
|
||||
</StyledMenuItem>
|
||||
)}
|
||||
</PermissionHOC>
|
||||
</StyledMenuList>
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
};
|
@ -44,6 +44,7 @@ export interface IFlags {
|
||||
personalAccessTokens?: boolean;
|
||||
syncSSOGroups?: boolean;
|
||||
suggestChanges?: boolean;
|
||||
cloneEnvironment?: boolean;
|
||||
}
|
||||
|
||||
export interface IVersionInfo {
|
||||
|
@ -69,6 +69,7 @@ exports[`should create default config 1`] = `
|
||||
"ENABLE_DARK_MODE_SUPPORT": false,
|
||||
"anonymiseEventLog": false,
|
||||
"batchMetrics": false,
|
||||
"cloneEnvironment": false,
|
||||
"embedProxy": false,
|
||||
"embedProxyFrontend": false,
|
||||
"personalAccessTokens": false,
|
||||
@ -82,6 +83,7 @@ exports[`should create default config 1`] = `
|
||||
"ENABLE_DARK_MODE_SUPPORT": false,
|
||||
"anonymiseEventLog": false,
|
||||
"batchMetrics": false,
|
||||
"cloneEnvironment": false,
|
||||
"embedProxy": false,
|
||||
"embedProxyFrontend": false,
|
||||
"personalAccessTokens": false,
|
||||
|
@ -34,6 +34,10 @@ export const defaultExperimentalOptions = {
|
||||
process.env.UNLEASH_EXPERIMENTAL_RESPONSE_TIME_WITH_APP_NAME,
|
||||
false,
|
||||
),
|
||||
cloneEnvironment: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_CLONE_ENVIRONMENT,
|
||||
false,
|
||||
),
|
||||
},
|
||||
externalResolver: { isEnabled: (): boolean => false },
|
||||
};
|
||||
@ -48,6 +52,7 @@ export interface IExperimentalOptions {
|
||||
anonymiseEventLog?: boolean;
|
||||
personalAccessTokens?: boolean;
|
||||
syncSSOGroups?: boolean;
|
||||
cloneEnvironment?: boolean;
|
||||
};
|
||||
externalResolver: IExternalFlagResolver;
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ process.nextTick(async () => {
|
||||
personalAccessTokens: true,
|
||||
syncSSOGroups: true,
|
||||
suggestChanges: true,
|
||||
cloneEnvironment: true,
|
||||
},
|
||||
},
|
||||
authentication: {
|
||||
|
@ -29,6 +29,7 @@ export function createTestConfig(config?: IUnleashOptions): IUnleashConfig {
|
||||
batchMetrics: true,
|
||||
personalAccessTokens: true,
|
||||
syncSSOGroups: true,
|
||||
cloneEnvironment: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user