diff --git a/frontend/src/component/environments/EnvironmentTable/EnvironmentActionCell/EnvironmentActionCell.tsx b/frontend/src/component/environments/EnvironmentTable/EnvironmentActionCell/EnvironmentActionCell.tsx index 2a2dab69b9..847c3b5c35 100644 --- a/frontend/src/component/environments/EnvironmentTable/EnvironmentActionCell/EnvironmentActionCell.tsx +++ b/frontend/src/component/environments/EnvironmentTable/EnvironmentActionCell/EnvironmentActionCell.tsx @@ -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)} /> - navigate(`/environments/${environment.name}`)} - > - - - setDeleteModal(true)} - > - - + navigate(`/environments/${environment.name}`)} + onClone={() => console.log('TODO: CLONE')} + onDelete={() => setDeleteModal(true)} + /> ({ + 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); + + const open = Boolean(anchorEl); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + const id = `environment-${environment.name}-actions`; + const menuId = `${id}-menu`; + + return ( + <> + + + + + + + + + {({ hasAccess }) => ( + { + onEdit(); + handleClose(); + }} + disabled={!hasAccess || environment.protected} + > + + + + + + Edit + + + + )} + + + {({ hasAccess }) => ( + { + onClone(); + handleClose(); + }} + disabled={!hasAccess} + > + + + + + + Clone + + + + )} + + } + /> + + {({ hasAccess }) => ( + { + onDelete(); + handleClose(); + }} + disabled={!hasAccess || environment.protected} + > + + + + + + Delete + + + + )} + + + + + ); +}; diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index d63e4db00c..895fd9e0ac 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -44,6 +44,7 @@ export interface IFlags { personalAccessTokens?: boolean; syncSSOGroups?: boolean; suggestChanges?: boolean; + cloneEnvironment?: boolean; } export interface IVersionInfo { diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index df078cf2c6..7559a12f89 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -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, diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 95a69bba02..e8af3c9538 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -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; } diff --git a/src/server-dev.ts b/src/server-dev.ts index 35d9a41990..4d7673a8fa 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -41,6 +41,7 @@ process.nextTick(async () => { personalAccessTokens: true, syncSSOGroups: true, suggestChanges: true, + cloneEnvironment: true, }, }, authentication: { diff --git a/src/test/config/test-config.ts b/src/test/config/test-config.ts index 33a7ca50e6..b2618d634a 100644 --- a/src/test/config/test-config.ts +++ b/src/test/config/test-config.ts @@ -29,6 +29,7 @@ export function createTestConfig(config?: IUnleashOptions): IUnleashConfig { batchMetrics: true, personalAccessTokens: true, syncSSOGroups: true, + cloneEnvironment: true, }, }, };