Skip to content

Commit

Permalink
update project archive and revive dialogs (Unleash#7918)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tymek authored Aug 19, 2024
1 parent cf3379d commit 004038e
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -10,7 +10,7 @@ interface IProjectFeaturesTable {
projectId: string;
}

export const ProjectFeaturesArchiveTable: VFC<IProjectFeaturesTable> = ({
export const ProjectFeaturesArchiveTable: FC<IProjectFeaturesTable> = ({
projectId,
}) => {
const { archivedFeatures, loading, refetchArchived } =
Expand All @@ -23,7 +23,7 @@ export const ProjectFeaturesArchiveTable: VFC<IProjectFeaturesTable> = ({

return (
<ArchiveTable
title='Project archive'
title='Archived flags'
archivedFeatures={archivedFeatures || []}
loading={loading}
storedParams={value}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,13 @@ export const ProjectArchiveCard: FC<ProjectArchiveCardProps> = ({
<ProjectIcon color='action' />
</StyledIconBox>
<StyledBox data-loading>
<StyledCardTitle>
<Highlighter search={searchQuery}>
{name}
</Highlighter>
</StyledCardTitle>
<Tooltip title={`id: ${id}`} arrow>
<StyledCardTitle>
<Highlighter search={searchQuery}>
{name}
</Highlighter>
</StyledCardTitle>
</Tooltip>
</StyledBox>
<ProjectModeBadge mode={mode} />
</StyledDivHeader>
Expand Down Expand Up @@ -114,7 +116,7 @@ export const ProjectArchiveCard: FC<ProjectArchiveCardProps> = ({
onClick={onRevive}
projectId={id}
permission={UPDATE_PROJECT}
tooltipProps={{ title: 'Restore project' }}
tooltipProps={{ title: 'Revive project' }}
data-testid={`revive-feature-flag-button`}
>
<Undo />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ export const ArchiveProjectDialogue = ({
open={open}
onClick={onClick}
onClose={onClose}
title='Really archive project'
title='Are you sure?'
>
<Typography>
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.
</Typography>
</Dialogue>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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({
Expand All @@ -52,17 +59,34 @@ export const DeleteProjectDialogue = ({
open={open}
onClick={onClick}
onClose={onClose}
title='Really delete project'
title='Are you sure?'
>
<Typography>
This will irreversibly remove the project, all feature flags
archived in it, all API keys scoped to only this project
<ConditionallyRender
condition={isEnterprise() && automatedActionsEnabled}
show=', and all actions configured for it'
/>
.
</Typography>
<StyledParagraph>
This will irreversibly remove:
<ul>
<li>project with all of its settings</li>
<li>all feature flags archived in it</li>
<li>all API keys scoped to only to this project</li>
<ConditionallyRender
condition={isEnterprise() && automatedActionsEnabled}
show={<li>all actions configured for it</li>}
/>
</ul>
</StyledParagraph>
<ConditionallyRender
condition={Boolean(projectName)}
show={
<>
<StyledParagraph>
Are you sure you'd like to permanently delete
project <strong>{projectName}</strong>?
</StyledParagraph>
<StyledParagraph>
Project ID: <ProjectId>{projectId}</ProjectId>
</StyledParagraph>
</>
}
/>
</Dialogue>
);
};
4 changes: 2 additions & 2 deletions frontend/src/component/project/Project/Project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const Project = () => {
name: 'health',
},
{
title: 'Archive',
title: 'Archived flags',
path: `${basePath}/archive`,
name: 'archive',
},
Expand Down Expand Up @@ -285,7 +285,7 @@ export const Project = () => {
</StyledTabContainer>
</StyledHeader>
<DeleteProjectDialogue
project={projectId}
projectId={projectId}
open={showDelDialog}
onClose={() => {
setShowDelDialog(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <ProjectFeaturesArchiveTable projectId={projectId} />;
};
Original file line number Diff line number Diff line change
@@ -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 }) => ({
Expand All @@ -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 (
<StyledContainer>
<p>
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.
</p>
<ConditionallyRender
condition={featureCount > 0}
show={
<p>
Currently there {featureCount <= 1 ? 'is' : 'are'}{' '}
<strong>
{featureCount} active feature{' '}
{featureCount === 1 ? 'flag' : 'flags'}.
</strong>
</p>
}
/>
<ConditionallyRender
condition={
isEnterprise() &&
automatedActionsEnabled &&
actionsCount > 0
}
show={
<p>
Currently there {actionsCount <= 1 ? 'is' : 'are'}{' '}
<strong>
{actionsCount} enabled{' '}
{actionsCount === 1 ? 'action' : 'actions'}.
</strong>
<Link component={RouterLink} to='../..'>
<strong>
{featureCount} active feature{' '}
{featureCount === 1 ? 'flag' : 'flags'}.
</strong>
</Link>
</p>
}
/>
<StyledButtonContainer>
<PermissionButton
permission={DELETE_PROJECT}
disabled={featureCount > 0}
disabled={disabled}
projectId={projectId}
onClick={() => {
setShowArchiveDialog(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -106,7 +108,8 @@ export const DeleteProject = ({
</PermissionButton>
</StyledButtonContainer>
<DeleteProjectDialogue
project={projectId}
projectId={projectId}
projectName={projectName}
open={showDelDialog}
onClose={() => {
setShowDelDialog(false);
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/component/project/ProjectId/ProjectId.tsx
Original file line number Diff line number Diff line change
@@ -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,
}));
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const ArchiveProjectList: FC = () => {
const [deleteProject, setDeleteProject] = useState<{
isOpen: boolean;
id?: string;
name?: string;
}>({ isOpen: false });

useEffect(() => {
Expand Down Expand Up @@ -76,6 +77,7 @@ export const ArchiveProjectList: FC = () => {
onDelete={() =>
setDeleteProject({
id,
name: projects?.find((project) => project.id === id)?.name,
isOpen: true,
})
}
Expand Down Expand Up @@ -155,7 +157,8 @@ export const ArchiveProjectList: FC = () => {
}
/>
<DeleteProjectDialogue
project={deleteProject.id || ''}
projectId={deleteProject.id || ''}
projectName={deleteProject.name || ''}
open={deleteProject.isOpen}
onClose={() => {
setDeleteProject((state) => ({ ...state, isOpen: false }));
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -11,6 +13,10 @@ type ReviveProjectDialogProps = {
onClose: () => void;
};

const StyledParagraph = styled(Typography)(({ theme }) => ({
marginBottom: theme.spacing(1),
}));

export const ReviveProjectDialog = ({
name,
id,
Expand All @@ -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));
Expand All @@ -43,14 +49,20 @@ export const ReviveProjectDialog = ({
return (
<Dialogue
open={open}
secondaryButtonText='Close'
onClose={onClose}
onClick={onClick}
title='Restore archived project'
title='Revive an archived project'
>
Are you sure you'd like to restore project <strong>{name}</strong>{' '}
(id: <code>{id}</code>)?
{/* TODO: more explanation */}
<StyledParagraph>
Are you sure you'd like to revive project{' '}
<strong>{name}</strong>?
</StyledParagraph>
<StyledParagraph>
Project ID: <ProjectId>{id}</ProjectId>
</StyledParagraph>
<StyledParagraph>
All flags in the revived project will remain archived.
</StyledParagraph>
</Dialogue>
);
};

0 comments on commit 004038e

Please sign in to comment.