1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: Bulk enabled disable (#3797)

This commit is contained in:
Mateusz Kwasniewski 2023-05-18 08:07:56 +02:00 committed by GitHub
parent 0335934bf0
commit 2487b990bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 276 additions and 2 deletions

View File

@ -0,0 +1,85 @@
import { useState } from 'react';
import { Box, styled, Typography } from '@mui/material';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
import useToast from 'hooks/useToast';
import type { FeatureSchema } from 'openapi';
import { formatUnknownError } from 'utils/formatUnknownError';
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
import useProject from 'hooks/api/getters/useProject/useProject';
interface IExportDialogProps {
showExportDialog: boolean;
data: Pick<FeatureSchema, 'name'>[];
onClose: () => void;
onConfirm?: () => void;
environments: string[];
projectId: string;
}
const StyledSelect = styled(GeneralSelect)(({ theme }) => ({
minWidth: '250px',
marginTop: theme.spacing(2),
}));
export const BulkDisableDialog = ({
showExportDialog,
data,
onClose,
onConfirm,
environments,
projectId,
}: IExportDialogProps) => {
const [selected, setSelected] = useState(environments[0]);
const { bulkToggleFeaturesEnvironmentOff } = useFeatureApi();
const { refetch } = useProject(projectId);
const { setToastApiError } = useToast();
const getOptions = () =>
environments.map(env => ({
key: env,
label: env,
}));
const onClick = async () => {
try {
await bulkToggleFeaturesEnvironmentOff(
projectId,
data.map(feature => feature.name),
selected
);
refetch();
onClose();
onConfirm?.();
} catch (e: unknown) {
setToastApiError(formatUnknownError(e));
}
};
return (
<Dialogue
open={showExportDialog}
title="Disable feature toggles"
onClose={onClose}
onClick={onClick}
primaryButtonText="Disable toggles"
secondaryButtonText="Cancel"
>
<Box>
You have selected <b>{data.length}</b> feature toggles to
disable.
<br />
<br />
<Typography>
Select which environment to disable the features for:
</Typography>
<StyledSelect
options={getOptions()}
value={selected}
onChange={(option: string) => setSelected(option)}
/>
</Box>
</Dialogue>
);
};

View File

@ -0,0 +1,85 @@
import { useState } from 'react';
import { Box, styled, Typography } from '@mui/material';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
import useToast from 'hooks/useToast';
import type { FeatureSchema } from 'openapi';
import { formatUnknownError } from 'utils/formatUnknownError';
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
import useProject from 'hooks/api/getters/useProject/useProject';
interface IExportDialogProps {
showExportDialog: boolean;
data: Pick<FeatureSchema, 'name'>[];
onClose: () => void;
onConfirm?: () => void;
environments: string[];
projectId: string;
}
const StyledSelect = styled(GeneralSelect)(({ theme }) => ({
minWidth: '250px',
marginTop: theme.spacing(2),
}));
export const BulkEnableDialog = ({
showExportDialog,
data,
onClose,
onConfirm,
environments,
projectId,
}: IExportDialogProps) => {
const [selected, setSelected] = useState(environments[0]);
const { bulkToggleFeaturesEnvironmentOn } = useFeatureApi();
const { refetch } = useProject(projectId);
const { setToastApiError } = useToast();
const getOptions = () =>
environments.map(env => ({
key: env,
label: env,
}));
const onClick = async () => {
try {
await bulkToggleFeaturesEnvironmentOn(
projectId,
data.map(feature => feature.name),
selected
);
refetch();
onClose();
onConfirm?.();
} catch (e: unknown) {
setToastApiError(formatUnknownError(e));
}
};
return (
<Dialogue
open={showExportDialog}
title="Enable feature toggles"
onClose={onClose}
onClick={onClick}
primaryButtonText="Enable toggles"
secondaryButtonText="Cancel"
>
<Box>
You have selected <b>{data.length}</b> feature toggles to
enable.
<br />
<br />
<Typography>
Select which environment to enable the features for:
</Typography>
<StyledSelect
options={getOptions()}
value={selected}
onChange={(option: string) => setSelected(option)}
/>
</Box>
</Dialogue>
);
};

View File

@ -1,12 +1,13 @@
import { FC, useMemo, useState } from 'react';
import { Button } from '@mui/material';
import type { FeatureSchema } from 'openapi';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { ExportDialog } from 'component/feature/FeatureToggleList/ExportDialog';
import { ArchiveButton } from './ArchiveButton';
import { MoreActions } from './MoreActions';
import { ManageTags } from './ManageTags';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { BulkDisableDialog } from 'component/feature/FeatureToggleList/BulkDisableDialog';
import { BulkEnableDialog } from 'component/feature/FeatureToggleList/BulkEnableDialog';
interface IProjectFeaturesBatchActionsProps {
selectedIds: string[];
@ -17,8 +18,9 @@ interface IProjectFeaturesBatchActionsProps {
export const ProjectFeaturesBatchActions: FC<
IProjectFeaturesBatchActionsProps
> = ({ selectedIds, data, projectId }) => {
const { uiConfig } = useUiConfig();
const [showExportDialog, setShowExportDialog] = useState(false);
const [showBulkEnableDialog, setShowBulkEnableDialog] = useState(false);
const [showBulkDisableDialog, setShowBulkDisableDialog] = useState(false);
const { trackEvent } = usePlausibleTracker();
const selectedData = useMemo(
() => data.filter(d => selectedIds.includes(d.name)),
@ -40,9 +42,37 @@ export const ProjectFeaturesBatchActions: FC<
},
});
};
const trackBulkEnabled = () => {
trackEvent('batch_operations', {
props: {
eventType: 'features enabled',
},
});
};
const trackBulkDisabled = () => {
trackEvent('batch_operations', {
props: {
eventType: 'features disabled',
},
});
};
return (
<>
<Button
variant="outlined"
size="small"
onClick={() => setShowBulkEnableDialog(true)}
>
Enable
</Button>
<Button
variant="outlined"
size="small"
onClick={() => setShowBulkDisableDialog(true)}
>
Disable
</Button>
<ArchiveButton projectId={projectId} features={selectedIds} />
<Button
variant="outlined"
@ -60,6 +90,22 @@ export const ProjectFeaturesBatchActions: FC<
environments={environments}
onConfirm={trackExport}
/>
<BulkEnableDialog
showExportDialog={showBulkEnableDialog}
data={selectedData}
onClose={() => setShowBulkEnableDialog(false)}
environments={environments}
projectId={projectId}
onConfirm={trackBulkEnabled}
/>
<BulkDisableDialog
showExportDialog={showBulkDisableDialog}
data={selectedData}
onClose={() => setShowBulkDisableDialog(false)}
environments={environments}
projectId={projectId}
onConfirm={trackBulkDisabled}
/>
</>
);
};

