From 2487b990bd788c73297f60e4300d17b50f12343e Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Thu, 18 May 2023 08:07:56 +0200 Subject: [PATCH] feat: Bulk enabled disable (#3797) --- .../FeatureToggleList/BulkDisableDialog.tsx | 85 +++++++++++++++++++ .../FeatureToggleList/BulkEnableDialog.tsx | 85 +++++++++++++++++++ .../ProjectFeaturesBatchActions.tsx | 50 ++++++++++- .../actions/useFeatureApi/useFeatureApi.ts | 58 +++++++++++++ 4 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 frontend/src/component/feature/FeatureToggleList/BulkDisableDialog.tsx create mode 100644 frontend/src/component/feature/FeatureToggleList/BulkEnableDialog.tsx diff --git a/frontend/src/component/feature/FeatureToggleList/BulkDisableDialog.tsx b/frontend/src/component/feature/FeatureToggleList/BulkDisableDialog.tsx new file mode 100644 index 0000000000..aaa7430bca --- /dev/null +++ b/frontend/src/component/feature/FeatureToggleList/BulkDisableDialog.tsx @@ -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[]; + 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 ( + + + You have selected {data.length} feature toggles to + disable. +
+
+ + Select which environment to disable the features for: + + setSelected(option)} + /> +
+
+ ); +}; diff --git a/frontend/src/component/feature/FeatureToggleList/BulkEnableDialog.tsx b/frontend/src/component/feature/FeatureToggleList/BulkEnableDialog.tsx new file mode 100644 index 0000000000..3cdd1ffaa6 --- /dev/null +++ b/frontend/src/component/feature/FeatureToggleList/BulkEnableDialog.tsx @@ -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[]; + 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 ( + + + You have selected {data.length} feature toggles to + enable. +
+
+ + Select which environment to enable the features for: + + setSelected(option)} + /> +
+
+ ); +}; diff --git a/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ProjectFeaturesBatchActions.tsx b/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ProjectFeaturesBatchActions.tsx index d867c2d2dc..f752fb7096 100644 --- a/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ProjectFeaturesBatchActions.tsx +++ b/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ProjectFeaturesBatchActions.tsx @@ -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 ( <> + +