mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: archive project form (#7797)
This commit is contained in:
		
							parent
							
								
									8caa1e242c
								
							
						
					
					
						commit
						a01305040d
					
				| @ -0,0 +1,56 @@ | ||||
| import { Dialogue } from 'component/common/Dialogue/Dialogue'; | ||||
| import type React from 'react'; | ||||
| import { formatUnknownError } from 'utils/formatUnknownError'; | ||||
| import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi'; | ||||
| import useProjects from 'hooks/api/getters/useProjects/useProjects'; | ||||
| import useToast from 'hooks/useToast'; | ||||
| import { Typography } from '@mui/material'; | ||||
| 
 | ||||
| interface IDeleteProjectDialogueProps { | ||||
|     project: string; | ||||
|     open: boolean; | ||||
|     onClose: (e: React.SyntheticEvent) => void; | ||||
|     onSuccess?: () => void; | ||||
| } | ||||
| 
 | ||||
| export const ArchiveProjectDialogue = ({ | ||||
|     open, | ||||
|     onClose, | ||||
|     project, | ||||
|     onSuccess, | ||||
| }: IDeleteProjectDialogueProps) => { | ||||
|     const { archiveProject } = useProjectApi(); | ||||
|     const { refetch: refetchProjectOverview } = useProjects(); | ||||
|     const { setToastData, setToastApiError } = useToast(); | ||||
| 
 | ||||
|     const onClick = async (e: React.SyntheticEvent) => { | ||||
|         e.preventDefault(); | ||||
|         try { | ||||
|             await archiveProject(project); | ||||
|             refetchProjectOverview(); | ||||
|             setToastData({ | ||||
|                 title: 'Archived project', | ||||
|                 type: 'success', | ||||
|                 text: 'Successfully archived project', | ||||
|             }); | ||||
|             onSuccess?.(); | ||||
|         } catch (ex: unknown) { | ||||
|             setToastApiError(formatUnknownError(ex)); | ||||
|         } | ||||
|         onClose(e); | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <Dialogue | ||||
|             open={open} | ||||
|             onClick={onClick} | ||||
|             onClose={onClose} | ||||
|             title='Really archive project' | ||||
|         > | ||||
|             <Typography> | ||||
|                 This will archive the project and all feature flags archived in | ||||
|                 it. | ||||
|             </Typography> | ||||
|         </Dialogue> | ||||
|     ); | ||||
| }; | ||||
| @ -0,0 +1,108 @@ | ||||
| import { 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 { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||
| import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; | ||||
| import { ArchiveProjectDialogue } from '../../ArchiveProject/ArchiveProjectDialogue'; | ||||
| 
 | ||||
| const StyledContainer = styled('div')(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     flexDirection: 'column', | ||||
|     borderTop: `1px solid ${theme.palette.divider}`, | ||||
|     paddingTop: theme.spacing(4), | ||||
|     gap: theme.spacing(2), | ||||
| })); | ||||
| 
 | ||||
| const StyledButtonContainer = styled('div')(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     justifyContent: 'flex-end', | ||||
|     paddingTop: theme.spacing(3), | ||||
| })); | ||||
| 
 | ||||
| interface IDeleteProjectProps { | ||||
|     projectId: string; | ||||
|     featureCount: number; | ||||
| } | ||||
| 
 | ||||
| export const ArchiveProject = ({ | ||||
|     projectId, | ||||
|     featureCount, | ||||
| }: IDeleteProjectProps) => { | ||||
|     const { isEnterprise } = useUiConfig(); | ||||
|     const automatedActionsEnabled = useUiFlag('automatedActions'); | ||||
|     const archiveProjectsEnabled = useUiFlag('archiveProjects'); | ||||
|     const { actions } = useActions(projectId); | ||||
|     const [showArchiveDialog, setShowArchiveDialog] = useState(false); | ||||
|     const actionsCount = actions.filter(({ enabled }) => enabled).length; | ||||
|     const navigate = useNavigate(); | ||||
|     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' | ||||
|                     : ''} | ||||
|                 . | ||||
|             </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> | ||||
|                     </p> | ||||
|                 } | ||||
|             /> | ||||
|             <StyledButtonContainer> | ||||
|                 <PermissionButton | ||||
|                     permission={DELETE_PROJECT} | ||||
|                     disabled={featureCount > 0} | ||||
|                     projectId={projectId} | ||||
|                     onClick={() => { | ||||
|                         setShowArchiveDialog(true); | ||||
|                     }} | ||||
|                     tooltipProps={{ | ||||
|                         title: 'Archive project', | ||||
|                     }} | ||||
|                     data-loading | ||||
|                 > | ||||
|                     Archive project | ||||
|                 </PermissionButton> | ||||
|             </StyledButtonContainer> | ||||
|             <ArchiveProjectDialogue | ||||
|                 project={projectId} | ||||
|                 open={showArchiveDialog} | ||||
|                 onClose={() => { | ||||
|                     setShowArchiveDialog(false); | ||||
|                 }} | ||||
|                 onSuccess={() => { | ||||
|                     navigate('/projects'); | ||||
|                 }} | ||||
|             /> | ||||
|         </StyledContainer> | ||||
|     ); | ||||
| }; | ||||
| @ -0,0 +1,43 @@ | ||||
| import FormTemplate from 'component/common/FormTemplate/FormTemplate'; | ||||
| import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; | ||||
| import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi'; | ||||
| import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; | ||||
| import { styled } from '@mui/material'; | ||||
| import { ArchiveProject } from '../ArchiveProject'; | ||||
| 
 | ||||
