From 2078dcc46aa2bb7b6a3d6f5ae91ca3d27aeda7aa Mon Sep 17 00:00:00 2001 From: David Leek Date: Tue, 26 Nov 2024 08:42:56 +0100 Subject: [PATCH] chore: milestone strategy tabs (#8851) --- .../MilestoneStrategyTitle.tsx | 35 +++++ .../ReleasePlanTemplateAddStrategyForm.tsx | 140 +++++++++++++++++- .../ReleasePlanTemplate/TemplateForm.tsx | 20 ++- 3 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 frontend/src/component/releases/ReleasePlanTemplate/MilestoneStrategyTitle.tsx diff --git a/frontend/src/component/releases/ReleasePlanTemplate/MilestoneStrategyTitle.tsx b/frontend/src/component/releases/ReleasePlanTemplate/MilestoneStrategyTitle.tsx new file mode 100644 index 0000000000..654ba36267 --- /dev/null +++ b/frontend/src/component/releases/ReleasePlanTemplate/MilestoneStrategyTitle.tsx @@ -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 ( + + + What would you like to call this strategy? (optional) + + setTitle(e.target.value)} + sx={{ width: '100%' }} + /> + + ); +}; diff --git a/frontend/src/component/releases/ReleasePlanTemplate/ReleasePlanTemplateAddStrategyForm.tsx b/frontend/src/component/releases/ReleasePlanTemplate/ReleasePlanTemplateAddStrategyForm.tsx index 7395c0dcbe..07756bd119 100644 --- a/frontend/src/component/releases/ReleasePlanTemplate/ReleasePlanTemplateAddStrategyForm.tsx +++ b/frontend/src/component/releases/ReleasePlanTemplate/ReleasePlanTemplateAddStrategyForm.tsx @@ -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 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 }) => ({ marginLeft: theme.spacing(3), @@ -11,19 +17,151 @@ const StyledButtonContainer = styled('div')(() => ({ 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 { + milestoneId: string | undefined; onCancel: () => void; + strategy: IReleasePlanMilestoneStrategy; + onAddStrategy: ( + milestoneId: string, + strategy: IReleasePlanMilestoneStrategy, + ) => void; } export const ReleasePlanTemplateAddStrategyForm = ({ + milestoneId, onCancel, + strategy, + onAddStrategy, }: 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 ( + + + {formatStrategyName(addStrategy.name || '')} + {addStrategy.name === 'flexibleRollout' && ( + + {addStrategy.parameters?.rollout}% + + )} + + + + + Targeting} /> + + + {activeTab === 0 && ( + <> + + updateParameter('title', title) + } + /> + + )} + {activeTab === 1 && ( + <> + + 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. + + + )} + + Cancel diff --git a/frontend/src/component/releases/ReleasePlanTemplate/TemplateForm.tsx b/frontend/src/component/releases/ReleasePlanTemplate/TemplateForm.tsx index 9098843190..ab9efe7228 100644 --- a/frontend/src/component/releases/ReleasePlanTemplate/TemplateForm.tsx +++ b/frontend/src/component/releases/ReleasePlanTemplate/TemplateForm.tsx @@ -67,7 +67,7 @@ export const TemplateForm: React.FC = ({ parameters: { rollout: '50' }, constraints: [], title: '', - id: '', + id: 'temp', }); const openAddStrategyForm = ( milestoneId: string, @@ -89,12 +89,25 @@ export const TemplateForm: React.FC = ({ ...milestone, strategies: [ ...(milestone.strategies || []), - strategy, + { + ...strategy, + strategyName: strategy.name, + sortOrder: milestone.strategies?.length || 0, + }, ], } : milestone, ), ); + setAddStrategyOpen(false); + setActiveMilestoneId(undefined); + setStrategy({ + name: 'flexibleRollout', + parameters: { rollout: '50' }, + constraints: [], + title: '', + id: 'temp', + }); }; return ( @@ -143,6 +156,9 @@ export const TemplateForm: React.FC = ({ open={addStrategyOpen} > { setAddStrategyOpen(false); }}