From 00ab52875ed21965fde541fd477b33b3890f7b19 Mon Sep 17 00:00:00 2001 From: sjaanus Date: Wed, 27 Jul 2022 10:46:12 +0300 Subject: [PATCH] Delete archived toggles from the front end (#1150) * Grouping fix grid not showing user names * Remove deprecated access endpoints * Manual prettier * Revert user role update * Add a way to delete archived toggles from the front end * Fix layout * Add project to permissionbutton * Prettier * Minor fixes * Run prettier --- .../archive/ArchiveTable/ArchiveTable.tsx | 27 ++++-- .../ArchivedFeatureActionCell.tsx} | 19 +++- .../ArchivedFeatureDeleteConfirm.tsx | 97 +++++++++++++++++++ .../useReviveFeatureApi.ts | 8 +- 4 files changed, 141 insertions(+), 10 deletions(-) rename frontend/src/component/archive/ArchiveTable/{ReviveArchivedFeatureCell/ReviveArchivedFeatureCell.tsx => ArchivedFeatureActionCell/ArchivedFeatureActionCell.tsx} (55%) create mode 100644 frontend/src/component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm.tsx diff --git a/frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx b/frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx index 3fc0127300..714e799804 100644 --- a/frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx +++ b/frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx @@ -14,7 +14,7 @@ import { FeatureTypeCell } from 'component/common/Table/cells/FeatureTypeCell/Fe import { FeatureSeenCell } from 'component/common/Table/cells/FeatureSeenCell/FeatureSeenCell'; import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; import { FeatureStaleCell } from 'component/feature/FeatureToggleList/FeatureStaleCell/FeatureStaleCell'; -import { ReviveArchivedFeatureCell } from 'component/archive/ArchiveTable/ReviveArchivedFeatureCell/ReviveArchivedFeatureCell'; +import { ArchivedFeatureActionCell } from 'component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureActionCell'; import { featuresPlaceholder } from 'component/feature/FeatureToggleList/FeatureToggleListTable'; import theme from 'themes/theme'; import { FeatureSchema } from 'openapi'; @@ -24,6 +24,8 @@ import { formatUnknownError } from 'utils/formatUnknownError'; import { useSearch } from 'hooks/useSearch'; import { FeatureArchivedCell } from './FeatureArchivedCell/FeatureArchivedCell'; import { useSearchParams } from 'react-router-dom'; +import { ArchivedFeatureDeleteConfirm } from './ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm'; +import { IFeatureToggle } from 'interfaces/featureToggle'; export interface IFeaturesArchiveTableProps { archivedFeatures: FeatureSchema[]; @@ -52,6 +54,9 @@ export const ArchiveTable = ({ const isMediumScreen = useMediaQuery(theme.breakpoints.down('lg')); const { setToastData, setToastApiError } = useToast(); + const [deleteModalOpen, setDeleteModalOpen] = useState(false); + const [deletedFeature, setDeletedFeature] = useState(); + const [searchParams, setSearchParams] = useSearchParams(); const { reviveFeature } = useFeatureArchiveApi(); @@ -153,12 +158,16 @@ export const ArchiveTable = ({ Header: 'Actions', id: 'Actions', align: 'center', - maxWidth: 85, + maxWidth: 120, canSort: false, - Cell: ({ row: { original } }: any) => ( - onRevive(original.name)} + Cell: ({ row: { original: feature } }: any) => ( + onRevive(feature.name)} + onDelete={() => { + setDeletedFeature(feature); + setDeleteModalOpen(true); + }} /> ), }, @@ -290,6 +299,12 @@ export const ArchiveTable = ({ /> )} /> + ); }; diff --git a/frontend/src/component/archive/ArchiveTable/ReviveArchivedFeatureCell/ReviveArchivedFeatureCell.tsx b/frontend/src/component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureActionCell.tsx similarity index 55% rename from frontend/src/component/archive/ArchiveTable/ReviveArchivedFeatureCell/ReviveArchivedFeatureCell.tsx rename to frontend/src/component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureActionCell.tsx index 8a9b7c5029..f536af806f 100644 --- a/frontend/src/component/archive/ArchiveTable/ReviveArchivedFeatureCell/ReviveArchivedFeatureCell.tsx +++ b/frontend/src/component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureActionCell.tsx @@ -1,16 +1,21 @@ import { VFC } from 'react'; import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell'; -import { Undo } from '@mui/icons-material'; +import { Delete, Undo } from '@mui/icons-material'; import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; -import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions'; +import { + DELETE_FEATURE, + UPDATE_FEATURE, +} from 'component/providers/AccessProvider/permissions'; interface IReviveArchivedFeatureCell { onRevive: () => void; + onDelete: () => void; project: string; } -export const ReviveArchivedFeatureCell: VFC = ({ +export const ArchivedFeatureActionCell: VFC = ({ onRevive, + onDelete, project, }) => { return ( @@ -23,6 +28,14 @@ export const ReviveArchivedFeatureCell: VFC = ({ > + + + ); }; diff --git a/frontend/src/component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm.tsx b/frontend/src/component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm.tsx new file mode 100644 index 0000000000..8232271980 --- /dev/null +++ b/frontend/src/component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm.tsx @@ -0,0 +1,97 @@ +import { Alert, styled } from '@mui/material'; +import React, { useState } from 'react'; +import { Dialogue } from 'component/common/Dialogue/Dialogue'; +import Input from 'component/common/Input/Input'; +import { IFeatureToggle } from 'interfaces/featureToggle'; +import { formatUnknownError } from 'utils/formatUnknownError'; +import { useFeatureArchiveApi } from 'hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi'; +import useToast from 'hooks/useToast'; + +interface IArchivedFeatureDeleteConfirmProps { + deletedFeature?: IFeatureToggle; + open: boolean; + setOpen: React.Dispatch>; + refetch: () => void; +} + +const StyledDeleteParagraph = styled('p')(({ theme }) => ({ + marginTop: theme.spacing(4), +})); + +const StyledFormInput = styled(Input)(({ theme }) => ({ + marginTop: theme.spacing(2), +})); + +export const ArchivedFeatureDeleteConfirm = ({ + deletedFeature, + open, + setOpen, + refetch, +}: IArchivedFeatureDeleteConfirmProps) => { + const [confirmName, setConfirmName] = useState(''); + const { setToastData, setToastApiError } = useToast(); + const { deleteFeature } = useFeatureArchiveApi(); + + const onDeleteFeatureToggle = async () => { + try { + if (!deletedFeature) { + return; + } + await deleteFeature(deletedFeature.name); + await refetch(); + setToastData({ + type: 'success', + title: 'Feature deleted', + text: `You have successfully deleted the ${deletedFeature.name} feature toggle.`, + }); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); + } finally { + clearModal(); + } + }; + + const clearModal = () => { + setOpen(false); + setConfirmName(''); + }; + + const formId = 'delete-feature-toggle-confirmation-form'; + + return ( + + + Warning! To safely delete a feature toggle you might want to + delete the related code in your application first. This ensures + you avoid any errors in case you create a new feature toggle + with the same name in the future. + + + + In order to delete this feature toggle, please enter the name of + the toggle in the textfield below:{' '} + {deletedFeature?.name} + + +
+ ) => { + setConfirmName(e.currentTarget.value); + }} + value={confirmName} + label="Feature toggle name" + /> + +
+ ); +}; diff --git a/frontend/src/hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi.ts b/frontend/src/hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi.ts index 9a8770c2a3..83f7416dda 100644 --- a/frontend/src/hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi.ts +++ b/frontend/src/hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi.ts @@ -11,5 +11,11 @@ export const useFeatureArchiveApi = () => { return makeRequest(req.caller, req.id); }; - return { reviveFeature, errors, loading }; + const deleteFeature = async (feature: string) => { + const path = `api/admin/archive/${feature}`; + const req = createRequest(path, { method: 'DELETE' }); + return makeRequest(req.caller, req.id); + }; + + return { reviveFeature, deleteFeature, errors, loading }; };