| const StyledContainer = styled('div')(({ theme }) => ({ | ||||
|     borderRadius: theme.spacing(2), | ||||
|     overflow: 'hidden', | ||||
| })); | ||||
| 
 | ||||
| interface IDeleteProjectForm { | ||||
|     featureCount: number; | ||||
| } | ||||
| export const ArchiveProjectForm = ({ featureCount }: IDeleteProjectForm) => { | ||||
|     const id = useRequiredPathParam('projectId'); | ||||
|     const { uiConfig } = useUiConfig(); | ||||
|     const { loading } = useProjectApi(); | ||||
|     const formatProjectArchiveApiCode = () => { | ||||
|         return `curl --location --request DELETE '${uiConfig.unleashUrl}/api/admin/projects/${id}/archive' \\ | ||||
| --header 'Authorization: INSERT_API_KEY' '`;
 | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <StyledContainer> | ||||
|             <FormTemplate | ||||
|                 loading={loading} | ||||
|                 title='Archive project' | ||||
|                 description='' | ||||
|                 documentationLink='https://docs.getunleash.io/reference/projects' | ||||
|                 documentationLinkLabel='Projects documentation' | ||||
|                 formatApiCode={formatProjectArchiveApiCode} | ||||
|                 compact | ||||
|                 compactPadding | ||||
|                 showDescription={false} | ||||
|                 showLink={false} | ||||
|             > | ||||
|                 <ArchiveProject projectId={id} featureCount={featureCount} /> | ||||
|             </FormTemplate> | ||||
|         </StyledContainer> | ||||
|     ); | ||||
| }; | ||||
| @ -14,6 +14,8 @@ import { DeleteProjectForm } from './DeleteProjectForm'; | ||||
| import useProjectOverview, { | ||||
|     featuresCount, | ||||
| } from 'hooks/api/getters/useProjectOverview/useProjectOverview'; | ||||
| import { ArchiveProjectForm } from './ArchiveProjectForm'; | ||||
| import { useUiFlag } from 'hooks/useUiFlag'; | ||||
| 
 | ||||
| const StyledFormContainer = styled('div')(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
| @ -26,6 +28,7 @@ const EditProject = () => { | ||||
|     const { hasAccess } = useContext(AccessContext); | ||||
|     const id = useRequiredPathParam('projectId'); | ||||
|     const { project } = useProjectOverview(id); | ||||
|     const archiveProjectsEnabled = useUiFlag('archiveProjects'); | ||||
| 
 | ||||
|     if (!project.name) { | ||||
|         return null; | ||||
| @ -49,7 +52,19 @@ const EditProject = () => { | ||||
|                     condition={isEnterprise()} | ||||
|                     show={<UpdateEnterpriseSettings project={project} />} | ||||
|                 /> | ||||
|                 <DeleteProjectForm featureCount={featuresCount(project)} /> | ||||
|                 <ConditionallyRender | ||||
|                     condition={archiveProjectsEnabled} | ||||
|                     show={ | ||||
|                         <ArchiveProjectForm | ||||
|                             featureCount={featuresCount(project)} | ||||
|                         /> | ||||
|                     } | ||||
|                     elseShow={ | ||||
|                         <DeleteProjectForm | ||||
|                             featureCount={featuresCount(project)} | ||||
|                         /> | ||||
|                     } | ||||
|                 /> | ||||
|             </StyledFormContainer> | ||||
|         </> | ||||
|     ); | ||||
|  | ||||
| @ -80,6 +80,15 @@ const useProjectApi = () => { | ||||
|         return res; | ||||
|     }; | ||||
| 
 | ||||
|     const archiveProject = async (projectId: string) => { | ||||
|         const path = `api/admin/projects/${projectId}/archive`; | ||||
|         const req = createRequest(path, { method: 'POST' }); | ||||
| 
 | ||||
|         const res = await makeRequest(req.caller, req.id); | ||||
| 
 | ||||
|         return res; | ||||
|     }; | ||||
| 
 | ||||
|     const addEnvironmentToProject = async ( | ||||
|         projectId: string, | ||||
|         environment: string, | ||||
| @ -253,6 +262,7 @@ const useProjectApi = () => { | ||||
|         editProject, | ||||
|         editProjectSettings, | ||||
|         deleteProject, | ||||
|         archiveProject, | ||||
|         addEnvironmentToProject, | ||||
|         removeEnvironmentFromProject, | ||||
|         addAccessToProject, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user