From 7ea14b8d22082ef32f6b008670e961cbc2da5902 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Wed, 19 Nov 2025 12:47:47 +0100 Subject: [PATCH] feat: resume milestone progressions (#10999) --- .../ReleasePlan/ReleasePlan.tsx | 49 +++++++++++++++++-- .../useMilestoneProgressionsApi.ts | 20 ++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx index eaa3acacb2..97a7ac9e72 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx @@ -1,5 +1,6 @@ import Delete from '@mui/icons-material/Delete'; -import { Alert, styled } from '@mui/material'; +import { Alert, styled, Link } from '@mui/material'; +import PlayCircle from '@mui/icons-material/PlayCircle'; import { DELETE_FEATURE_STRATEGY } from '@server/types/permissions'; import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; import { useReleasePlansApi } from 'hooks/api/actions/useReleasePlansApi/useReleasePlansApi'; @@ -106,6 +107,14 @@ const StyledMilestones = styled('div', { }), })); +const StyledResumeMilestoneProgressions = styled(Link)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(0.5), + textDecoration: 'none', + color: 'inherit', +})); + interface IReleasePlanProps { plan: IReleasePlan; environmentIsDisabled?: boolean; @@ -136,8 +145,11 @@ export const ReleasePlan = ({ ); const { removeReleasePlanFromFeature, startReleasePlanMilestone } = useReleasePlansApi(); - const { deleteMilestoneProgression, loading: milestoneProgressionLoading } = - useMilestoneProgressionsApi(); + const { + deleteMilestoneProgression, + resumeMilestoneProgressions, + loading: milestoneProgressionLoading, + } = useMilestoneProgressionsApi(); const { createOrUpdateSafeguard, deleteSafeguard, @@ -389,6 +401,24 @@ export const ReleasePlan = ({ } }; + const onResumeMilestoneProgressions = async () => { + try { + await resumeMilestoneProgressions( + projectId, + environment, + featureName, + id, + ); + setToastData({ + type: 'success', + text: 'Automation resumed successfully', + }); + refetch(); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); + } + }; + const activeIndex = milestones.findIndex( (milestone) => milestone.id === activeMilestoneId, ); @@ -477,7 +507,18 @@ export const ReleasePlan = ({ {releasePlanAutomationsPaused ? ( - + + + Resume automation + + } + > Automation paused by safeguard. Existing users on this release plan can still access the feature. diff --git a/frontend/src/hooks/api/actions/useMilestoneProgressionsApi/useMilestoneProgressionsApi.ts b/frontend/src/hooks/api/actions/useMilestoneProgressionsApi/useMilestoneProgressionsApi.ts index 50cea7805f..3df72750e9 100644 --- a/frontend/src/hooks/api/actions/useMilestoneProgressionsApi/useMilestoneProgressionsApi.ts +++ b/frontend/src/hooks/api/actions/useMilestoneProgressionsApi/useMilestoneProgressionsApi.ts @@ -46,9 +46,29 @@ export const useMilestoneProgressionsApi = () => { await makeRequest(req.caller, req.id); }; + const resumeMilestoneProgressions = async ( + projectId: string, + environment: string, + featureName: string, + planId: string, + ): Promise => { + const requestId = 'resumeProgressions'; + const path = `api/admin/projects/${projectId}/features/${featureName}/environments/${environment}/progressions/${planId}/resume`; + const req = createRequest( + path, + { + method: 'POST', + }, + requestId, + ); + + await makeRequest(req.caller, req.id); + }; + return { changeMilestoneProgression, deleteMilestoneProgression, + resumeMilestoneProgressions, errors, loading, };