From 4500f484ece003987847c0b76efbd390b1cb6ee9 Mon Sep 17 00:00:00 2001 From: Fredrik Strand Oseberg Date: Fri, 10 Oct 2025 12:34:20 +0200 Subject: [PATCH] feat: improve milestone visual states in release plans (#10775) --- .../ReleasePlan/ReleasePlan.tsx | 16 +++++++++--- .../MilestoneAutomationSection.tsx | 8 ++++-- .../MilestoneTransitionDisplay.tsx | 25 ++++++++++++++----- .../ReleasePlanMilestone.tsx | 25 ++++++++++++++----- 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx index f25ee0b639..6dd681f61c 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan.tsx @@ -72,10 +72,14 @@ const StyledBody = styled('div')(({ theme }) => ({ flexDirection: 'column', })); -const StyledConnection = styled('div')(({ theme }) => ({ - width: 4, +const StyledConnection = styled('div', { + shouldForwardProp: (prop) => prop !== 'isCompleted', +})<{ isCompleted: boolean }>(({ theme, isCompleted }) => ({ + width: 2, height: theme.spacing(2), - backgroundColor: theme.palette.divider, + backgroundColor: isCompleted + ? theme.palette.divider + : theme.palette.primary.main, marginLeft: theme.spacing(3.25), })); @@ -367,7 +371,11 @@ export const ReleasePlan = ({ /> } + 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 index 30b3dd8b38..3e317b5c4c 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneAutomationSection.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneAutomationSection.tsx @@ -6,11 +6,14 @@ import { MilestoneTransitionDisplay } from './MilestoneTransitionDisplay.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}`, + border: `${status === 'active' ? '1.25px' : '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, + backgroundColor: + status === 'completed' + ? theme.palette.background.default + : theme.palette.background.paper, display: 'flex', flexDirection: 'column', alignItems: 'stretch', @@ -79,6 +82,7 @@ export const MilestoneAutomationSection = ({ intervalMinutes={transitionCondition.intervalMinutes} onDelete={onDeleteAutomation!} milestoneName={milestoneName} + status={status} /> ) : ( ({ display: 'flex', @@ -17,17 +18,27 @@ const StyledContentGroup = styled('div')(({ theme }) => ({ gap: theme.spacing(1), })); -const StyledIcon = styled(BoltIcon)(({ theme }) => ({ +const StyledIcon = styled(BoltIcon, { + shouldForwardProp: (prop) => prop !== 'status', +})<{ status?: MilestoneStatus }>(({ theme, status }) => ({ color: theme.palette.common.white, fontSize: 18, flexShrink: 0, - backgroundColor: theme.palette.primary.main, + backgroundColor: + status === 'completed' + ? theme.palette.neutral.border + : theme.palette.primary.main, borderRadius: '50%', padding: theme.spacing(0.25), })); -const StyledText = styled('span')(({ theme }) => ({ - color: theme.palette.text.primary, +const StyledText = styled('span', { + shouldForwardProp: (prop) => prop !== 'status', +})<{ status?: MilestoneStatus }>(({ theme, status }) => ({ + color: + status === 'completed' + ? theme.palette.text.secondary + : theme.palette.text.primary, fontSize: theme.typography.body2.fontSize, })); @@ -35,6 +46,7 @@ interface IMilestoneTransitionDisplayProps { intervalMinutes: number; onDelete: () => void; milestoneName: string; + status?: MilestoneStatus; } const formatInterval = (minutes: number): string => { @@ -55,12 +67,13 @@ export const MilestoneTransitionDisplay = ({ intervalMinutes, onDelete, milestoneName, + status, }: IMilestoneTransitionDisplayProps) => { return ( - - + + Proceed to the next milestone after{' '} {formatInterval(intervalMinutes)} 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 0802d4ba73..25d72f74d8 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone.tsx @@ -22,14 +22,17 @@ const StyledAccordion = styled(Accordion, { 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}`, + border: `${status === 'active' ? '1.25px' : '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}`, + : `${status === 'active' ? '1.25px' : '1px'} solid ${status === 'active' ? theme.palette.success.border : theme.palette.divider}`, overflow: 'hidden', boxShadow: 'none', margin: 0, - backgroundColor: theme.palette.background.paper, + backgroundColor: + status === 'completed' + ? theme.palette.background.default + : theme.palette.background.paper, borderRadius: hasAutomation ? `${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px 0 0 !important` : `${theme.shape.borderRadiusLarge}px`, @@ -54,8 +57,14 @@ const StyledTitleContainer = styled('div')(({ theme }) => ({ gap: theme.spacing(0.5), })); -const StyledTitle = styled('span')(({ theme }) => ({ +const StyledTitle = styled('span', { + shouldForwardProp: (prop) => prop !== 'status', +})<{ status?: MilestoneStatus }>(({ theme, status }) => ({ fontWeight: theme.fontWeight.bold, + color: + status === 'completed' + ? theme.palette.text.secondary + : theme.palette.text.primary, })); const StyledSecondaryLabel = styled('span')(({ theme }) => ({ @@ -100,7 +109,9 @@ export const ReleasePlanMilestone = ({ - {milestone.name} + + {milestone.name} + {!readonly && onStartMilestone && ( }> - {milestone.name} + + {milestone.name} + {!readonly && onStartMilestone && (