mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-06 01:15:28 +02:00
Adds the prod guard dialog when enabling/disabling production environment Closes # [1-1386] (https://linear.app/unleash/issue/1-1386/production-guard-modal-for-enablingdisabling-environment-is-gone) https://github.com/Unleash/unleash/assets/104830839/0041bfc8-872b-455c-b4fa-e03cc160c3e9 --------- Signed-off-by: andreas-unleash <andreas@getunleash.ai>
257 lines
9.2 KiB
TypeScript
257 lines
9.2 KiB
TypeScript
import React, { useState, VFC } from 'react';
|
|
import { Box, styled } from '@mui/material';
|
|
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
|
|
import { UPDATE_FEATURE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions';
|
|
import { useOptimisticUpdate } from './hooks/useOptimisticUpdate';
|
|
import { flexRow } from 'themes/themeStyles';
|
|
import { ENVIRONMENT_STRATEGY_ERROR } from 'constants/apiErrors';
|
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
|
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
|
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
|
import useToast from 'hooks/useToast';
|
|
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
|
import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle';
|
|
import { EnableEnvironmentDialog } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelEnvironmentSwitches/FeatureOverviewSidePanelEnvironmentSwitch/EnableEnvironmentDialog';
|
|
import { UpdateEnabledMessage } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/UpdateEnabledMessage';
|
|
import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
|
|
import {
|
|
FeatureStrategyProdGuard,
|
|
useFeatureStrategyProdGuard,
|
|
} from '../../../../feature/FeatureStrategy/FeatureStrategyProdGuard/FeatureStrategyProdGuard';
|
|
|
|
const StyledBoxContainer = styled(Box)<{ 'data-testid': string }>(() => ({
|
|
mx: 'auto',
|
|
...flexRow,
|
|
}));
|
|
|
|
interface IFeatureToggleSwitchProps {
|
|
featureId: string;
|
|
environmentName: string;
|
|
projectId: string;
|
|
value: boolean;
|
|
onError?: () => void;
|
|
onToggle?: (
|
|
projectId: string,
|
|
feature: string,
|
|
env: string,
|
|
state: boolean
|
|
) => void;
|
|
}
|
|
|
|
export const FeatureToggleSwitch: VFC<IFeatureToggleSwitchProps> = ({
|
|
projectId,
|
|
featureId,
|
|
environmentName,
|
|
value,
|
|
onToggle,
|
|
onError,
|
|
}) => {
|
|
const { loading, toggleFeatureEnvironmentOn, toggleFeatureEnvironmentOff } =
|
|
useFeatureApi();
|
|
const { setToastData, setToastApiError } = useToast();
|
|
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
|
|
const {
|
|
onChangeRequestToggle,
|
|
onChangeRequestToggleClose,
|
|
onChangeRequestToggleConfirm,
|
|
changeRequestDialogDetails,
|
|
} = useChangeRequestToggle(projectId);
|
|
const [isChecked, setIsChecked, rollbackIsChecked] =
|
|
useOptimisticUpdate<boolean>(value);
|
|
|
|
const [showEnabledDialog, setShowEnabledDialog] = useState(false);
|
|
const { feature } = useFeature(projectId, featureId);
|
|
const enableProdGuard = useFeatureStrategyProdGuard(
|
|
feature,
|
|
environmentName
|
|
);
|
|
const [showProdGuard, setShowProdGuard] = useState(false);
|
|
|
|
const disabledStrategiesCount =
|
|
feature?.environments
|
|
.find(env => env.name === environmentName)
|
|
?.strategies.filter(strategy => strategy.disabled).length ?? 0;
|
|
|
|
const handleToggleEnvironmentOn = async (
|
|
shouldActivateDisabled = false
|
|
) => {
|
|
try {
|
|
setIsChecked(!isChecked);
|
|
await toggleFeatureEnvironmentOn(
|
|
projectId,
|
|
feature.name,
|
|
environmentName,
|
|
shouldActivateDisabled
|
|
);
|
|
setToastData({
|
|
type: 'success',
|
|
title: `Available in ${environmentName}`,
|
|
text: `${feature.name} is now available in ${environmentName} based on its defined strategies.`,
|
|
});
|
|
onToggle?.(projectId, feature.name, environmentName, !isChecked);
|
|
} catch (error: unknown) {
|
|
if (
|
|
error instanceof Error &&
|
|
error.message === ENVIRONMENT_STRATEGY_ERROR
|
|
) {
|
|
onError?.();
|
|
} else {
|
|
setToastApiError(formatUnknownError(error));
|
|
}
|
|
rollbackIsChecked();
|
|
}
|
|
};
|
|
|
|
const handleToggleEnvironmentOff = async () => {
|
|
try {
|
|
setIsChecked(!isChecked);
|
|
await toggleFeatureEnvironmentOff(
|
|
projectId,
|
|
feature.name,
|
|
environmentName
|
|
);
|
|
setToastData({
|
|
type: 'success',
|
|
title: `Unavailable in ${environmentName}`,
|
|
text: `${feature.name} is unavailable in ${environmentName} and its strategies will no longer have any effect.`,
|
|
});
|
|
onToggle?.(projectId, feature.name, environmentName, !isChecked);
|
|
} catch (error: unknown) {
|
|
setToastApiError(formatUnknownError(error));
|
|
rollbackIsChecked();
|
|
}
|
|
};
|
|
|
|
const handleClick = async () => {
|
|
setShowProdGuard(false);
|
|
if (isChangeRequestConfigured(environmentName)) {
|
|
if (featureHasOnlyDisabledStrategies()) {
|
|
setShowEnabledDialog(true);
|
|
} else {
|
|
onChangeRequestToggle(
|
|
feature.name,
|
|
environmentName,
|
|
!isChecked,
|
|
false
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
if (isChecked) {
|
|
await handleToggleEnvironmentOff();
|
|
return;
|
|
}
|
|
|
|
if (featureHasOnlyDisabledStrategies()) {
|
|
setShowEnabledDialog(true);
|
|
} else {
|
|
await handleToggleEnvironmentOn();
|
|
}
|
|
};
|
|
|
|
const onClick = async () => {
|
|
console.log('*********');
|
|
console.log(enableProdGuard);
|
|
console.log(isChangeRequestConfigured(environmentName));
|
|
console.log('*********');
|
|
if (enableProdGuard && !isChangeRequestConfigured(environmentName)) {
|
|
setShowProdGuard(true);
|
|
} else {
|
|
await handleClick();
|
|
}
|
|
};
|
|
|
|
const onActivateStrategies = async () => {
|
|
if (isChangeRequestConfigured(environmentName)) {
|
|
onChangeRequestToggle(
|
|
feature.name,
|
|
environmentName,
|
|
!isChecked,
|
|
true
|
|
);
|
|
} else {
|
|
await handleToggleEnvironmentOn(true);
|
|
}
|
|
setShowEnabledDialog(false);
|
|
};
|
|
|
|
const onAddDefaultStrategy = async () => {
|
|
if (isChangeRequestConfigured(environmentName)) {
|
|
onChangeRequestToggle(
|
|
feature.name,
|
|
environmentName,
|
|
!isChecked,
|
|
false
|
|
);
|
|
} else {
|
|
await handleToggleEnvironmentOn();
|
|
}
|
|
setShowEnabledDialog(false);
|
|
};
|
|
|
|
const featureHasOnlyDisabledStrategies = () => {
|
|
const featureEnvironment = feature?.environments?.find(
|
|
env => env.name === environmentName
|
|
);
|
|
return (
|
|
featureEnvironment?.strategies &&
|
|
featureEnvironment?.strategies?.length > 0 &&
|
|
featureEnvironment?.strategies?.every(strategy => strategy.disabled)
|
|
);
|
|
};
|
|
|
|
const key = `${feature.name}-${environmentName}`;
|
|
|
|
return (
|
|
<>
|
|
<StyledBoxContainer
|
|
key={key} // Prevent animation when archiving rows
|
|
data-testid={`TOGGLE-${key}`}
|
|
>
|
|
<PermissionSwitch
|
|
tooltip={
|
|
isChecked
|
|
? `Disable feature in ${environmentName}`
|
|
: `Enable feature in ${environmentName}`
|
|
}
|
|
checked={isChecked}
|
|
environmentId={environmentName}
|
|
projectId={projectId}
|
|
permission={UPDATE_FEATURE_ENVIRONMENT}
|
|
inputProps={{ 'aria-label': environmentName }}
|
|
onClick={onClick}
|
|
data-testId={'permission-switch'}
|
|
/>
|
|
</StyledBoxContainer>
|
|
<EnableEnvironmentDialog
|
|
isOpen={showEnabledDialog}
|
|
onClose={() => setShowEnabledDialog(false)}
|
|
environment={environmentName}
|
|
disabledStrategiesCount={disabledStrategiesCount}
|
|
onActivateDisabledStrategies={onActivateStrategies}
|
|
onAddDefaultStrategy={onAddDefaultStrategy}
|
|
/>
|
|
<ChangeRequestDialogue
|
|
isOpen={changeRequestDialogDetails.isOpen}
|
|
onClose={onChangeRequestToggleClose}
|
|
environment={changeRequestDialogDetails?.environment}
|
|
onConfirm={onChangeRequestToggleConfirm}
|
|
messageComponent={
|
|
<UpdateEnabledMessage
|
|
enabled={changeRequestDialogDetails?.enabled!}
|
|
featureName={changeRequestDialogDetails?.featureName!}
|
|
environment={changeRequestDialogDetails.environment!}
|
|
/>
|
|
}
|
|
/>
|
|
<FeatureStrategyProdGuard
|
|
open={showProdGuard}
|
|
onClose={() => setShowProdGuard(false)}
|
|
onClick={handleClick}
|
|
loading={loading}
|
|
label={`${isChecked ? 'Disable' : 'Enable'} Environment`}
|
|
/>
|
|
</>
|
|
);
|
|
};
|