mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	chore: new confirmation dialog for replacing release plans (#10720)
https://linear.app/unleash/issue/2-3931/add-a-confirmation-dialog-when-replacing-existing-release-plan Adds a confirmation dialog when replacing an already active release plan. <img width="706" height="325" alt="image" src="https://github.com/user-attachments/assets/f682809c-f563-4dca-9924-be1e9188c698" />
This commit is contained in:
		
							parent
							
								
									6c6d4c0ccc
								
							
						
					
					
						commit
						df67c041fc
					
				| @ -21,12 +21,13 @@ import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; | ||||
| import { formatUnknownError } from 'utils/formatUnknownError'; | ||||
| import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; | ||||
| import { LegacyReleasePlanReviewDialog } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/LegacyReleasePlanReviewDialog.tsx'; | ||||
| import { ReleasePlanPreview } from '../../FeatureView/FeatureOverview/ReleasePlan/ReleasePlanPreview.tsx'; | ||||
| import { ReleasePlanPreview } from './ReleasePlanPreview.tsx'; | ||||
| import { | ||||
|     FeatureStrategyMenuCards, | ||||
|     type StrategyFilterValue, | ||||
| } from './FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx'; | ||||
| import { useUiFlag } from 'hooks/useUiFlag.ts'; | ||||
| import { ReleasePlanConfirmationDialog } from './ReleasePlanConfirmationDialog.tsx'; | ||||
| 
 | ||||
| interface IFeatureStrategyMenuProps { | ||||
|     label: string; | ||||
| @ -78,6 +79,8 @@ export const FeatureStrategyMenu = ({ | ||||
|         useState<IReleasePlanTemplate>(); | ||||
|     const [addReleasePlanOpen, setAddReleasePlanOpen] = useState(false); | ||||
|     const [releasePlanPreview, setReleasePlanPreview] = useState(false); | ||||
|     const [addReleasePlanConfirmationOpen, setAddReleasePlanConfirmationOpen] = | ||||
|         useState(false); | ||||
|     const dialogId = isStrategyMenuDialogOpen | ||||
|         ? 'FeatureStrategyMenuDialog' | ||||
|         : undefined; | ||||
| @ -86,13 +89,19 @@ export const FeatureStrategyMenu = ({ | ||||
|     const { addChange } = useChangeRequestApi(); | ||||
|     const { refetch: refetchChangeRequests } = | ||||
|         usePendingChangeRequests(projectId); | ||||
|     const { refetch } = useReleasePlans(projectId, featureId, environmentId); | ||||
|     const { refetch, releasePlans } = useReleasePlans( | ||||
|         projectId, | ||||
|         featureId, | ||||
|         environmentId, | ||||
|     ); | ||||
|     const { addReleasePlanToFeature } = useReleasePlansApi(); | ||||
|     const { isEnterprise } = useUiConfig(); | ||||
|     const displayReleasePlanButton = isEnterprise(); | ||||
|     const crProtected = isChangeRequestConfigured(environmentId); | ||||
|     const newStrategyModalEnabled = useUiFlag('newStrategyModal'); | ||||
| 
 | ||||
|     const activeReleasePlan = releasePlans[0]; | ||||
| 
 | ||||
|     const onClose = () => { | ||||
|         setIsStrategyMenuDialogOpen(false); | ||||
|     }; | ||||
| @ -121,8 +130,15 @@ export const FeatureStrategyMenu = ({ | ||||
|         setIsStrategyMenuDialogOpen(true); | ||||
|     }; | ||||
| 
 | ||||
|     const addReleasePlan = async (template: IReleasePlanTemplate) => { | ||||
|     const addReleasePlan = async ( | ||||
|         template: IReleasePlanTemplate, | ||||
|         confirmed?: boolean, | ||||
|     ) => { | ||||
|         try { | ||||
|             if (newStrategyModalEnabled && !confirmed && activeReleasePlan) { | ||||
|                 setAddReleasePlanConfirmationOpen(true); | ||||
|                 return; | ||||
|             } | ||||
|             if (crProtected) { | ||||
|                 await addChange(projectId, environmentId, { | ||||
|                     feature: featureId, | ||||
| @ -153,18 +169,19 @@ export const FeatureStrategyMenu = ({ | ||||
| 
 | ||||
|                 refetch(); | ||||
|             } | ||||
| 
 | ||||
|             trackEvent('release-management', { | ||||
|                 props: { | ||||
|                     eventType: 'add-plan', | ||||
|                     plan: template.name, | ||||
|                 }, | ||||
|             }); | ||||
|         } catch (error: unknown) { | ||||
|             setToastApiError(formatUnknownError(error)); | ||||
|         } finally { | ||||
|             setAddReleasePlanConfirmationOpen(false); | ||||
|             setAddReleasePlanOpen(false); | ||||
|             setSelectedTemplate(undefined); | ||||
|             onClose(); | ||||
|         } catch (error: unknown) { | ||||
|             setToastApiError(formatUnknownError(error)); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| @ -284,6 +301,7 @@ export const FeatureStrategyMenu = ({ | ||||
|                                 projectId={projectId} | ||||
|                                 featureName={featureId} | ||||
|                                 environment={environmentId} | ||||
|                                 activeReleasePlan={activeReleasePlan} | ||||
|                                 crProtected={crProtected} | ||||
|                                 onBack={() => setReleasePlanPreview(false)} | ||||
|                                 onConfirm={() => { | ||||
| @ -329,23 +347,34 @@ export const FeatureStrategyMenu = ({ | ||||
|                 )} | ||||
|             </Dialog> | ||||
|             {selectedTemplate && ( | ||||
|                 <LegacyReleasePlanReviewDialog | ||||
|                     open={addReleasePlanOpen} | ||||
|                     setOpen={(open) => { | ||||
|                         setAddReleasePlanOpen(open); | ||||
|                         if (!open) { | ||||
|                             setIsStrategyMenuDialogOpen(true); | ||||
|                         } | ||||
|                     }} | ||||
|                     onConfirm={() => { | ||||
|                         addReleasePlan(selectedTemplate); | ||||
|                     }} | ||||
|                     template={selectedTemplate} | ||||
|                     projectId={projectId} | ||||
|                     featureName={featureId} | ||||
|                     environment={environmentId} | ||||
|                     crProtected={crProtected} | ||||
|                 /> | ||||
|                 <> | ||||
|                     <LegacyReleasePlanReviewDialog | ||||
|                         open={addReleasePlanOpen} | ||||
|                         setOpen={(open) => { | ||||
|                             setAddReleasePlanOpen(open); | ||||
|                             if (!open) { | ||||
|                                 setIsStrategyMenuDialogOpen(true); | ||||
|                             } | ||||
|                         }} | ||||
|                         onConfirm={() => { | ||||
|                             addReleasePlan(selectedTemplate); | ||||
|                         }} | ||||
|                         template={selectedTemplate} | ||||
|                         projectId={projectId} | ||||
|                         featureName={featureId} | ||||
|                         environment={environmentId} | ||||
|                         crProtected={crProtected} | ||||
|                     /> | ||||
|                     <ReleasePlanConfirmationDialog | ||||
|                         template={selectedTemplate} | ||||
|                         crProtected={crProtected} | ||||
|                         open={addReleasePlanConfirmationOpen} | ||||
|                         setOpen={setAddReleasePlanConfirmationOpen} | ||||
|                         onConfirm={() => { | ||||
|                             addReleasePlan(selectedTemplate, true); | ||||
|                         }} | ||||
|                     /> | ||||
|                 </> | ||||
|             )} | ||||
|         </StyledStrategyMenu> | ||||
|     ); | ||||
|  | ||||
| @ -0,0 +1,35 @@ | ||||
| import type React from 'react'; | ||||
| import { Dialogue } from 'component/common/Dialogue/Dialogue'; | ||||
| import type { IReleasePlanTemplate } from 'interfaces/releasePlans'; | ||||
| 
 | ||||
| interface IReleasePlanConfirmationDialogProps { | ||||
|     template: IReleasePlanTemplate; | ||||
|     crProtected: boolean; | ||||
|     open: boolean; | ||||
|     setOpen: React.Dispatch<React.SetStateAction<boolean>>; | ||||
|     onConfirm: () => void; | ||||
| } | ||||
| 
 | ||||
| export const ReleasePlanConfirmationDialog = ({ | ||||
|     template, | ||||
|     crProtected, | ||||
|     open, | ||||
|     setOpen, | ||||
|     onConfirm, | ||||
| }: IReleasePlanConfirmationDialogProps) => ( | ||||
|     <Dialogue | ||||
|         title='Replace release plan?' | ||||
|         open={open} | ||||
|         primaryButtonText={ | ||||
|             crProtected ? 'Add suggestion to draft' : 'Add release plan' | ||||
|         } | ||||
|         secondaryButtonText='Close' | ||||
|         onClick={onConfirm} | ||||
|         onClose={() => { | ||||
|             setOpen(false); | ||||
|         }} | ||||
|     > | ||||
|         This environment currently has a release plan added. Do you want to | ||||
|         replace it with <strong>{template.name}</strong>? | ||||
|     </Dialogue> | ||||
| ); | ||||
| @ -1,5 +1,8 @@ | ||||
| import type { IReleasePlanTemplate } from 'interfaces/releasePlans'; | ||||
| import { ReleasePlan } from './ReleasePlan.tsx'; | ||||
| import type { | ||||
|     IReleasePlan, | ||||
|     IReleasePlanTemplate, | ||||
| } from 'interfaces/releasePlans'; | ||||
| import { ReleasePlan } from '../../FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx'; | ||||
| import { useReleasePlanPreview } from 'hooks/useReleasePlanPreview'; | ||||
| import { | ||||
|     styled, | ||||
| @ -9,9 +12,8 @@ import { | ||||
|     DialogActions, | ||||
|     Button, | ||||
| } from '@mui/material'; | ||||
| import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; | ||||
| import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePlans'; | ||||
| import ArrowBackIcon from '@mui/icons-material/ArrowBack'; | ||||
| import { useFeature } from 'hooks/api/getters/useFeature/useFeature.ts'; | ||||
| 
 | ||||
| const StyledScrollableContent = styled(Box)(({ theme }) => ({ | ||||
|     width: theme.breakpoints.values.md, | ||||
| @ -38,6 +40,8 @@ interface IReleasePlanPreviewProps { | ||||
|     projectId: string; | ||||
|     featureName: string; | ||||
|     environment: string; | ||||
|     environmentEnabled?: boolean; | ||||
|     activeReleasePlan?: IReleasePlan; | ||||
|     crProtected?: boolean; | ||||
|     onConfirm: () => void; | ||||
|     onBack: () => void; | ||||
| @ -48,19 +52,12 @@ export const ReleasePlanPreview = ({ | ||||
|     projectId, | ||||
|     featureName, | ||||
|     environment, | ||||
|     activeReleasePlan, | ||||
|     crProtected, | ||||
|     onConfirm, | ||||
|     onBack, | ||||
| }: IReleasePlanPreviewProps) => { | ||||
|     const { feature } = useFeature(projectId, featureName); | ||||
|     const { releasePlans, loading } = useReleasePlans( | ||||
|         projectId, | ||||
|         featureName, | ||||
|         environment, | ||||
|     ); | ||||
| 
 | ||||
|     const activeReleasePlan = releasePlans[0]; | ||||
| 
 | ||||
|     const environmentData = feature?.environments.find( | ||||
|         ({ name }) => name === environment, | ||||
|     ); | ||||
| @ -72,8 +69,6 @@ export const ReleasePlanPreview = ({ | ||||
|         environment, | ||||
|     ); | ||||
| 
 | ||||
|     if (loading) return null; | ||||
| 
 | ||||
|     return ( | ||||
|         <> | ||||
|             <StyledSubHeader> | ||||
| @ -85,13 +80,17 @@ export const ReleasePlanPreview = ({ | ||||
|             <StyledScrollableContent> | ||||
|                 {activeReleasePlan && ( | ||||
|                     <Box sx={{ px: 4, pb: 2 }}> | ||||
|                         <Alert severity='error'> | ||||
|                         <Alert severity='warning'> | ||||
|                             This feature environment currently has{' '} | ||||
|                             <strong>{activeReleasePlan.name}</strong> -{' '} | ||||
|                             <strong>{activeReleasePlan.name}</strong> ( | ||||
|                             <strong> | ||||
|                                 {activeReleasePlan.milestones[0].name} | ||||
|                                 {activeReleasePlan.milestones.find( | ||||
|                                     ({ id }) => | ||||
|                                         activeReleasePlan.activeMilestoneId === | ||||
|                                         id, | ||||
|                                 )?.name ?? activeReleasePlan.milestones[0].name} | ||||
|                             </strong> | ||||
|                             {environmentEnabled ? ' running' : ' paused'}. | ||||
|                             ){environmentEnabled ? ' running' : ' paused'}. | ||||
|                             Adding a new release plan will replace the existing | ||||
|                             release plan. | ||||
|                         </Alert> | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user