From 004038e872f886f32b57a4e7652cbc707d100e7a Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:33:00 +0200 Subject: [PATCH] update project archive and revive dialogs (#7918) --- .../archive/ProjectFeaturesArchiveTable.tsx | 6 +-- .../NewProjectCard/ProjectArchiveCard.tsx | 14 ++--- .../ArchiveProject/ArchiveProjectDialogue.tsx | 6 +-- .../DeleteProject/DeleteProjectDialogue.tsx | 52 ++++++++++++++----- .../src/component/project/Project/Project.tsx | 4 +- .../ProjectFeaturesArchive.tsx | 2 +- .../Settings/ArchiveProject.tsx | 48 +++++------------ .../Settings/DeleteProject.tsx | 5 +- .../component/project/ProjectId/ProjectId.tsx | 9 ++++ .../ProjectList/ArchiveProjectList.tsx | 5 +- .../ReviveProjectDialog.tsx | 26 +++++++--- 11 files changed, 104 insertions(+), 73 deletions(-) create mode 100644 frontend/src/component/project/ProjectId/ProjectId.tsx diff --git a/frontend/src/component/archive/ProjectFeaturesArchiveTable.tsx b/frontend/src/component/archive/ProjectFeaturesArchiveTable.tsx index 68801ebf23f4..bec8e743adc0 100644 --- a/frontend/src/component/archive/ProjectFeaturesArchiveTable.tsx +++ b/frontend/src/component/archive/ProjectFeaturesArchiveTable.tsx @@ -1,5 +1,5 @@ import { useFeaturesArchive } from 'hooks/api/getters/useFeaturesArchive/useFeaturesArchive'; -import type { VFC } from 'react'; +import type { FC } from 'react'; import type { SortingRule } from 'react-table'; import { createLocalStorage } from 'utils/createLocalStorage'; import { ArchiveTable } from './ArchiveTable/ArchiveTable'; @@ -10,7 +10,7 @@ interface IProjectFeaturesTable { projectId: string; } -export const ProjectFeaturesArchiveTable: VFC = ({ +export const ProjectFeaturesArchiveTable: FC = ({ projectId, }) => { const { archivedFeatures, loading, refetchArchived } = @@ -23,7 +23,7 @@ export const ProjectFeaturesArchiveTable: VFC = ({ return ( = ({ - - - {name} - - + + + + {name} + + + @@ -114,7 +116,7 @@ export const ProjectArchiveCard: FC = ({ onClick={onRevive} projectId={id} permission={UPDATE_PROJECT} - tooltipProps={{ title: 'Restore project' }} + tooltipProps={{ title: 'Revive project' }} data-testid={`revive-feature-flag-button`} > diff --git a/frontend/src/component/project/Project/ArchiveProject/ArchiveProjectDialogue.tsx b/frontend/src/component/project/Project/ArchiveProject/ArchiveProjectDialogue.tsx index 10cf5e88eb75..c6c46bbee603 100644 --- a/frontend/src/component/project/Project/ArchiveProject/ArchiveProjectDialogue.tsx +++ b/frontend/src/component/project/Project/ArchiveProject/ArchiveProjectDialogue.tsx @@ -45,11 +45,11 @@ export const ArchiveProjectDialogue = ({ open={open} onClick={onClick} onClose={onClose} - title='Really archive project' + title='Are you sure?' > - This will archive the project and all feature flags archived in - it. + The project will be moved to the projects archive, where it can + either be revived or permanently deleted. ); diff --git a/frontend/src/component/project/Project/DeleteProject/DeleteProjectDialogue.tsx b/frontend/src/component/project/Project/DeleteProject/DeleteProjectDialogue.tsx index 0bf07a820147..6ab5dabcf95a 100644 --- a/frontend/src/component/project/Project/DeleteProject/DeleteProjectDialogue.tsx +++ b/frontend/src/component/project/Project/DeleteProject/DeleteProjectDialogue.tsx @@ -7,19 +7,26 @@ import useToast from 'hooks/useToast'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { useUiFlag } from 'hooks/useUiFlag'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; -import { Typography } from '@mui/material'; +import { styled, Typography } from '@mui/material'; +import { ProjectId } from 'component/project/ProjectId/ProjectId'; interface IDeleteProjectDialogueProps { - project: string; + projectId: string; + projectName?: string; open: boolean; onClose: (e: React.SyntheticEvent) => void; onSuccess?: () => void; } +const StyledParagraph = styled(Typography)(({ theme }) => ({ + marginBottom: theme.spacing(1), +})); + export const DeleteProjectDialogue = ({ open, onClose, - project, + projectId, + projectName, onSuccess, }: IDeleteProjectDialogueProps) => { const { deleteProject } = useProjectApi(); @@ -32,7 +39,7 @@ export const DeleteProjectDialogue = ({ const onClick = async (e: React.SyntheticEvent) => { e.preventDefault(); try { - await deleteProject(project); + await deleteProject(projectId); refetchProjects(); refetchProjectArchive(); setToastData({ @@ -52,17 +59,34 @@ export const DeleteProjectDialogue = ({ open={open} onClick={onClick} onClose={onClose} - title='Really delete project' + title='Are you sure?' > - - This will irreversibly remove the project, all feature flags - archived in it, all API keys scoped to only this project - - . - + + This will irreversibly remove: +
    +
  • project with all of its settings
  • +
  • all feature flags archived in it
  • +
  • all API keys scoped to only to this project
  • + all actions configured for it} + /> +
