1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-24 01:18:01 +02:00

chore: move strategy UI into milestonecard and open milestone when adding strategy to it (#9095)

This commit is contained in:
David Leek 2025-01-14 13:02:53 +01:00 committed by GitHub
parent 74c1bd5ad8
commit 900df537e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 240 additions and 256 deletions

View File

@ -20,6 +20,8 @@ import ExpandMore from '@mui/icons-material/ExpandMore';
import { MilestoneCardName } from './MilestoneCardName'; import { MilestoneCardName } from './MilestoneCardName';
import { MilestoneStrategyMenuCards } from './MilestoneStrategyMenu/MilestoneStrategyMenuCards'; import { MilestoneStrategyMenuCards } from './MilestoneStrategyMenu/MilestoneStrategyMenuCards';
import { MilestoneStrategyDraggableItem } from './MilestoneStrategyDraggableItem'; import { MilestoneStrategyDraggableItem } from './MilestoneStrategyDraggableItem';
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
import { ReleasePlanTemplateAddStrategyForm } from '../../MilestoneStrategy/ReleasePlanTemplateAddStrategyForm';
const StyledMilestoneCard = styled(Card, { const StyledMilestoneCard = styled(Card, {
shouldForwardProp: (prop) => prop !== 'hasError', shouldForwardProp: (prop) => prop !== 'hasError',
@ -111,11 +113,6 @@ const StyledIconButton = styled(IconButton)(({ theme }) => ({
interface IMilestoneCardProps { interface IMilestoneCardProps {
milestone: IReleasePlanMilestonePayload; milestone: IReleasePlanMilestonePayload;
milestoneChanged: (milestone: IReleasePlanMilestonePayload) => void; milestoneChanged: (milestone: IReleasePlanMilestonePayload) => void;
showAddStrategyDialog: (
milestoneId: string,
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>,
editing: boolean,
) => void;
errors: { [key: string]: string }; errors: { [key: string]: string };
clearErrors: () => void; clearErrors: () => void;
removable: boolean; removable: boolean;
@ -125,7 +122,6 @@ interface IMilestoneCardProps {
export const MilestoneCard = ({ export const MilestoneCard = ({
milestone, milestone,
milestoneChanged, milestoneChanged,
showAddStrategyDialog,
errors, errors,
clearErrors, clearErrors,
removable, removable,
@ -137,6 +133,9 @@ export const MilestoneCard = ({
index: number; index: number;
height: number; height: number;
} | null>(null); } | null>(null);
const [addUpdateStrategyOpen, setAddUpdateStrategyOpen] = useState(false);
const [strategyModeEdit, setStrategyModeEdit] = useState(false);
const [expanded, setExpanded] = useState(false);
const isPopoverOpen = Boolean(anchor); const isPopoverOpen = Boolean(anchor);
const popoverId = isPopoverOpen const popoverId = isPopoverOpen
? 'MilestoneStrategyMenuPopover' ? 'MilestoneStrategyMenuPopover'
@ -146,16 +145,76 @@ export const MilestoneCard = ({
setAnchor(undefined); setAnchor(undefined);
}; };
const onSelectEditStrategy = ( const [currentStrategy, setCurrentStrategy] = useState<
Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>
>({
name: 'flexibleRollout',
parameters: { rollout: '50' },
constraints: [],
title: '',
id: 'temp',
});
const milestoneStrategyChanged = (
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>, strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>,
) => { ) => {
showAddStrategyDialog(milestone.id, strategy, true); const strategies = milestone.strategies || [];
milestoneChanged({
...milestone,
strategies: [
...strategies.map((strat) =>
strat.id === strategy.id ? strategy : strat,
),
],
});
}; };
const onSelectStrategy = ( const milestoneStrategyAdded = (
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>, strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>,
) => { ) => {
showAddStrategyDialog(milestone.id, strategy, false); milestoneChanged({
...milestone,
strategies: [
...(milestone.strategies || []),
{
...strategy,
strategyName: strategy.strategyName,
sortOrder: milestone.strategies?.length || 0,
},
],
});
};
const addUpdateStrategy = (
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>,
) => {
const existingStrategy = milestone.strategies?.find(
(strat) => strat.id === strategy.id,
);
if (existingStrategy) {
milestoneStrategyChanged(strategy);
} else {
milestoneStrategyAdded(strategy);
setExpanded(true);
}
setAddUpdateStrategyOpen(false);
setStrategyModeEdit(false);
setCurrentStrategy({
name: 'flexibleRollout',
parameters: { rollout: '50' },
constraints: [],
title: '',
id: 'temp',
});
};
const openAddUpdateStrategyForm = (
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>,
editing: boolean,
) => {
setStrategyModeEdit(editing);
setCurrentStrategy(strategy);
setAddUpdateStrategyOpen(true);
}; };
const onDragOver = const onDragOver =
@ -248,6 +307,7 @@ export const MilestoneCard = ({
if (!milestone.strategies || milestone.strategies.length === 0) { if (!milestone.strategies || milestone.strategies.length === 0) {
return ( return (
<>
<StyledMilestoneCard <StyledMilestoneCard
hasError={ hasError={
Boolean(errors?.[milestone.id]) || Boolean(errors?.[milestone.id]) ||
@ -268,7 +328,9 @@ export const MilestoneCard = ({
<Button <Button
variant='outlined' variant='outlined'
color='primary' color='primary'
onClick={(ev) => setAnchor(ev.currentTarget)} onClick={(ev) =>
setAnchor(ev.currentTarget)
}
> >
Add strategy Add strategy
</Button> </Button>
@ -293,18 +355,47 @@ export const MilestoneCard = ({
}} }}
> >
<MilestoneStrategyMenuCards <MilestoneStrategyMenuCards
openEditAddStrategy={onSelectStrategy} openEditAddStrategy={(strategy) => {
openAddUpdateStrategyForm(
strategy,
false,
);
}}
/> />
</Popover> </Popover>
</StyledMilestoneActionGrid> </StyledMilestoneActionGrid>
</Grid> </Grid>
</StyledMilestoneCardBody> </StyledMilestoneCardBody>
</StyledMilestoneCard> </StyledMilestoneCard>
<SidebarModal
label='Add strategy to template milestone'
onClose={() => {
setAddUpdateStrategyOpen(false);
setStrategyModeEdit(false);
}}
open={addUpdateStrategyOpen}
>
<ReleasePlanTemplateAddStrategyForm
strategy={currentStrategy}
onAddUpdateStrategy={addUpdateStrategy}
onCancel={() => {
setAddUpdateStrategyOpen(false);
setStrategyModeEdit(false);
}}
editMode={strategyModeEdit}
/>
</SidebarModal>
</>
); );
} }
return ( return (
<StyledAccordion> <>
<StyledAccordion
expanded={expanded}
onChange={(e, change) => setExpanded(change)}
>
<StyledAccordionSummary <StyledAccordionSummary
expandIcon={<ExpandMore titleAccess='Toggle' />} expandIcon={<ExpandMore titleAccess='Toggle' />}
> >
@ -327,7 +418,7 @@ export const MilestoneCard = ({
milestoneStrategyDeleted(strg.id) milestoneStrategyDeleted(strg.id)
} }
onEditClick={() => { onEditClick={() => {
onSelectEditStrategy(strg); openAddUpdateStrategyForm(strg, true);
}} }}
isDragging={dragItem?.id === strg.id} isDragging={dragItem?.id === strg.id}
strategy={strg} strategy={strg}
@ -363,11 +454,33 @@ export const MilestoneCard = ({
}} }}
> >
<MilestoneStrategyMenuCards <MilestoneStrategyMenuCards
openEditAddStrategy={onSelectStrategy} openEditAddStrategy={(strategy) => {
openAddUpdateStrategyForm(strategy, false);
}}
/> />
</Popover> </Popover>
</StyledAccordionFooter> </StyledAccordionFooter>
</StyledAccordionDetails> </StyledAccordionDetails>
</StyledAccordion> </StyledAccordion>
<SidebarModal
label='Add strategy to template milestone'
onClose={() => {
setAddUpdateStrategyOpen(false);
setStrategyModeEdit(false);
}}
open={addUpdateStrategyOpen}
>
<ReleasePlanTemplateAddStrategyForm
strategy={currentStrategy}
onAddUpdateStrategy={addUpdateStrategy}
onCancel={() => {
setAddUpdateStrategyOpen(false);
setStrategyModeEdit(false);
}}
editMode={strategyModeEdit}
/>
</SidebarModal>
</>
); );
}; };

View File

@ -1,7 +1,4 @@
import type { import type { IReleasePlanMilestonePayload } from 'interfaces/releasePlans';
IReleasePlanMilestonePayload,
IReleasePlanMilestoneStrategy,
} from 'interfaces/releasePlans';
import { MilestoneCard } from './MilestoneCard/MilestoneCard'; import { MilestoneCard } from './MilestoneCard/MilestoneCard';
import { styled, Button, FormHelperText } from '@mui/material'; import { styled, Button, FormHelperText } from '@mui/material';
import Add from '@mui/icons-material/Add'; import Add from '@mui/icons-material/Add';
@ -12,11 +9,6 @@ interface IMilestoneListProps {
setMilestones: React.Dispatch< setMilestones: React.Dispatch<
React.SetStateAction<IReleasePlanMilestonePayload[]> React.SetStateAction<IReleasePlanMilestonePayload[]>
>; >;
openAddStrategyForm: (
milestoneId: string,
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>,
editing: boolean,
) => void;
errors: { [key: string]: string }; errors: { [key: string]: string };
clearErrors: () => void; clearErrors: () => void;
milestoneChanged: (milestone: IReleasePlanMilestonePayload) => void; milestoneChanged: (milestone: IReleasePlanMilestonePayload) => void;
@ -30,7 +22,6 @@ const StyledAddMilestoneButton = styled(Button)(({ theme }) => ({
export const MilestoneList = ({ export const MilestoneList = ({
milestones, milestones,
setMilestones, setMilestones,
openAddStrategyForm,
errors, errors,
clearErrors, clearErrors,
milestoneChanged, milestoneChanged,
@ -51,7 +42,6 @@ export const MilestoneList = ({
key={milestone.id} key={milestone.id}
milestone={milestone} milestone={milestone}
milestoneChanged={milestoneChanged} milestoneChanged={milestoneChanged}
showAddStrategyDialog={openAddStrategyForm}
errors={errors} errors={errors}
clearErrors={clearErrors} clearErrors={clearErrors}
removable={milestones.length > 1} removable={milestones.length > 1}

View File

@ -124,18 +124,15 @@ const StyledDividerContent = styled(Box)(({ theme }) => ({
})); }));
interface IReleasePlanTemplateAddStrategyFormProps { interface IReleasePlanTemplateAddStrategyFormProps {
milestoneId: string | undefined;
onCancel: () => void; onCancel: () => void;
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>; strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>;
onAddUpdateStrategy: ( onAddUpdateStrategy: (
milestoneId: string,
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>, strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>,
) => void; ) => void;
editMode: boolean; editMode: boolean;
} }
export const ReleasePlanTemplateAddStrategyForm = ({ export const ReleasePlanTemplateAddStrategyForm = ({
milestoneId,
onCancel, onCancel,
strategy, strategy,
onAddUpdateStrategy, onAddUpdateStrategy,
@ -231,11 +228,7 @@ export const ReleasePlanTemplateAddStrategyForm = ({
}; };
const AddUpdateMilestoneStrategy = () => { const AddUpdateMilestoneStrategy = () => {
if (!milestoneId) { onAddUpdateStrategy(currentStrategy);
return;
}
onAddUpdateStrategy(milestoneId, currentStrategy);
}; };
return ( return (

View File

@ -1,15 +1,9 @@
import Input from 'component/common/Input/Input'; import Input from 'component/common/Input/Input';
import { styled, useTheme } from '@mui/material'; import { styled, useTheme } from '@mui/material';
import type { import type { IReleasePlanMilestonePayload } from 'interfaces/releasePlans';
IReleasePlanMilestonePayload,
IReleasePlanMilestoneStrategy,
} from 'interfaces/releasePlans';
import FormTemplate from 'component/common/FormTemplate/FormTemplate'; import FormTemplate from 'component/common/FormTemplate/FormTemplate';
import { useState } from 'react';
import { TemplateFormDescription } from './TemplateFormDescription'; import { TemplateFormDescription } from './TemplateFormDescription';
import { MilestoneList } from './MilestoneList/MilestoneList'; import { MilestoneList } from './MilestoneList/MilestoneList';
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
import { ReleasePlanTemplateAddStrategyForm } from './MilestoneStrategy/ReleasePlanTemplateAddStrategyForm';
const StyledInput = styled(Input)(({ theme }) => ({ const StyledInput = styled(Input)(({ theme }) => ({
width: '100%', width: '100%',
@ -61,77 +55,7 @@ export const TemplateForm: React.FC<ITemplateFormProps> = ({
handleSubmit, handleSubmit,
children, children,
}) => { }) => {
const [addUpdateStrategyOpen, setAddUpdateStrategyOpen] = useState(false);
const [activeMilestoneId, setActiveMilestoneId] = useState<
string | undefined
>();
const [strategyModeEdit, setStrategyModeEdit] = useState(false);
const [strategy, setStrategy] = useState<
Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>
>({
name: 'flexibleRollout',
parameters: { rollout: '50' },
constraints: [],
title: '',
id: 'temp',
});
const theme = useTheme(); const theme = useTheme();
const openAddUpdateStrategyForm = (
milestoneId: string,
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>,
editing: boolean,
) => {
setStrategyModeEdit(editing);
setActiveMilestoneId(milestoneId);
setStrategy(strategy);
setAddUpdateStrategyOpen(true);
};
const addUpdateStrategy = (
milestoneId: string,
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>,
) => {
const milestone = milestones.find((m) => m.id === milestoneId);
const existingStrategy = milestone?.strategies?.find(
(strat) => strat.id === strategy.id,
);
if (!milestone) {
return;
}
if (existingStrategy) {
milestoneStrategyChanged(milestone, strategy);
} else {
setMilestones((prev) =>
prev.map((milestone, i) =>
milestone.id === milestoneId
? {
...milestone,
strategies: [
...(milestone.strategies || []),
{
...strategy,
strategyName: strategy.strategyName,
sortOrder:
milestone.strategies?.length || 0,
},
],
}
: milestone,
),
);
}
setAddUpdateStrategyOpen(false);
setStrategyModeEdit(false);
setActiveMilestoneId(undefined);
setStrategy({
name: 'flexibleRollout',
parameters: { rollout: '50' },
constraints: [],
title: '',
id: 'temp',
});
};
const milestoneChanged = (milestone: IReleasePlanMilestonePayload) => { const milestoneChanged = (milestone: IReleasePlanMilestonePayload) => {
setMilestones((prev) => setMilestones((prev) =>
@ -141,21 +65,6 @@ export const TemplateForm: React.FC<ITemplateFormProps> = ({
); );
}; };
const milestoneStrategyChanged = (
milestone: IReleasePlanMilestonePayload,
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>,
) => {
const strategies = milestone.strategies || [];
milestoneChanged({
...milestone,
strategies: [
...strategies.map((strat) =>
strat.id === strategy.id ? strategy : strat,
),
],
});
};
return ( return (
<FormTemplate <FormTemplate
title={formTitle} title={formTitle}
@ -203,33 +112,12 @@ export const TemplateForm: React.FC<ITemplateFormProps> = ({
<MilestoneList <MilestoneList
milestones={milestones} milestones={milestones}
setMilestones={setMilestones} setMilestones={setMilestones}
openAddStrategyForm={openAddUpdateStrategyForm}
errors={errors} errors={errors}
clearErrors={clearErrors} clearErrors={clearErrors}
milestoneChanged={milestoneChanged} milestoneChanged={milestoneChanged}
/> />
{children} {children}
<SidebarModal
label='Add strategy to template milestone'
onClose={() => {
setAddUpdateStrategyOpen(false);
setStrategyModeEdit(false);
}}
open={addUpdateStrategyOpen}
>
<ReleasePlanTemplateAddStrategyForm
milestoneId={activeMilestoneId}
strategy={strategy}
onAddUpdateStrategy={addUpdateStrategy}
onCancel={() => {
setAddUpdateStrategyOpen(false);
setStrategyModeEdit(false);
}}
editMode={strategyModeEdit}
/>
</SidebarModal>
</StyledForm> </StyledForm>
</FormTemplate> </FormTemplate>
); );