1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-28 17:55:15 +02:00

feat: UI for archiving release templates (#9415)

This commit is contained in:
David Leek 2025-03-05 08:08:57 +01:00 committed by GitHub
parent 065fd4b469
commit f6bb94addf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 76 additions and 48 deletions

View File

@ -16,11 +16,13 @@ import { useReleasePlanTemplatesApi } from 'hooks/api/actions/useReleasePlanTemp
import { useReleasePlanTemplates } from 'hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates'; import { useReleasePlanTemplates } from 'hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates';
import useToast from 'hooks/useToast'; import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError'; import { formatUnknownError } from 'utils/formatUnknownError';
import { TemplateDeleteDialog } from '../TemplateDeleteDialog'; import { TemplateArchiveDialog } from '../TemplateArchiveDialog';
import EditIcon from '@mui/icons-material/Edit'; import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from '@mui/icons-material/Delete';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { RELEASE_PLAN_TEMPLATE_DELETE } from '@server/types/permissions';
import { useHasRootAccess } from 'hooks/useHasAccess';
const StyledActions = styled('div')(({ theme }) => ({ const StyledActions = styled('div')(({ theme }) => ({
margin: theme.spacing(-1), margin: theme.spacing(-1),
@ -39,30 +41,31 @@ export const ReleasePlanTemplateCardActions = ({
template, template,
}: { template: IReleasePlanTemplate }) => { }: { template: IReleasePlanTemplate }) => {
const [anchorEl, setAnchorEl] = useState<Element | null>(null); const [anchorEl, setAnchorEl] = useState<Element | null>(null);
const { deleteReleasePlanTemplate } = useReleasePlanTemplatesApi(); const { archiveReleasePlanTemplate } = useReleasePlanTemplatesApi();
const { refetch } = useReleasePlanTemplates(); const { refetch } = useReleasePlanTemplates();
const { setToastData, setToastApiError } = useToast(); const { setToastData, setToastApiError } = useToast();
const { trackEvent } = usePlausibleTracker(); const { trackEvent } = usePlausibleTracker();
const [deleteOpen, setDeleteOpen] = useState(false); const [archiveOpen, setArchiveOpen] = useState(false);
const deleteReleasePlan = useCallback(async () => { const hasArchivePermission = useHasRootAccess(RELEASE_PLAN_TEMPLATE_DELETE);
const archiveReleasePlan = useCallback(async () => {
try { try {
await deleteReleasePlanTemplate(template.id); await archiveReleasePlanTemplate(template.id);
refetch(); refetch();
setToastData({ setToastData({
type: 'success', type: 'success',
text: 'Release plan template deleted', text: 'Release plan template archived',
}); });
trackEvent('release-management', { trackEvent('release-management', {
props: { props: {
eventType: 'delete-template', eventType: 'archive-template',
template: template.name, template: template.name,
}, },
}); });
} catch (error: unknown) { } catch (error: unknown) {
setToastApiError(formatUnknownError(error)); setToastApiError(formatUnknownError(error));
} }
}, [setToastApiError, refetch, setToastData, deleteReleasePlanTemplate]); }, [setToastApiError, refetch, setToastData, archiveReleasePlanTemplate]);
const open = Boolean(anchorEl); const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
@ -121,26 +124,27 @@ export const ReleasePlanTemplateCardActions = ({
</MenuItem> </MenuItem>
<MenuItem <MenuItem
onClick={() => { onClick={() => {
setDeleteOpen(true); setArchiveOpen(true);
handleClose(); handleClose();
}} }}
disabled={!hasArchivePermission}
> >
<ListItemIcon> <ListItemIcon>
<DeleteIcon /> <DeleteIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography variant='body2'> <Typography variant='body2'>
Delete template Archive template
</Typography> </Typography>
</ListItemText> </ListItemText>
</MenuItem> </MenuItem>
</MenuList> </MenuList>
</StyledPopover> </StyledPopover>
<TemplateDeleteDialog <TemplateArchiveDialog
template={template} template={template}
open={deleteOpen} open={archiveOpen}
setOpen={setDeleteOpen} setOpen={setArchiveOpen}
onConfirm={deleteReleasePlan} onConfirm={archiveReleasePlan}
/> />
</StyledActions> </StyledActions>
); );

View File

@ -0,0 +1,43 @@
import { RELEASE_PLAN_TEMPLATE_DELETE } from '@server/types/permissions';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
import type { IReleasePlanTemplate } from 'interfaces/releasePlans';
interface ITemplateArchiveDialogProps {
template?: IReleasePlanTemplate;
open: boolean;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
onConfirm: (template: IReleasePlanTemplate) => void;
}
export const TemplateArchiveDialog: React.FC<ITemplateArchiveDialogProps> = ({
template,
open,
setOpen,
onConfirm,
}) => {
return (
<Dialogue
title='Archive release plan template?'
open={open}
secondaryButtonText='Cancel'
onClose={() => {
setOpen(false);
}}
permissionButton={
<PermissionButton
variant='contained'
onClick={() => onConfirm(template!)}
permission={RELEASE_PLAN_TEMPLATE_DELETE}
>
Archive template
</PermissionButton>
}
>
<p>
You are about to archive release plan template:{' '}
<strong>{template?.name}</strong>
</p>
</Dialogue>
);
};

View File

@ -1,34 +0,0 @@
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import type { IReleasePlanTemplate } from 'interfaces/releasePlans';
interface ITemplateDeleteDialogProps {
template?: IReleasePlanTemplate;
open: boolean;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
onConfirm: (template: IReleasePlanTemplate) => void;
}
export const TemplateDeleteDialog: React.FC<ITemplateDeleteDialogProps> = ({
template,
open,
setOpen,
onConfirm,
}) => {
return (
<Dialogue
title='Delete release plan template?'
open={open}
primaryButtonText='Delete template'
secondaryButtonText='Cancel'
onClick={() => onConfirm(template!)}
onClose={() => {
setOpen(false);
}}
>
<p>
You are about to delete release plan template:{' '}
<strong>{template?.name}</strong>
</p>
</Dialogue>
);
};

View File

@ -60,10 +60,25 @@ export const useReleasePlanTemplatesApi = () => {
return makeRequest(req.caller, req.id); return makeRequest(req.caller, req.id);
}; };
const archiveReleasePlanTemplate = async (templateId: string) => {
const requestId = 'updateReleasePlanTemplate';
const path = `api/admin/release-plan-templates/archive/${templateId}`;
const req = createRequest(
path,
{
method: 'POST',
},
requestId,
);
return makeRequest(req.caller, req.id);
};
return { return {
deleteReleasePlanTemplate, deleteReleasePlanTemplate,
updateReleasePlanTemplate, updateReleasePlanTemplate,
createReleasePlanTemplate, createReleasePlanTemplate,
archiveReleasePlanTemplate,
}; };
}; };