+
+ + + Are you sure you'd like to permanently delete + project {projectName}? + + + Project ID: {projectId} + + + } + /> ); }; diff --git a/frontend/src/component/project/Project/Project.tsx b/frontend/src/component/project/Project/Project.tsx index bfaf798cbe8a..b627786a2d87 100644 --- a/frontend/src/component/project/Project/Project.tsx +++ b/frontend/src/component/project/Project/Project.tsx @@ -103,7 +103,7 @@ export const Project = () => { name: 'health', }, { - title: 'Archive', + title: 'Archived flags', path: `${basePath}/archive`, name: 'archive', }, @@ -285,7 +285,7 @@ export const Project = () => { { setShowDelDialog(false); diff --git a/frontend/src/component/project/Project/ProjectFeaturesArchive/ProjectFeaturesArchive.tsx b/frontend/src/component/project/Project/ProjectFeaturesArchive/ProjectFeaturesArchive.tsx index c02ff218128b..f4d6020dd15d 100644 --- a/frontend/src/component/project/Project/ProjectFeaturesArchive/ProjectFeaturesArchive.tsx +++ b/frontend/src/component/project/Project/ProjectFeaturesArchive/ProjectFeaturesArchive.tsx @@ -6,7 +6,7 @@ import { useProjectOverviewNameOrId } from 'hooks/api/getters/useProjectOverview export const ProjectFeaturesArchive = () => { const projectId = useRequiredPathParam('projectId'); const projectName = useProjectOverviewNameOrId(projectId); - usePageTitle(`Project archive – ${projectName}`); + usePageTitle(`Project archived flags – ${projectName}`); return ; }; diff --git a/frontend/src/component/project/Project/ProjectSettings/Settings/ArchiveProject.tsx b/frontend/src/component/project/Project/ProjectSettings/Settings/ArchiveProject.tsx index 66efc96b5496..0990ce8bc53b 100644 --- a/frontend/src/component/project/Project/ProjectSettings/Settings/ArchiveProject.tsx +++ b/frontend/src/component/project/Project/ProjectSettings/Settings/ArchiveProject.tsx @@ -1,12 +1,10 @@ -import { styled } from '@mui/material'; +import { Link, styled } from '@mui/material'; import { DELETE_PROJECT } from 'component/providers/AccessProvider/permissions'; import PermissionButton from 'component/common/PermissionButton/PermissionButton'; import { useState } from 'react'; import { useNavigate } from 'react-router'; -import { useUiFlag } from 'hooks/useUiFlag'; -import { useActions } from 'hooks/api/getters/useActions/useActions'; +import { Link as RouterLink } from 'react-router-dom'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { ArchiveProjectDialogue } from '../../ArchiveProject/ArchiveProjectDialogue'; const StyledContainer = styled('div')(({ theme }) => ({ @@ -32,54 +30,34 @@ export const ArchiveProject = ({ projectId, featureCount, }: IDeleteProjectProps) => { - const { isEnterprise } = useUiConfig(); - const automatedActionsEnabled = useUiFlag('automatedActions'); - const { actions } = useActions(projectId); const [showArchiveDialog, setShowArchiveDialog] = useState(false); - const actionsCount = actions.filter(({ enabled }) => enabled).length; const navigate = useNavigate(); + const disabled = featureCount > 0; + return (

- Before you can archive a project, you must first archive all the - feature flags associated with it - {isEnterprise() && automatedActionsEnabled - ? ' and disable all actions that are in it' - : ''} - . + Before you can archive a project, you must first archive all of + the feature flags associated with it.

0} show={

Currently there {featureCount <= 1 ? 'is' : 'are'}{' '} - - {featureCount} active feature{' '} - {featureCount === 1 ? 'flag' : 'flags'}. - -

- } - /> - 0 - } - show={ -

- Currently there {actionsCount <= 1 ? 'is' : 'are'}{' '} - - {actionsCount} enabled{' '} - {actionsCount === 1 ? 'action' : 'actions'}. - + + + {featureCount} active feature{' '} + {featureCount === 1 ? 'flag' : 'flags'}. + +

} /> 0} + disabled={disabled} projectId={projectId} onClick={() => { setShowArchiveDialog(true); diff --git a/frontend/src/component/project/Project/ProjectSettings/Settings/DeleteProject.tsx b/frontend/src/component/project/Project/ProjectSettings/Settings/DeleteProject.tsx index 174dc004c320..06e68f5e2dee 100644 --- a/frontend/src/component/project/Project/ProjectSettings/Settings/DeleteProject.tsx +++ b/frontend/src/component/project/Project/ProjectSettings/Settings/DeleteProject.tsx @@ -25,11 +25,13 @@ const StyledButtonContainer = styled('div')(({ theme }) => ({ interface IDeleteProjectProps { projectId: string; + projectName?: string; featureCount: number; } export const DeleteProject = ({ projectId, + projectName, featureCount, }: IDeleteProjectProps) => { const { isEnterprise } = useUiConfig(); @@ -106,7 +108,8 @@ export const DeleteProject = ({ { setShowDelDialog(false); diff --git a/frontend/src/component/project/ProjectId/ProjectId.tsx b/frontend/src/component/project/ProjectId/ProjectId.tsx new file mode 100644 index 000000000000..6477c40daf3e --- /dev/null +++ b/frontend/src/component/project/ProjectId/ProjectId.tsx @@ -0,0 +1,9 @@ +import { styled } from '@mui/material'; + +export const ProjectId = styled('code')(({ theme }) => ({ + backgroundColor: theme.palette.background.elevation2, + padding: theme.spacing(0.5, 1.5), + display: 'inline-block', + borderRadius: `${theme.shape.borderRadius}px`, + fontSize: theme.typography.body2.fontSize, +})); diff --git a/frontend/src/component/project/ProjectList/ArchiveProjectList.tsx b/frontend/src/component/project/ProjectList/ArchiveProjectList.tsx index f76caef9089c..1134c3a7a95f 100644 --- a/frontend/src/component/project/ProjectList/ArchiveProjectList.tsx +++ b/frontend/src/component/project/ProjectList/ArchiveProjectList.tsx @@ -49,6 +49,7 @@ export const ArchiveProjectList: FC = () => { const [deleteProject, setDeleteProject] = useState<{ isOpen: boolean; id?: string; + name?: string; }>({ isOpen: false }); useEffect(() => { @@ -76,6 +77,7 @@ export const ArchiveProjectList: FC = () => { onDelete={() => setDeleteProject({ id, + name: projects?.find((project) => project.id === id)?.name, isOpen: true, }) } @@ -155,7 +157,8 @@ export const ArchiveProjectList: FC = () => { } /> { setDeleteProject((state) => ({ ...state, isOpen: false })); diff --git a/frontend/src/component/project/ProjectList/ReviveProjectDialog/ReviveProjectDialog.tsx b/frontend/src/component/project/ProjectList/ReviveProjectDialog/ReviveProjectDialog.tsx index 86f535bd5139..ae59733a3721 100644 --- a/frontend/src/component/project/ProjectList/ReviveProjectDialog/ReviveProjectDialog.tsx +++ b/frontend/src/component/project/ProjectList/ReviveProjectDialog/ReviveProjectDialog.tsx @@ -1,4 +1,6 @@ +import { styled, Typography } from '@mui/material'; import { Dialogue } from 'component/common/Dialogue/Dialogue'; +import { ProjectId } from 'component/project/ProjectId/ProjectId'; import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi'; import useProjects from 'hooks/api/getters/useProjects/useProjects'; import useToast from 'hooks/useToast'; @@ -11,6 +13,10 @@ type ReviveProjectDialogProps = { onClose: () => void; }; +const StyledParagraph = styled(Typography)(({ theme }) => ({ + marginBottom: theme.spacing(1), +})); + export const ReviveProjectDialog = ({ name, id, @@ -30,9 +36,9 @@ export const ReviveProjectDialog = ({ refetchProjects(); refetchProjectArchive(); setToastData({ - title: 'Restored project', + title: 'Revive project', type: 'success', - text: 'Successfully restored project', + text: 'Successfully revived project', }); } catch (ex: unknown) { setToastApiError(formatUnknownError(ex)); @@ -43,14 +49,20 @@ export const ReviveProjectDialog = ({ return ( - Are you sure you'd like to restore project {name}{' '} - (id: {id})? - {/* TODO: more explanation */} + + Are you sure you'd like to revive project{' '} + {name}? + + + Project ID: {id} + + + All flags in the revived project will remain archived. + ); };