1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-10-27 11:02:16 +01:00

feat: improve milestone visual states in release plans (#10775)

This commit is contained in:
Fredrik Strand Oseberg 2025-10-10 12:34:20 +02:00 committed by GitHub
parent 0b7c141e70
commit 4500f484ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 56 additions and 18 deletions

View File

@ -72,10 +72,14 @@ const StyledBody = styled('div')(({ theme }) => ({
flexDirection: 'column', flexDirection: 'column',
})); }));
const StyledConnection = styled('div')(({ theme }) => ({ const StyledConnection = styled('div', {
width: 4, shouldForwardProp: (prop) => prop !== 'isCompleted',
})<{ isCompleted: boolean }>(({ theme, isCompleted }) => ({
width: 2,
height: theme.spacing(2), height: theme.spacing(2),
backgroundColor: theme.palette.divider, backgroundColor: isCompleted
? theme.palette.divider
: theme.palette.primary.main,
marginLeft: theme.spacing(3.25), marginLeft: theme.spacing(3.25),
})); }));
@ -367,7 +371,11 @@ export const ReleasePlan = ({
/> />
<ConditionallyRender <ConditionallyRender
condition={isNotLastMilestone} condition={isNotLastMilestone}
show={<StyledConnection />} show={
<StyledConnection
isCompleted={index < activeIndex}
/>
}
/> />
</div> </div>
); );

View File

