mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: Add transition condition UI for release plan milestones (#10768)
This commit is contained in:
		
							parent
							
								
									247dd3af51
								
							
						
					
					
						commit
						a922801690
					
				| @ -69,7 +69,7 @@ export const EnvironmentAccordionBody = ({ | ||||
|     const { releasePlans } = useFeatureReleasePlans( | ||||
|         projectId, | ||||
|         featureId, | ||||
|         featureEnvironment, | ||||
|         featureEnvironment?.name, | ||||
|     ); | ||||
|     const { trackEvent } = usePlausibleTracker(); | ||||
| 
 | ||||
|  | ||||
| @ -21,7 +21,7 @@ const FeatureOverviewWithReleasePlans: FC< | ||||
|     const { releasePlans } = useFeatureReleasePlans( | ||||
|         projectId, | ||||
|         featureId, | ||||
|         environment, | ||||
|         environment?.name, | ||||
|     ); | ||||
|     const envAddStrategySuggestionEnabled = useUiFlag( | ||||
|         'envAddStrategySuggestion', | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| import Add from '@mui/icons-material/Add'; | ||||
| import { Button, styled } from '@mui/material'; | ||||
| import type { MilestoneStatus } from './ReleasePlanMilestoneStatus.tsx'; | ||||
| import { MilestoneTransitionDisplay } from './MilestoneTransitionDisplay.tsx'; | ||||
| 
 | ||||
| const StyledAutomationContainer = styled('div', { | ||||
|     shouldForwardProp: (prop) => prop !== 'status', | ||||
| @ -51,6 +52,9 @@ interface IMilestoneAutomationSectionProps { | ||||
|     status?: MilestoneStatus; | ||||
|     onAddAutomation?: () => void; | ||||
|     automationForm?: React.ReactNode; | ||||
|     transitionCondition?: { | ||||
|         intervalMinutes: number; | ||||
|     } | null; | ||||
| } | ||||
| 
 | ||||
| export const MilestoneAutomationSection = ({ | ||||
| @ -58,6 +62,7 @@ export const MilestoneAutomationSection = ({ | ||||
|     status, | ||||
|     onAddAutomation, | ||||
|     automationForm, | ||||
|     transitionCondition, | ||||
| }: IMilestoneAutomationSectionProps) => { | ||||
|     if (!showAutomation) return null; | ||||
| 
 | ||||
| @ -65,6 +70,10 @@ export const MilestoneAutomationSection = ({ | ||||
|         <StyledAutomationContainer status={status}> | ||||
|             {automationForm ? ( | ||||
|                 automationForm | ||||
|             ) : transitionCondition ? ( | ||||
|                 <MilestoneTransitionDisplay | ||||
|                     intervalMinutes={transitionCondition.intervalMinutes} | ||||
|                 /> | ||||
|             ) : ( | ||||
|                 <StyledAddAutomationButton | ||||
|                     onClick={onAddAutomation} | ||||
|  | ||||
| @ -0,0 +1,55 @@ | ||||
| import BoltIcon from '@mui/icons-material/Bolt'; | ||||
| import { styled } from '@mui/material'; | ||||
| import { formatDuration, intervalToDuration } from 'date-fns'; | ||||
| 
 | ||||
| const StyledDisplayContainer = styled('div')(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     alignItems: 'center', | ||||
|     gap: theme.spacing(1), | ||||
| })); | ||||
| 
 | ||||
| const StyledIcon = styled(BoltIcon)(({ theme }) => ({ | ||||
|     color: theme.palette.common.white, | ||||
|     fontSize: 18, | ||||
|     flexShrink: 0, | ||||
|     backgroundColor: theme.palette.primary.main, | ||||
|     borderRadius: '50%', | ||||
|     padding: theme.spacing(0.25), | ||||
| })); | ||||
| 
 | ||||
| const StyledText = styled('span')(({ theme }) => ({ | ||||
|     color: theme.palette.text.primary, | ||||
|     fontSize: theme.typography.body2.fontSize, | ||||
| })); | ||||
| 
 | ||||
| interface IMilestoneTransitionDisplayProps { | ||||
|     intervalMinutes: number; | ||||
| } | ||||
| 
 | ||||
| const formatInterval = (minutes: number): string => { | ||||
|     if (minutes === 0) return '0 minutes'; | ||||
| 
 | ||||
|     const duration = intervalToDuration({ | ||||
|         start: 0, | ||||
|         end: minutes * 60 * 1000, | ||||
|     }); | ||||
| 
 | ||||
|     return formatDuration(duration, { | ||||
|         format: ['days', 'hours', 'minutes'], | ||||
|         delimiter: ', ', | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| export const MilestoneTransitionDisplay = ({ | ||||
|     intervalMinutes, | ||||
| }: IMilestoneTransitionDisplayProps) => { | ||||
|     return ( | ||||
|         <StyledDisplayContainer> | ||||
|             <StyledIcon /> | ||||
|             <StyledText> | ||||
|                 Proceed to the next milestone after{' '} | ||||
|                 {formatInterval(intervalMinutes)} | ||||
|             </StyledText> | ||||
|         </StyledDisplayContainer> | ||||
|     ); | ||||
| }; | ||||
| @ -118,6 +118,7 @@ export const ReleasePlanMilestone = ({ | ||||
|                     status={status} | ||||
|                     onAddAutomation={onAddAutomation} | ||||
|                     automationForm={automationForm} | ||||
|                     transitionCondition={milestone.transitionCondition} | ||||
|                 /> | ||||
|             </StyledMilestoneContainer> | ||||
|         ); | ||||
| @ -174,6 +175,7 @@ export const ReleasePlanMilestone = ({ | ||||
|                 status={status} | ||||
|                 onAddAutomation={onAddAutomation} | ||||
|                 automationForm={automationForm} | ||||
|                 transitionCondition={milestone.transitionCondition} | ||||
|             /> | ||||
|         </StyledMilestoneContainer> | ||||
|     ); | ||||
|  | ||||
| @ -1,28 +1,28 @@ | ||||
| import { useUiFlag } from 'hooks/useUiFlag'; | ||||
| import { useReleasePlans } from '../useReleasePlans/useReleasePlans.js'; | ||||
| import { useFeature } from '../useFeature/useFeature.js'; | ||||
| import type { IFeatureEnvironment } from 'interfaces/featureToggle'; | ||||
| 
 | ||||
| export const useFeatureReleasePlans = ( | ||||
|     projectId: string, | ||||
|     featureId: string, | ||||
|     environment?: IFeatureEnvironment | string, | ||||
|     environmentName?: string, | ||||
| ) => { | ||||
|     const featureReleasePlansEnabled = useUiFlag('featureReleasePlans'); | ||||
|     const envName = | ||||
|         typeof environment === 'string' ? environment : environment?.name; | ||||
|     const { | ||||
|         releasePlans: releasePlansFromHook, | ||||
|         refetch: refetchReleasePlans, | ||||
|         ...rest | ||||
|     } = useReleasePlans(projectId, featureId, envName); | ||||
|     const { refetchFeature } = useFeature(projectId, featureId); | ||||
|     } = useReleasePlans(projectId, featureId, environmentName); | ||||
|     const { feature, refetchFeature } = useFeature(projectId, featureId); | ||||
| 
 | ||||
|     const releasePlans = featureReleasePlansEnabled | ||||
|         ? typeof environment === 'object' | ||||
|             ? environment?.releasePlans || [] | ||||
|             : [] | ||||
|         : releasePlansFromHook; | ||||
|     let releasePlans = releasePlansFromHook; | ||||
| 
 | ||||
|     if (featureReleasePlansEnabled) { | ||||
|         const matchingEnvironment = feature?.environments?.find( | ||||
|             (env) => env.name === environmentName, | ||||
|         ); | ||||
|         releasePlans = matchingEnvironment?.releasePlans || []; | ||||
|     } | ||||
| 
 | ||||
|     const refetch = featureReleasePlansEnabled | ||||
|         ? refetchFeature | ||||
|  | ||||
| @ -35,6 +35,9 @@ export interface IReleasePlanMilestone { | ||||
|     name: string; | ||||
|     releasePlanDefinitionId: string; | ||||
|     strategies: IReleasePlanMilestoneStrategy[]; | ||||
|     transitionCondition?: { | ||||
|         intervalMinutes: number; | ||||
|     } | null; | ||||
| } | ||||
| 
 | ||||
| export interface IReleasePlanMilestoneStrategy extends IFeatureStrategy { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user