mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: add change request support for updating milestone progressions (#10819)
This commit is contained in:
		
							parent
							
								
									022226dd43
								
							
						
					
					
						commit
						795b674133
					
				@ -5,6 +5,7 @@ import type { IUser } from '../../interfaces/user.js';
 | 
			
		||||
import type {
 | 
			
		||||
    SetStrategySortOrderSchema,
 | 
			
		||||
    CreateMilestoneProgressionSchema,
 | 
			
		||||
    UpdateMilestoneProgressionSchema,
 | 
			
		||||
} from 'openapi';
 | 
			
		||||
import type { IReleasePlan } from 'interfaces/releasePlans';
 | 
			
		||||
 | 
			
		||||
@ -135,7 +136,8 @@ type ChangeRequestPayload =
 | 
			
		||||
    | ChangeRequestAddReleasePlan
 | 
			
		||||
    | ChangeRequestDeleteReleasePlan
 | 
			
		||||
    | ChangeRequestStartMilestone
 | 
			
		||||
    | ChangeRequestCreateMilestoneProgression;
 | 
			
		||||
    | ChangeRequestCreateMilestoneProgression
 | 
			
		||||
    | ChangeRequestUpdateMilestoneProgression;
 | 
			
		||||
 | 
			
		||||
export interface IChangeRequestAddStrategy extends IChangeRequestChangeBase {
 | 
			
		||||
    action: 'addStrategy';
 | 
			
		||||
@ -198,6 +200,12 @@ export interface IChangeRequestCreateMilestoneProgression
 | 
			
		||||
    payload: ChangeRequestCreateMilestoneProgression;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IChangeRequestUpdateMilestoneProgression
 | 
			
		||||
    extends IChangeRequestChangeBase {
 | 
			
		||||
    action: 'updateMilestoneProgression';
 | 
			
		||||
    payload: ChangeRequestUpdateMilestoneProgression;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IChangeRequestReorderStrategy
 | 
			
		||||
    extends IChangeRequestChangeBase {
 | 
			
		||||
    action: 'reorderStrategy';
 | 
			
		||||
@ -246,7 +254,8 @@ export type IFeatureChange =
 | 
			
		||||
    | IChangeRequestAddReleasePlan
 | 
			
		||||
    | IChangeRequestDeleteReleasePlan
 | 
			
		||||
    | IChangeRequestStartMilestone
 | 
			
		||||
    | IChangeRequestCreateMilestoneProgression;
 | 
			
		||||
    | IChangeRequestCreateMilestoneProgression
 | 
			
		||||
    | IChangeRequestUpdateMilestoneProgression;
 | 
			
		||||
 | 
			
		||||
export type ISegmentChange =
 | 
			
		||||
    | IChangeRequestUpdateSegment
 | 
			
		||||
@ -281,6 +290,11 @@ type ChangeRequestStartMilestone = {
 | 
			
		||||
 | 
			
		||||
type ChangeRequestCreateMilestoneProgression = CreateMilestoneProgressionSchema;
 | 
			
		||||
 | 
			
		||||
type ChangeRequestUpdateMilestoneProgression =
 | 
			
		||||
    UpdateMilestoneProgressionSchema & {
 | 
			
		||||
        sourceMilestoneId: string;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
export type ChangeRequestAddStrategy = Pick<
 | 
			
		||||
    IFeatureStrategy,
 | 
			
		||||
    | 'parameters'
 | 
			
		||||
@ -319,4 +333,5 @@ export type ChangeRequestAction =
 | 
			
		||||
    | 'addReleasePlan'
 | 
			
		||||
    | 'deleteReleasePlan'
 | 
			
		||||
    | 'startMilestone'
 | 
			
		||||
    | 'createMilestoneProgression';
 | 
			
		||||
    | 'createMilestoneProgression'
 | 
			
		||||
    | 'updateMilestoneProgression';
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,10 @@ import type {
 | 
			
		||||
    IReleasePlan,
 | 
			
		||||
    IReleasePlanMilestone,
 | 
			
		||||
} from 'interfaces/releasePlans';
 | 
			
		||||
import type { CreateMilestoneProgressionSchema } from 'openapi';
 | 
			
		||||
import type {
 | 
			
		||||
    CreateMilestoneProgressionSchema,
 | 
			
		||||
    UpdateMilestoneProgressionSchema,
 | 
			
		||||
} from 'openapi';
 | 
			
		||||
import { getTimeValueAndUnitFromMinutes } from '../hooks/useMilestoneProgressionForm.js';
 | 
			
		||||
 | 
			
		||||
const StyledBoldSpan = styled('span')(({ theme }) => ({
 | 
			
		||||
@ -23,6 +26,11 @@ type ChangeRequestAction =
 | 
			
		||||
    | {
 | 
			
		||||
          type: 'createMilestoneProgression';
 | 
			
		||||
          payload: CreateMilestoneProgressionSchema;
 | 
			
		||||
      }
 | 
			
		||||
    | {
 | 
			
		||||
          type: 'updateMilestoneProgression';
 | 
			
		||||
          sourceMilestoneId: string;
 | 
			
		||||
          payload: UpdateMilestoneProgressionSchema;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
interface IReleasePlanChangeRequestDialogProps {
 | 
			
		||||
@ -105,6 +113,27 @@ export const ReleasePlanChangeRequestDialog = ({
 | 
			
		||||
                    </p>
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            case 'updateMilestoneProgression': {
 | 
			
		||||
                const milestone = releasePlan.milestones.find(
 | 
			
		||||
                    (milestone) => milestone.id === action.sourceMilestoneId,
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                const { value, unit } = getTimeValueAndUnitFromMinutes(
 | 
			
		||||
                    action.payload.transitionCondition.intervalMinutes,
 | 
			
		||||
                );
 | 
			
		||||
                const timeInterval = `${value} ${unit}`;
 | 
			
		||||
 | 
			
		||||
                return (
 | 
			
		||||
                    <p>
 | 
			
		||||
                        Update automation for{' '}
 | 
			
		||||
                        <StyledBoldSpan>{milestone?.name}</StyledBoldSpan> to
 | 
			
		||||
                        proceed after{' '}
 | 
			
		||||
                        <StyledBoldSpan>{timeInterval}</StyledBoldSpan> in{' '}
 | 
			
		||||
                        {environmentId}
 | 
			
		||||
                    </p>
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,10 @@ import { useUiFlag } from 'hooks/useUiFlag';
 | 
			
		||||
import { MilestoneProgressionForm } from './MilestoneProgressionForm/MilestoneProgressionForm.tsx';
 | 
			
		||||
import { useMilestoneProgressionsApi } from 'hooks/api/actions/useMilestoneProgressionsApi/useMilestoneProgressionsApi';
 | 
			
		||||
import { DeleteProgressionDialog } from './DeleteProgressionDialog.tsx';
 | 
			
		||||
import type { CreateMilestoneProgressionSchema } from 'openapi';
 | 
			
		||||
import type {
 | 
			
		||||
    CreateMilestoneProgressionSchema,
 | 
			
		||||
    UpdateMilestoneProgressionSchema,
 | 
			
		||||
} from 'openapi';
 | 
			
		||||
 | 
			
		||||
const StyledContainer = styled('div')(({ theme }) => ({
 | 
			
		||||
    padding: theme.spacing(2),
 | 
			
		||||
@ -124,6 +127,11 @@ export const ReleasePlan = ({
 | 
			
		||||
              type: 'createMilestoneProgression';
 | 
			
		||||
              payload: CreateMilestoneProgressionSchema;
 | 
			
		||||
          }
 | 
			
		||||
        | {
 | 
			
		||||
              type: 'updateMilestoneProgression';
 | 
			
		||||
              sourceMilestoneId: string;
 | 
			
		||||
              payload: UpdateMilestoneProgressionSchema;
 | 
			
		||||
          }
 | 
			
		||||
        | null
 | 
			
		||||
    >(null);
 | 
			
		||||
    const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
 | 
			
		||||
@ -171,6 +179,17 @@ export const ReleasePlan = ({
 | 
			
		||||
                });
 | 
			
		||||
                setProgressionFormOpenIndex(null);
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case 'updateMilestoneProgression':
 | 
			
		||||
                await addChange(projectId, environment, {
 | 
			
		||||
                    feature: featureName,
 | 
			
		||||
                    action: 'updateMilestoneProgression',
 | 
			
		||||
                    payload: {
 | 
			
		||||
                        sourceMilestone: changeRequestAction.sourceMilestoneId,
 | 
			
		||||
                        ...changeRequestAction.payload,
 | 
			
		||||
                    },
 | 
			
		||||
                });
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await refetchChangeRequests();
 | 
			
		||||
@ -273,6 +292,17 @@ export const ReleasePlan = ({
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const handleUpdateProgressionChangeRequestSubmit = (
 | 
			
		||||
        sourceMilestoneId: string,
 | 
			
		||||
        payload: UpdateMilestoneProgressionSchema,
 | 
			
		||||
    ) => {
 | 
			
		||||
        setChangeRequestAction({
 | 
			
		||||
            type: 'updateMilestoneProgression',
 | 
			
		||||
            sourceMilestoneId,
 | 
			
		||||
            payload,
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const handleDeleteProgression = (milestone: IReleasePlanMilestone) => {
 | 
			
		||||
        setMilestoneToDeleteProgression(milestone);
 | 
			
		||||
    };
 | 
			
		||||
@ -398,6 +428,9 @@ export const ReleasePlan = ({
 | 
			
		||||
                                environment={environment}
 | 
			
		||||
                                featureName={featureName}
 | 
			
		||||
                                onUpdate={refetch}
 | 
			
		||||
                                onUpdateChangeRequestSubmit={
 | 
			
		||||
                                    handleUpdateProgressionChangeRequestSubmit
 | 
			
		||||
                                }
 | 
			
		||||
                                allMilestones={milestones}
 | 
			
		||||
                                activeMilestoneId={activeMilestoneId}
 | 
			
		||||
                            />
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,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';
 | 
			
		||||
import type { UpdateMilestoneProgressionSchema } from 'openapi';
 | 
			
		||||
 | 
			
		||||
const StyledAutomationContainer = styled('div', {
 | 
			
		||||
    shouldForwardProp: (prop) => prop !== 'status',
 | 
			
		||||
@ -65,6 +66,10 @@ interface IMilestoneAutomationSectionProps {
 | 
			
		||||
    featureName: string;
 | 
			
		||||
    sourceMilestoneId: string;
 | 
			
		||||
    onUpdate: () => void;
 | 
			
		||||
    onUpdateChangeRequestSubmit?: (
 | 
			
		||||
        sourceMilestoneId: string,
 | 
			
		||||
        payload: UpdateMilestoneProgressionSchema,
 | 
			
		||||
    ) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const MilestoneAutomationSection = ({
 | 
			
		||||
@ -80,6 +85,7 @@ export const MilestoneAutomationSection = ({
 | 
			
		||||
    featureName,
 | 
			
		||||
    sourceMilestoneId,
 | 
			
		||||
    onUpdate,
 | 
			
		||||
    onUpdateChangeRequestSubmit,
 | 
			
		||||
}: IMilestoneAutomationSectionProps) => {
 | 
			
		||||
    if (!showAutomation) return null;
 | 
			
		||||
 | 
			
		||||
@ -98,6 +104,7 @@ export const MilestoneAutomationSection = ({
 | 
			
		||||
                    featureName={featureName}
 | 
			
		||||
                    sourceMilestoneId={sourceMilestoneId}
 | 
			
		||||
                    onUpdate={onUpdate}
 | 
			
		||||
                    onChangeRequestSubmit={onUpdateChangeRequestSubmit}
 | 
			
		||||
                />
 | 
			
		||||
            ) : (
 | 
			
		||||
                <StyledAddAutomationButton
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,8 @@ import {
 | 
			
		||||
    useMilestoneProgressionForm,
 | 
			
		||||
    getTimeValueAndUnitFromMinutes,
 | 
			
		||||
} from '../hooks/useMilestoneProgressionForm.js';
 | 
			
		||||
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
 | 
			
		||||
import type { UpdateMilestoneProgressionSchema } from 'openapi';
 | 
			
		||||
 | 
			
		||||
const StyledDisplayContainer = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
@ -67,6 +69,10 @@ interface IMilestoneTransitionDisplayProps {
 | 
			
		||||
    featureName: string;
 | 
			
		||||
    sourceMilestoneId: string;
 | 
			
		||||
    onUpdate: () => void;
 | 
			
		||||
    onChangeRequestSubmit?: (
 | 
			
		||||
        sourceMilestoneId: string,
 | 
			
		||||
        payload: UpdateMilestoneProgressionSchema,
 | 
			
		||||
    ) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const MilestoneTransitionDisplay = ({
 | 
			
		||||
@ -79,9 +85,11 @@ export const MilestoneTransitionDisplay = ({
 | 
			
		||||
    featureName,
 | 
			
		||||
    sourceMilestoneId,
 | 
			
		||||
    onUpdate,
 | 
			
		||||
    onChangeRequestSubmit,
 | 
			
		||||
}: IMilestoneTransitionDisplayProps) => {
 | 
			
		||||
    const { updateMilestoneProgression } = useMilestoneProgressionsApi();
 | 
			
		||||
    const { setToastData, setToastApiError } = useToast();
 | 
			
		||||
    const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
 | 
			
		||||
 | 
			
		||||
    const initial = getTimeValueAndUnitFromMinutes(intervalMinutes);
 | 
			
		||||
    const form = useMilestoneProgressionForm(
 | 
			
		||||
@ -100,6 +108,17 @@ export const MilestoneTransitionDisplay = ({
 | 
			
		||||
    const handleSave = async () => {
 | 
			
		||||
        if (isSubmitting || !hasChanged) return;
 | 
			
		||||
 | 
			
		||||
        const payload: UpdateMilestoneProgressionSchema = {
 | 
			
		||||
            transitionCondition: {
 | 
			
		||||
                intervalMinutes: currentIntervalMinutes,
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (isChangeRequestConfigured(environment) && onChangeRequestSubmit) {
 | 
			
		||||
            onChangeRequestSubmit(sourceMilestoneId, payload);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setIsSubmitting(true);
 | 
			
		||||
        try {
 | 
			
		||||
            await updateMilestoneProgression(
 | 
			
		||||
@ -107,11 +126,7 @@ export const MilestoneTransitionDisplay = ({
 | 
			
		||||
                environment,
 | 
			
		||||
                featureName,
 | 
			
		||||
                sourceMilestoneId,
 | 
			
		||||
                {
 | 
			
		||||
                    transitionCondition: {
 | 
			
		||||
                        intervalMinutes: currentIntervalMinutes,
 | 
			
		||||
                    },
 | 
			
		||||
                },
 | 
			
		||||
                payload,
 | 
			
		||||
            );
 | 
			
		||||
            setToastData({
 | 
			
		||||
                type: 'success',
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ import { StrategyList } from 'component/common/StrategyList/StrategyList';
 | 
			
		||||
import { StrategyListItem } from 'component/common/StrategyList/StrategyListItem';
 | 
			
		||||
import { MilestoneAutomationSection } from './MilestoneAutomationSection.tsx';
 | 
			
		||||
import { formatDateYMDHMS } from 'utils/formatDate';
 | 
			
		||||
import type { UpdateMilestoneProgressionSchema } from 'openapi';
 | 
			
		||||
 | 
			
		||||
const StyledAccordion = styled(Accordion, {
 | 
			
		||||
    shouldForwardProp: (prop) => prop !== 'status' && prop !== 'hasAutomation',
 | 
			
		||||
@ -107,6 +108,10 @@ interface IReleasePlanMilestoneProps {
 | 
			
		||||
    environment?: string;
 | 
			
		||||
    featureName?: string;
 | 
			
		||||
    onUpdate?: () => void;
 | 
			
		||||
    onUpdateChangeRequestSubmit?: (
 | 
			
		||||
        sourceMilestoneId: string,
 | 
			
		||||
        payload: UpdateMilestoneProgressionSchema,
 | 
			
		||||
    ) => void;
 | 
			
		||||
    allMilestones: IReleasePlanMilestone[];
 | 
			
		||||
    activeMilestoneId?: string;
 | 
			
		||||
}
 | 
			
		||||
@ -124,6 +129,7 @@ export const ReleasePlanMilestone = ({
 | 
			
		||||
    environment,
 | 
			
		||||
    featureName,
 | 
			
		||||
    onUpdate,
 | 
			
		||||
    onUpdateChangeRequestSubmit,
 | 
			
		||||
    allMilestones,
 | 
			
		||||
    activeMilestoneId,
 | 
			
		||||
}: IReleasePlanMilestoneProps) => {
 | 
			
		||||
@ -193,6 +199,9 @@ export const ReleasePlanMilestone = ({
 | 
			
		||||
                            featureName={featureName}
 | 
			
		||||
                            sourceMilestoneId={milestone.id}
 | 
			
		||||
                            onUpdate={onUpdate}
 | 
			
		||||
                            onUpdateChangeRequestSubmit={
 | 
			
		||||
                                onUpdateChangeRequestSubmit
 | 
			
		||||
                            }
 | 
			
		||||
                        />
 | 
			
		||||
                    )}
 | 
			
		||||
            </StyledMilestoneContainer>
 | 
			
		||||
@ -283,6 +292,9 @@ export const ReleasePlanMilestone = ({
 | 
			
		||||
                        featureName={featureName}
 | 
			
		||||
                        sourceMilestoneId={milestone.id}
 | 
			
		||||
                        onUpdate={onUpdate}
 | 
			
		||||
                        onUpdateChangeRequestSubmit={
 | 
			
		||||
                            onUpdateChangeRequestSubmit
 | 
			
		||||
                        }
 | 
			
		||||
                    />
 | 
			
		||||
                )}
 | 
			
		||||
        </StyledMilestoneContainer>
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,8 @@ export interface IChangeSchema {
 | 
			
		||||
        | 'addReleasePlan'
 | 
			
		||||
        | 'deleteReleasePlan'
 | 
			
		||||
        | 'startMilestone'
 | 
			
		||||
        | 'createMilestoneProgression';
 | 
			
		||||
        | 'createMilestoneProgression'
 | 
			
		||||
        | 'updateMilestoneProgression';
 | 
			
		||||
    payload: string | boolean | object | number | undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user