mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-07 01:16:28 +02: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 { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
|
||||||
import {
|
import { UPDATE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions';
|
||||||
DELETE_ENVIRONMENT,
|
|
||||||
UPDATE_ENVIRONMENT,
|
|
||||||
} from 'component/providers/AccessProvider/permissions';
|
|
||||||
import { Edit, Delete } from '@mui/icons-material';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { IEnvironment } from 'interfaces/environments';
|
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 { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
|
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
|
||||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
import { EnvironmentActionCellPopover } from './EnvironmentActionCellPopover/EnvironmentActionCellPopover';
|
||||||
|
|
||||||
interface IEnvironmentTableActionsProps {
|
interface IEnvironmentTableActionsProps {
|
||||||
environment: IEnvironment;
|
environment: IEnvironment;
|
||||||
@ -103,32 +99,12 @@ export const EnvironmentActionCell = ({
|
|||||||
onClick={() => setToggleModal(true)}
|
onClick={() => setToggleModal(true)}
|
||||||
/>
|
/>
|
||||||
<ActionCell.Divider />
|
<ActionCell.Divider />
|
||||||
<PermissionIconButton
|
<EnvironmentActionCellPopover
|
||||||
permission={UPDATE_ENVIRONMENT}
|
environment={environment}
|
||||||
disabled={environment.protected}
|
onEdit={() => navigate(`/environments/${environment.name}`)}
|
||||||
size="large"
|
onClone={() => console.log('TODO: CLONE')}
|
||||||
tooltipProps={{
|
onDelete={() => setDeleteModal(true)}
|
||||||
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>
|
|
||||||
<EnvironmentDeleteConfirm
|
<EnvironmentDeleteConfirm
|
||||||
env={environment}
|
env={environment}
|
||||||
setDeldialogue={setDeleteModal}
|
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;
|
personalAccessTokens?: boolean;
|
||||||
syncSSOGroups?: boolean;
|
syncSSOGroups?: boolean;
|
||||||
suggestChanges?: boolean;
|
suggestChanges?: boolean;
|
||||||
|
cloneEnvironment?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IVersionInfo {
|
export interface IVersionInfo {
|
||||||
|
@ -69,6 +69,7 @@ exports[`should create default config 1`] = `
|
|||||||
"ENABLE_DARK_MODE_SUPPORT": false,
|
"ENABLE_DARK_MODE_SUPPORT": false,
|
||||||
"anonymiseEventLog": false,
|
"anonymiseEventLog": false,
|
||||||
"batchMetrics": false,
|
"batchMetrics": false,
|
||||||
|
"cloneEnvironment": false,
|
||||||
"embedProxy": false,
|
"embedProxy": false,
|
||||||
"embedProxyFrontend": false,
|
"embedProxyFrontend": false,
|
||||||
"personalAccessTokens": false,
|
"personalAccessTokens": false,
|
||||||
@ -82,6 +83,7 @@ exports[`should create default config 1`] = `
|
|||||||
"ENABLE_DARK_MODE_SUPPORT": false,
|
"ENABLE_DARK_MODE_SUPPORT": false,
|
||||||
"anonymiseEventLog": false,
|
"anonymiseEventLog": false,
|
||||||
"batchMetrics": false,
|
"batchMetrics": false,
|
||||||
|
"cloneEnvironment": false,
|
||||||
"embedProxy": false,
|
"embedProxy": false,
|
||||||
"embedProxyFrontend": false,
|
"embedProxyFrontend": false,
|
||||||
"personalAccessTokens": false,
|
"personalAccessTokens": false,
|
||||||
|
@ -34,6 +34,10 @@ export const defaultExperimentalOptions = {
|
|||||||
process.env.UNLEASH_EXPERIMENTAL_RESPONSE_TIME_WITH_APP_NAME,
|
process.env.UNLEASH_EXPERIMENTAL_RESPONSE_TIME_WITH_APP_NAME,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
cloneEnvironment: parseEnvVarBoolean(
|
||||||
|
process.env.UNLEASH_EXPERIMENTAL_CLONE_ENVIRONMENT,
|
||||||
|
false,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
externalResolver: { isEnabled: (): boolean => false },
|
externalResolver: { isEnabled: (): boolean => false },
|
||||||
};
|
};
|
||||||
@ -48,6 +52,7 @@ export interface IExperimentalOptions {
|
|||||||
anonymiseEventLog?: boolean;
|
anonymiseEventLog?: boolean;
|
||||||
personalAccessTokens?: boolean;
|
personalAccessTokens?: boolean;
|
||||||
syncSSOGroups?: boolean;
|
syncSSOGroups?: boolean;
|
||||||
|
cloneEnvironment?: boolean;
|
||||||
};
|
};
|
||||||
externalResolver: IExternalFlagResolver;
|
externalResolver: IExternalFlagResolver;
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ process.nextTick(async () => {
|
|||||||
personalAccessTokens: true,
|
personalAccessTokens: true,
|
||||||
syncSSOGroups: true,
|
syncSSOGroups: true,
|
||||||
suggestChanges: true,
|
suggestChanges: true,
|
||||||
|
cloneEnvironment: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
authentication: {
|
authentication: {
|
||||||
|
@ -29,6 +29,7 @@ export function createTestConfig(config?: IUnleashOptions): IUnleashConfig {
|
|||||||
batchMetrics: true,
|
batchMetrics: true,
|
||||||
personalAccessTokens: true,
|
personalAccessTokens: true,
|
||||||
syncSSOGroups: true,
|
syncSSOGroups: true,
|
||||||
|
cloneEnvironment: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user