mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-18 00:19:49 +01:00
chore: milestone strategy tabs (#8851)
This commit is contained in:
parent
1433878f32
commit
2078dcc46a
@ -0,0 +1,35 @@
|
|||||||
|
import { Box, Typography, styled } from '@mui/material';
|
||||||
|
import Input from 'component/common/Input/Input';
|
||||||
|
|
||||||
|
const StyledBox = styled(Box)(({ theme }) => ({
|
||||||
|
paddingBottom: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTypography = styled(Typography)(({ theme }) => ({
|
||||||
|
paddingBottom: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface IMilestoneStrategyTitleProps {
|
||||||
|
title: string;
|
||||||
|
setTitle: (title: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MilestoneStrategyTitle = ({
|
||||||
|
title,
|
||||||
|
setTitle,
|
||||||
|
}: IMilestoneStrategyTitleProps) => {
|
||||||
|
return (
|
||||||
|
<StyledBox>
|
||||||
|
<StyledTypography>
|
||||||
|
What would you like to call this strategy? (optional)
|
||||||
|
</StyledTypography>
|
||||||
|
<Input
|
||||||
|
label='Strategy title'
|
||||||
|
id='title-input'
|
||||||
|
value={title}
|
||||||
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
|
sx={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</StyledBox>
|
||||||
|
);
|
||||||
|
};
|
@ -1,5 +1,11 @@
|
|||||||
import { Button, styled } from '@mui/material';
|
import { Box, Button, styled, Tab, Tabs, Typography } from '@mui/material';
|
||||||
|
import { Badge } from 'component/common/Badge/Badge';
|
||||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||||
|
import type { IReleasePlanMilestoneStrategy } from 'interfaces/releasePlans';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { formatStrategyName } from 'utils/strategyNames';
|
||||||
|
import { MilestoneStrategyTitle } from './MilestoneStrategyTitle';
|
||||||
|
import produce from 'immer';
|
||||||
|
|
||||||
const StyledCancelButton = styled(Button)(({ theme }) => ({
|
const StyledCancelButton = styled(Button)(({ theme }) => ({
|
||||||
marginLeft: theme.spacing(3),
|
marginLeft: theme.spacing(3),
|
||||||
@ -11,19 +17,151 @@ const StyledButtonContainer = styled('div')(() => ({
|
|||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const StyledHeaderBox = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
paddingLeft: theme.spacing(6),
|
||||||
|
paddingRight: theme.spacing(6),
|
||||||
|
paddingTop: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTitle = styled('h1')(({ theme }) => ({
|
||||||
|
fontWeight: 'normal',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingTop: theme.spacing(2),
|
||||||
|
paddingBottom: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTabs = styled(Tabs)(({ theme }) => ({
|
||||||
|
borderTop: `1px solid ${theme.palette.divider}`,
|
||||||
|
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||||
|
paddingLeft: theme.spacing(6),
|
||||||
|
paddingRight: theme.spacing(6),
|
||||||
|
minHeight: '60px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTab = styled(Tab)(({ theme }) => ({
|
||||||
|
width: '100px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledContentDiv = styled('div')(({ theme }) => ({
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
padding: theme.spacing(6),
|
||||||
|
paddingBottom: theme.spacing(16),
|
||||||
|
paddingTop: theme.spacing(4),
|
||||||
|
overflow: 'auto',
|
||||||
|
height: '100%',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTargetingHeader = styled('div')(({ theme }) => ({
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
marginTop: theme.spacing(1.5),
|
||||||
|
}));
|
||||||
|
|
||||||
interface IReleasePlanTemplateAddStrategyFormProps {
|
interface IReleasePlanTemplateAddStrategyFormProps {
|
||||||
|
milestoneId: string | undefined;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
|
strategy: IReleasePlanMilestoneStrategy;
|
||||||
|
onAddStrategy: (
|
||||||
|
milestoneId: string,
|
||||||
|
strategy: IReleasePlanMilestoneStrategy,
|
||||||
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ReleasePlanTemplateAddStrategyForm = ({
|
export const ReleasePlanTemplateAddStrategyForm = ({
|
||||||
|
milestoneId,
|
||||||
onCancel,
|
onCancel,
|
||||||
|
strategy,
|
||||||
|
onAddStrategy,
|
||||||
}: IReleasePlanTemplateAddStrategyFormProps) => {
|
}: IReleasePlanTemplateAddStrategyFormProps) => {
|
||||||
|
const [addStrategy, setAddStrategy] = useState(strategy);
|
||||||
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
|
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
|
||||||
|
setActiveTab(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateParameter = (name: string, value: string) => {
|
||||||
|
setAddStrategy(
|
||||||
|
produce((draft) => {
|
||||||
|
if (!draft) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (name === 'title') {
|
||||||
|
draft.title = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
draft.parameters = draft.parameters ?? {};
|
||||||
|
draft.parameters[name] = value;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addStrategyToMilestone = () => {
|
||||||
|
if (!milestoneId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onAddStrategy(milestoneId, addStrategy);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!strategy) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormTemplate
|
<FormTemplate
|
||||||
modal
|
modal
|
||||||
description='Add a strategy to your release plan template.'
|
description='Add a strategy to your release plan template.'
|
||||||
>
|
>
|
||||||
|
<StyledHeaderBox>
|
||||||
|
<StyledTitle>
|
||||||
|
{formatStrategyName(addStrategy.name || '')}
|
||||||
|
{addStrategy.name === 'flexibleRollout' && (
|
||||||
|
<Badge color='success' sx={{ marginLeft: '1rem' }}>
|
||||||
|
{addStrategy.parameters?.rollout}%
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</StyledTitle>
|
||||||
|
</StyledHeaderBox>
|
||||||
|
<StyledTabs value={activeTab} onChange={handleChange}>
|
||||||
|
<StyledTab label='General' />
|
||||||
|
<Tab label={<Typography>Targeting</Typography>} />
|
||||||
|
</StyledTabs>
|
||||||
|
<StyledContentDiv>
|
||||||
|
{activeTab === 0 && (
|
||||||
|
<>
|
||||||
|
<MilestoneStrategyTitle
|
||||||
|
title={addStrategy.title}
|
||||||
|
setTitle={(title) =>
|
||||||
|
updateParameter('title', title)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{activeTab === 1 && (
|
||||||
|
<>
|
||||||
|
<StyledTargetingHeader>
|
||||||
|
Segmentation and constraints allow you to set
|
||||||
|
filters on your strategies, so that they will only
|
||||||
|
be evaluated for users and applications that match
|
||||||
|
the specified preconditions.
|
||||||
|
</StyledTargetingHeader>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</StyledContentDiv>
|
||||||
<StyledButtonContainer>
|
<StyledButtonContainer>
|
||||||
|
<Button
|
||||||
|
variant='contained'
|
||||||
|
color='primary'
|
||||||
|
type='submit'
|
||||||
|
onClick={addStrategyToMilestone}
|
||||||
|
>
|
||||||
|
Save strategy
|
||||||
|
</Button>
|
||||||
<StyledCancelButton onClick={onCancel}>
|
<StyledCancelButton onClick={onCancel}>
|
||||||
Cancel
|
Cancel
|
||||||
</StyledCancelButton>
|
</StyledCancelButton>
|
||||||
|
@ -67,7 +67,7 @@ export const TemplateForm: React.FC<ITemplateFormProps> = ({
|
|||||||
parameters: { rollout: '50' },
|
parameters: { rollout: '50' },
|
||||||
constraints: [],
|
constraints: [],
|
||||||
title: '',
|
title: '',
|
||||||
id: '',
|
id: 'temp',
|
||||||
});
|
});
|
||||||
const openAddStrategyForm = (
|
const openAddStrategyForm = (
|
||||||
milestoneId: string,
|
milestoneId: string,
|
||||||
@ -89,12 +89,25 @@ export const TemplateForm: React.FC<ITemplateFormProps> = ({
|
|||||||
...milestone,
|
...milestone,
|
||||||
strategies: [
|
strategies: [
|
||||||
...(milestone.strategies || []),
|
...(milestone.strategies || []),
|
||||||
strategy,
|
{
|
||||||
|
...strategy,
|
||||||
|
strategyName: strategy.name,
|
||||||
|
sortOrder: milestone.strategies?.length || 0,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
: milestone,
|
: milestone,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
setAddStrategyOpen(false);
|
||||||
|
setActiveMilestoneId(undefined);
|
||||||
|
setStrategy({
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
parameters: { rollout: '50' },
|
||||||
|
constraints: [],
|
||||||
|
title: '',
|
||||||
|
id: 'temp',
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -143,6 +156,9 @@ export const TemplateForm: React.FC<ITemplateFormProps> = ({
|
|||||||
open={addStrategyOpen}
|
open={addStrategyOpen}
|
||||||
>
|
>
|
||||||
<ReleasePlanTemplateAddStrategyForm
|
<ReleasePlanTemplateAddStrategyForm
|
||||||
|
milestoneId={activeMilestoneId}
|
||||||
|
strategy={strategy}
|
||||||
|
onAddStrategy={addStrategy}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setAddStrategyOpen(false);
|
setAddStrategyOpen(false);
|
||||||
}}
|
}}
|
||||||
|
Loading…
Reference in New Issue
Block a user