View File

@ -75,6 +75,62 @@ const useFeatureApi = () => {
[createRequest, makeRequest]
);
const bulkToggleFeaturesEnvironmentOn = useCallback(
async (
projectId: string,
featureIds: string[],
environmentId: string,
shouldActivateDisabledStrategies = false
) => {
const path = `api/admin/projects/${projectId}/bulk_features/environments/${environmentId}/on?shouldActivateDisabledStrategies=${shouldActivateDisabledStrategies}`;
const req = createRequest(
path,
{
method: 'POST',
body: JSON.stringify({ features: featureIds }),
},
'bulkToggleFeaturesEnvironmentOn'
);
try {
const res = await makeRequest(req.caller, req.id);
return res;
} catch (e) {
throw e;
}
},
[createRequest, makeRequest]
);
const bulkToggleFeaturesEnvironmentOff = useCallback(
async (
projectId: string,
featureIds: string[],
environmentId: string,
shouldActivateDisabledStrategies = false
) => {
const path = `api/admin/projects/${projectId}/bulk_features/environments/${environmentId}/off?shouldActivateDisabledStrategies=${shouldActivateDisabledStrategies}`;
const req = createRequest(
path,
{
method: 'POST',
body: JSON.stringify({ features: featureIds }),
},
'bulkToggleFeaturesEnvironmentOff'
);
try {
const res = await makeRequest(req.caller, req.id);
return res;
} catch (e) {
throw e;
}
},
[createRequest, makeRequest]
);
const toggleFeatureEnvironmentOff = useCallback(
async (projectId: string, featureId: string, environmentId: string) => {
const path = `api/admin/projects/${projectId}/features/${featureId}/environments/${environmentId}/off`;
@ -307,6 +363,8 @@ const useFeatureApi = () => {
overrideVariantsInEnvironments,
cloneFeatureToggle,
loading,
bulkToggleFeaturesEnvironmentOn,
bulkToggleFeaturesEnvironmentOff,
};
};