@ -6,11 +6,14 @@ import { MilestoneTransitionDisplay } from './MilestoneTransitionDisplay.tsx';
const StyledAutomationContainer = styled('div', { const StyledAutomationContainer = styled('div', {
shouldForwardProp: (prop) => prop !== 'status', shouldForwardProp: (prop) => prop !== 'status',
})<{ status?: MilestoneStatus }>(({ theme, 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}`, borderTop: `1px solid ${theme.palette.divider}`,
borderRadius: `0 0 ${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px`, borderRadius: `0 0 ${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px`,
padding: theme.spacing(1.5, 2), padding: theme.spacing(1.5, 2),
backgroundColor: theme.palette.background.paper, backgroundColor:
status === 'completed'
? theme.palette.background.default
: theme.palette.background.paper,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
alignItems: 'stretch', alignItems: 'stretch',
@ -79,6 +82,7 @@ export const MilestoneAutomationSection = ({
intervalMinutes={transitionCondition.intervalMinutes} intervalMinutes={transitionCondition.intervalMinutes}
onDelete={onDeleteAutomation!} onDelete={onDeleteAutomation!}
milestoneName={milestoneName} milestoneName={milestoneName}
status={status}
/> />
) : ( ) : (
<StyledAddAutomationButton <StyledAddAutomationButton

View File

@ -2,6 +2,7 @@ import BoltIcon from '@mui/icons-material/Bolt';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import { IconButton, styled } from '@mui/material'; import { IconButton, styled } from '@mui/material';
import { formatDuration, intervalToDuration } from 'date-fns'; import { formatDuration, intervalToDuration } from 'date-fns';
import type { MilestoneStatus } from './ReleasePlanMilestoneStatus.tsx';
const StyledDisplayContainer = styled('div')(({ theme }) => ({ const StyledDisplayContainer = styled('div')(({ theme }) => ({
display: 'flex', display: 'flex',
@ -17,17 +18,27 @@ const StyledContentGroup = styled('div')(({ theme }) => ({
gap: theme.spacing(1), 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, color: theme.palette.common.white,
fontSize: 18, fontSize: 18,
flexShrink: 0, flexShrink: 0,
backgroundColor: theme.palette.primary.main, backgroundColor:
status === 'completed'
? theme.palette.neutral.border
: theme.palette.primary.main,
borderRadius: '50%', borderRadius: '50%',
padding: theme.spacing(0.25), padding: theme.spacing(0.25),
})); }));
const StyledText = styled('span')(({ theme }) => ({ const StyledText = styled('span', {
color: theme.palette.text.primary, shouldForwardProp: (prop) => prop !== 'status',
})<{ status?: MilestoneStatus }>(({ theme, status }) => ({
color:
status === 'completed'
? theme.palette.text.secondary
: theme.palette.text.primary,
fontSize: theme.typography.body2.fontSize, fontSize: theme.typography.body2.fontSize,
})); }));
@ -35,6 +46,7 @@ interface IMilestoneTransitionDisplayProps {
intervalMinutes: number; intervalMinutes: number;
onDelete: () => void; onDelete: () => void;
milestoneName: string; milestoneName: string;
status?: MilestoneStatus;
} }
const formatInterval = (minutes: number): string => { const formatInterval = (minutes: number): string => {
@ -55,12 +67,13 @@ export const MilestoneTransitionDisplay = ({
intervalMinutes, intervalMinutes,
onDelete, onDelete,
milestoneName, milestoneName,
status,
}: IMilestoneTransitionDisplayProps) => { }: IMilestoneTransitionDisplayProps) => {
return ( return (
<StyledDisplayContainer> <StyledDisplayContainer>
<StyledContentGroup> <StyledContentGroup>
<StyledIcon /> <StyledIcon status={status} />
<StyledText> <StyledText status={status}>
Proceed to the next milestone after{' '} Proceed to the next milestone after{' '}
{formatInterval(intervalMinutes)} {formatInterval(intervalMinutes)}
</StyledText> </StyledText>

View File

@ -22,14 +22,17 @@ const StyledAccordion = styled(Accordion, {
shouldForwardProp: (prop) => prop !== 'status' && prop !== 'hasAutomation', shouldForwardProp: (prop) => prop !== 'status' && prop !== 'hasAutomation',
})<{ status: MilestoneStatus; hasAutomation?: boolean }>( })<{ status: MilestoneStatus; hasAutomation?: boolean }>(
({ theme, status, hasAutomation }) => ({ ({ 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 borderBottom: hasAutomation
? 'none' ? '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', overflow: 'hidden',
boxShadow: 'none', boxShadow: 'none',
margin: 0, margin: 0,
backgroundColor: theme.palette.background.paper, backgroundColor:
status === 'completed'
? theme.palette.background.default
: theme.palette.background.paper,
borderRadius: hasAutomation borderRadius: hasAutomation
? `${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px 0 0 !important` ? `${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px 0 0 !important`
: `${theme.shape.borderRadiusLarge}px`, : `${theme.shape.borderRadiusLarge}px`,
@ -54,8 +57,14 @@ const StyledTitleContainer = styled('div')(({ theme }) => ({
gap: theme.spacing(0.5), 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, fontWeight: theme.fontWeight.bold,
color:
status === 'completed'
? theme.palette.text.secondary
: theme.palette.text.primary,
})); }));
const StyledSecondaryLabel = styled('span')(({ theme }) => ({ const StyledSecondaryLabel = styled('span')(({ theme }) => ({
@ -100,7 +109,9 @@ export const ReleasePlanMilestone = ({
<StyledAccordion status={status} hasAutomation={showAutomation}> <StyledAccordion status={status} hasAutomation={showAutomation}>
<StyledAccordionSummary> <StyledAccordionSummary>
<StyledTitleContainer> <StyledTitleContainer>
<StyledTitle>{milestone.name}</StyledTitle> <StyledTitle status={status}>
{milestone.name}
</StyledTitle>
{!readonly && onStartMilestone && ( {!readonly && onStartMilestone && (
<ReleasePlanMilestoneStatus <ReleasePlanMilestoneStatus
status={status} status={status}
@ -137,7 +148,9 @@ export const ReleasePlanMilestone = ({
> >
<StyledAccordionSummary expandIcon={<ExpandMore />}> <StyledAccordionSummary expandIcon={<ExpandMore />}>
<StyledTitleContainer> <StyledTitleContainer>
<StyledTitle>{milestone.name}</StyledTitle> <StyledTitle status={status}>
{milestone.name}
</StyledTitle>
{!readonly && onStartMilestone && ( {!readonly && onStartMilestone && (
<ReleasePlanMilestoneStatus <ReleasePlanMilestoneStatus
status={status} status={status}