diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/MilestoneProgressionForm/MilestoneProgressionForm.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/MilestoneProgressionForm/MilestoneProgressionForm.tsx index b2621a75cc..ec9db97ae1 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/MilestoneProgressionForm/MilestoneProgressionForm.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/MilestoneProgressionForm/MilestoneProgressionForm.tsx @@ -20,41 +20,26 @@ const StyledFormContainer = styled('div')(({ theme }) => ({ display: 'flex', flexDirection: 'column', gap: theme.spacing(1.5), - padding: theme.spacing(2), - backgroundColor: theme.palette.background.paper, - borderRadius: theme.spacing(0.75), - border: `1px solid ${theme.palette.divider}`, - boxShadow: theme.boxShadows.elevated, + padding: theme.spacing(1.5, 2), + backgroundColor: theme.palette.background.elevation1, + width: '100%', + borderRadius: `${theme.shape.borderRadiusLarge}px`, position: 'relative', - marginLeft: theme.spacing(3.25), - marginTop: theme.spacing(1.5), - marginBottom: theme.spacing(1.5), - animation: 'slideDown 0.5s ease-out', - '@keyframes slideDown': { - from: { - opacity: 0, - transform: 'translateY(-24px)', - }, - to: { - opacity: 1, - transform: 'translateY(0)', - }, - }, })); const StyledTopRow = styled('div')(({ theme }) => ({ display: 'flex', alignItems: 'center', - gap: theme.spacing(1.5), + gap: theme.spacing(1), })); const StyledIcon = styled(BoltIcon)(({ theme }) => ({ - color: theme.palette.primary.main, - fontSize: 20, + color: theme.palette.common.white, + fontSize: 18, flexShrink: 0, - backgroundColor: theme.palette.background.elevation1, + backgroundColor: theme.palette.primary.main, borderRadius: '50%', - border: `1px solid ${theme.palette.divider}`, + padding: theme.spacing(0.25), })); const StyledLabel = styled('span')(({ theme }) => ({ @@ -105,8 +90,8 @@ const StyledButtonGroup = styled('div')(({ theme }) => ({ gap: theme.spacing(1), justifyContent: 'flex-end', alignItems: 'center', - paddingTop: theme.spacing(1.5), - marginTop: theme.spacing(1), + paddingTop: theme.spacing(1), + marginTop: theme.spacing(0.5), borderTop: `1px solid ${theme.palette.divider}`, })); diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx index 18436282dc..3e0317cd70 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx @@ -1,6 +1,5 @@ import Delete from '@mui/icons-material/Delete'; -import Add from '@mui/icons-material/Add'; -import { styled, IconButton, Button } from '@mui/material'; +import { styled } from '@mui/material'; import { DELETE_FEATURE_STRATEGY } from '@server/types/permissions'; import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; import { useReleasePlansApi } from 'hooks/api/actions/useReleasePlansApi/useReleasePlansApi'; @@ -72,47 +71,12 @@ const StyledBody = styled('div')(({ theme }) => ({ })); const StyledConnection = styled('div')(({ theme }) => ({ - width: 4, - height: theme.spacing(6), - backgroundColor: theme.palette.divider, - marginLeft: theme.spacing(3.25), -})); - -const StyledConnectionSimple = styled('div')(({ theme }) => ({ width: 4, height: theme.spacing(2), backgroundColor: theme.palette.divider, marginLeft: theme.spacing(3.25), })); -const StyledConnectionContainer = styled('div')(({ theme }) => ({ - position: 'relative', - display: 'flex', - alignItems: 'center', -})); - -const StyledAddAutomationIconButton = styled(IconButton)(({ theme }) => ({ - position: 'absolute', - left: theme.spacing(2), - top: '12px', - width: 24, - height: 24, - border: `1px solid ${theme.palette.primary.main}`, - backgroundColor: theme.palette.background.elevation2, - zIndex: 1, - '& svg': { - fontSize: 16, - }, -})); - -const StyledAddAutomationButton = styled(Button)(({ theme }) => ({ - marginLeft: theme.spacing(3), - textTransform: 'none', - fontWeight: theme.typography.fontWeightBold, - padding: 0, - minWidth: 'auto', -})); - interface IReleasePlanProps { plan: IReleasePlan; environmentIsDisabled?: boolean; @@ -332,63 +296,27 @@ export const ReleasePlan = ({ : 'not-started' } onStartMilestone={onStartMilestone} + showAutomation={ + milestoneProgressionsEnabled && + isNotLastMilestone + } + onAddAutomation={handleOpenProgressionForm} + automationForm={ + isProgressionFormOpen ? ( + + ) : undefined + } /> - } - elseShow={ - - - - - - - Add automation - - - } - /> - } - elseShow={} - /> - } + show={} /> ); diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneAutomationSection.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneAutomationSection.tsx new file mode 100644 index 0000000000..9beb220e24 --- /dev/null +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneAutomationSection.tsx @@ -0,0 +1,79 @@ +import Add from '@mui/icons-material/Add'; +import { Button, styled } from '@mui/material'; +import type { MilestoneStatus } from './ReleasePlanMilestoneStatus.tsx'; + +const StyledAutomationContainer = styled('div', { + shouldForwardProp: (prop) => prop !== 'status', +})<{ status?: MilestoneStatus }>(({ theme, status }) => ({ + border: `1px solid ${status === 'active' ? theme.palette.success.border : theme.palette.divider}`, + borderTop: `1px solid ${theme.palette.divider}`, + borderRadius: `0 0 ${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px`, + padding: theme.spacing(1.5, 2), + backgroundColor: theme.palette.background.paper, + display: 'flex', + flexDirection: 'column', + alignItems: 'stretch', + gap: theme.spacing(1), + '& > *': { + alignSelf: 'flex-start', + }, +})); + +const StyledAddAutomationButton = styled(Button)(({ theme }) => ({ + textTransform: 'none', + fontWeight: theme.typography.fontWeightBold, + fontSize: theme.typography.body2.fontSize, + padding: 0, + minWidth: 'auto', + gap: theme.spacing(1), + '&:hover': { + backgroundColor: 'transparent', + }, + '& .MuiButton-startIcon': { + margin: 0, + width: 20, + height: 20, + border: `1px solid ${theme.palette.primary.main}`, + backgroundColor: theme.palette.background.elevation2, + borderRadius: '50%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + '& svg': { + fontSize: 14, + color: theme.palette.primary.main, + }, + }, +})); + +interface IMilestoneAutomationSectionProps { + showAutomation?: boolean; + status?: MilestoneStatus; + onAddAutomation?: () => void; + automationForm?: React.ReactNode; +} + +export const MilestoneAutomationSection = ({ + showAutomation, + status, + onAddAutomation, + automationForm, +}: IMilestoneAutomationSectionProps) => { + if (!showAutomation) return null; + + return ( + + {automationForm ? ( + automationForm + ) : ( + } + > + Add automation + + )} + + ); +}; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone.tsx index c8ec8a6504..ba7289d7ae 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone.tsx @@ -16,19 +16,28 @@ import { StrategySeparator } from 'component/common/StrategySeparator/StrategySe import { StrategyItem } from '../../FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx'; import { StrategyList } from 'component/common/StrategyList/StrategyList'; import { StrategyListItem } from 'component/common/StrategyList/StrategyListItem'; +import { MilestoneAutomationSection } from './MilestoneAutomationSection.tsx'; const StyledAccordion = styled(Accordion, { - shouldForwardProp: (prop) => prop !== 'status', -})<{ status: MilestoneStatus }>(({ theme, status }) => ({ - border: `1px solid ${status === 'active' ? theme.palette.success.border : theme.palette.divider}`, - overflow: 'hidden', - boxShadow: 'none', - margin: 0, - backgroundColor: theme.palette.background.paper, - '&:before': { - display: 'none', - }, -})); + shouldForwardProp: (prop) => prop !== 'status' && prop !== 'hasAutomation', +})<{ status: MilestoneStatus; hasAutomation?: boolean }>( + ({ theme, status, hasAutomation }) => ({ + border: `1px solid ${status === 'active' ? theme.palette.success.border : theme.palette.divider}`, + borderBottom: hasAutomation + ? 'none' + : `1px solid ${status === 'active' ? theme.palette.success.border : theme.palette.divider}`, + overflow: 'hidden', + boxShadow: 'none', + margin: 0, + backgroundColor: theme.palette.background.paper, + borderRadius: hasAutomation + ? `${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px 0 0 !important` + : `${theme.shape.borderRadiusLarge}px`, + '&:before': { + display: 'none', + }, + }), +); const StyledAccordionSummary = styled(AccordionSummary)({ '& .MuiAccordionSummary-content': { @@ -58,11 +67,18 @@ const StyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({ padding: 0, })); +const StyledMilestoneContainer = styled('div')({ + position: 'relative', +}); + interface IReleasePlanMilestoneProps { milestone: IReleasePlanMilestone; status?: MilestoneStatus; onStartMilestone?: (milestone: IReleasePlanMilestone) => void; readonly?: boolean; + showAutomation?: boolean; + onAddAutomation?: () => void; + automationForm?: React.ReactNode; } export const ReleasePlanMilestone = ({ @@ -70,13 +86,51 @@ export const ReleasePlanMilestone = ({ status = 'not-started', onStartMilestone, readonly, + showAutomation, + onAddAutomation, + automationForm, }: IReleasePlanMilestoneProps) => { const [expanded, setExpanded] = useState(false); if (!milestone.strategies.length) { return ( - - + + + + + {milestone.name} + {!readonly && onStartMilestone && ( + + onStartMilestone(milestone) + } + /> + )} + + + No strategies + + + + + + ); + } + + return ( + + setExpanded(expanded)} + > + }> {milestone.name} {!readonly && onStartMilestone && ( @@ -88,53 +142,39 @@ export const ReleasePlanMilestone = ({ /> )} - No strategies + + {milestone.strategies.length === 1 + ? `${expanded ? 'Hide' : 'View'} strategy` + : `${expanded ? 'Hide' : 'View'} ${milestone.strategies.length} strategies`} + + + + {milestone.strategies.map((strategy, index) => ( + + {index > 0 ? : null} + + + + ))} + + - ); - } - - return ( - setExpanded(expanded)} - > - }> - - {milestone.name} - {!readonly && onStartMilestone && ( - onStartMilestone(milestone)} - /> - )} - - - {milestone.strategies.length === 1 - ? `${expanded ? 'Hide' : 'View'} strategy` - : `${expanded ? 'Hide' : 'View'} ${milestone.strategies.length} strategies`} - - - - - {milestone.strategies.map((strategy, index) => ( - - {index > 0 ? : null} - - - - ))} - - - + + ); };