mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
feat: live and pre-live stages UI (#6913)
This commit is contained in:
parent
d578deab7f
commit
f63bae21f5
@ -33,7 +33,7 @@ const StyledBox = styled(Box)(({ theme }) => ({
|
|||||||
margin: '0 auto',
|
margin: '0 auto',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledIconWrapper = styled('div')(({ theme }) => ({
|
export const StyledIconWrapper = styled('div')(({ theme }) => ({
|
||||||
width: '20px',
|
width: '20px',
|
||||||
height: '20px',
|
height: '20px',
|
||||||
background: theme.palette.background.paper,
|
background: theme.palette.background.paper,
|
||||||
|
@ -8,8 +8,14 @@ import { ReactComponent as ArchivedStageIcon } from 'assets/icons/stage-archived
|
|||||||
|
|
||||||
export type LifecycleStage =
|
export type LifecycleStage =
|
||||||
| { name: 'initial' }
|
| { name: 'initial' }
|
||||||
| { name: 'pre-live' }
|
| {
|
||||||
| { name: 'live' }
|
name: 'pre-live';
|
||||||
|
environments: Array<{ name: string; lastSeenAt: string }>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: 'live';
|
||||||
|
environments: Array<{ name: string; lastSeenAt: string }>;
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
name: 'completed';
|
name: 'completed';
|
||||||
status: 'kept' | 'discarded';
|
status: 'kept' | 'discarded';
|
||||||
|
@ -9,10 +9,16 @@ import { ReactComponent as LiveStageIcon } from 'assets/icons/stage-live.svg';
|
|||||||
import { ReactComponent as CompletedStageIcon } from 'assets/icons/stage-completed.svg';
|
import { ReactComponent as CompletedStageIcon } from 'assets/icons/stage-completed.svg';
|
||||||
import { ReactComponent as CompletedDiscardedStageIcon } from 'assets/icons/stage-completed-discarded.svg';
|
import { ReactComponent as CompletedDiscardedStageIcon } from 'assets/icons/stage-completed-discarded.svg';
|
||||||
import { ReactComponent as ArchivedStageIcon } from 'assets/icons/stage-archived.svg';
|
import { ReactComponent as ArchivedStageIcon } from 'assets/icons/stage-archived.svg';
|
||||||
|
import CloudCircle from '@mui/icons-material/CloudCircle';
|
||||||
|
import { ReactComponent as UsageRate } from 'assets/icons/usage-rate.svg';
|
||||||
import {
|
import {
|
||||||
FeatureLifecycleStageIcon,
|
FeatureLifecycleStageIcon,
|
||||||
type LifecycleStage,
|
type LifecycleStage,
|
||||||
} from './FeatureLifecycleStageIcon';
|
} from './FeatureLifecycleStageIcon';
|
||||||
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import TimeAgo from 'react-timeago';
|
||||||
|
import { StyledIconWrapper } from '../../FeatureEnvironmentSeen/FeatureEnvironmentSeen';
|
||||||
|
import { useLastSeenColors } from '../../FeatureEnvironmentSeen/useLastSeenColors';
|
||||||
|
|
||||||
const TimeLabel = styled('span')(({ theme }) => ({
|
const TimeLabel = styled('span')(({ theme }) => ({
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
@ -95,6 +101,159 @@ const ColorFill = styled(Box)(({ theme }) => ({
|
|||||||
padding: theme.spacing(2, 3),
|
padding: theme.spacing(2, 3),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const LastSeenIcon: FC<{ lastSeen: string }> = ({ lastSeen }) => {
|
||||||
|
const getColor = useLastSeenColors();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TimeAgo
|
||||||
|
date={lastSeen}
|
||||||
|
title=''
|
||||||
|
live={false}
|
||||||
|
formatter={(value: number, unit: string) => {
|
||||||
|
const [color, textColor] = getColor(unit);
|
||||||
|
return (
|
||||||
|
<StyledIconWrapper style={{ background: color }}>
|
||||||
|
<UsageRate stroke={textColor} />
|
||||||
|
</StyledIconWrapper>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const InitialStageDescription: FC = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<InfoText>
|
||||||
|
This feature toggle is currently in the initial phase of it's
|
||||||
|
life cycle.
|
||||||
|
</InfoText>
|
||||||
|
<InfoText>
|
||||||
|
This means that the flag has been created, but it has not yet
|
||||||
|
been seen in any environment.
|
||||||
|
</InfoText>
|
||||||
|
<InfoText>
|
||||||
|
Once we detect metrics for a non-production environment it will
|
||||||
|
move into pre-live.
|
||||||
|
</InfoText>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const StageTimeline: FC<{ stage: LifecycleStage }> = ({ stage }) => {
|
||||||
|
return (
|
||||||
|
<IconsRow>
|
||||||
|
<StageBox
|
||||||
|
data-after-content='Initial'
|
||||||
|
active={stage.name === 'initial'}
|
||||||
|
>
|
||||||
|
<InitialStageIcon />
|
||||||
|
</StageBox>
|
||||||
|
|
||||||
|
<Line />
|
||||||
|
|
||||||
|
<StageBox
|
||||||
|
data-after-content='Pre-live'
|
||||||
|
active={stage.name === 'pre-live'}
|
||||||
|
>
|
||||||
|
<PreLiveStageIcon />
|
||||||
|
</StageBox>
|
||||||
|
|
||||||
|
<Line />
|
||||||
|
|
||||||
|
<StageBox data-after-content='Live' active={stage.name === 'live'}>
|
||||||
|
<LiveStageIcon />
|
||||||
|
</StageBox>
|
||||||
|
|
||||||
|
<Line />
|
||||||
|
|
||||||
|
<StageBox
|
||||||
|
data-after-content='Completed'
|
||||||
|
active={stage.name === 'completed'}
|
||||||
|
>
|
||||||
|
{stage.name === 'completed' && stage.status === 'discarded' ? (
|
||||||
|
<CompletedDiscardedStageIcon />
|
||||||
|
) : (
|
||||||
|
<CompletedStageIcon />
|
||||||
|
)}
|
||||||
|
</StageBox>
|
||||||
|
|
||||||
|
<Line />
|
||||||
|
|
||||||
|
<StageBox
|
||||||
|
data-after-content='Archived'
|
||||||
|
active={stage.name === 'archived'}
|
||||||
|
>
|
||||||
|
<ArchivedStageIcon />
|
||||||
|
</StageBox>
|
||||||
|
</IconsRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const EnvironmentLine = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const CenteredBox = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const LiveStageDescription: FC<{
|
||||||
|
name: 'live' | 'pre-live';
|
||||||
|
environments: Array<{ name: string; lastSeenAt: string }>;
|
||||||
|
}> = ({ name, environments }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={name === 'pre-live'}
|
||||||
|
show={
|
||||||
|
<InfoText>
|
||||||
|
We've seen the feature flag in the following
|
||||||
|
non-production environments:
|
||||||
|
</InfoText>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={name === 'live'}
|
||||||
|
show={
|
||||||
|
<InfoText>
|
||||||
|
Users have been exposed to this feature in the following
|
||||||
|
production environments:
|
||||||
|
</InfoText>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
{environments.map((environment) => {
|
||||||
|
return (
|
||||||
|
<EnvironmentLine key={environment.name}>
|
||||||
|
<CenteredBox>
|
||||||
|
<CloudCircle />
|
||||||
|
<Box>{environment.name}</Box>
|
||||||
|
</CenteredBox>
|
||||||
|
<CenteredBox>
|
||||||
|
<TimeAgo
|
||||||
|
minPeriod={60}
|
||||||
|
date={environment.lastSeenAt}
|
||||||
|
/>
|
||||||
|
<LastSeenIcon
|
||||||
|
lastSeen={environment.lastSeenAt}
|
||||||
|
/>
|
||||||
|
</CenteredBox>
|
||||||
|
</EnvironmentLine>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const FeatureLifecycleTooltip: FC<{
|
export const FeatureLifecycleTooltip: FC<{
|
||||||
children: React.ReactElement<any, any>;
|
children: React.ReactElement<any, any>;
|
||||||
stage: LifecycleStage;
|
stage: LifecycleStage;
|
||||||
@ -129,69 +288,16 @@ export const FeatureLifecycleTooltip: FC<{
|
|||||||
<TimeLabel>Time spent in stage</TimeLabel>
|
<TimeLabel>Time spent in stage</TimeLabel>
|
||||||
<span>3 days</span>
|
<span>3 days</span>
|
||||||
</TimeLifecycleRow>
|
</TimeLifecycleRow>
|
||||||
<IconsRow>
|
<StageTimeline stage={stage} />
|
||||||
<StageBox
|
|
||||||
data-after-content='Initial'
|
|
||||||
active={stage.name === 'initial'}
|
|
||||||
>
|
|
||||||
<InitialStageIcon />
|
|
||||||
</StageBox>
|
|
||||||
|
|
||||||
<Line />
|
|
||||||
|
|
||||||
<StageBox
|
|
||||||
data-after-content='Pre-live'
|
|
||||||
active={stage.name === 'pre-live'}
|
|
||||||
>
|
|
||||||
<PreLiveStageIcon />
|
|
||||||
</StageBox>
|
|
||||||
|
|
||||||
<Line />
|
|
||||||
|
|
||||||
<StageBox
|
|
||||||
data-after-content='Live'
|
|
||||||
active={stage.name === 'live'}
|
|
||||||
>
|
|
||||||
<LiveStageIcon />
|
|
||||||
</StageBox>
|
|
||||||
|
|
||||||
<Line />
|
|
||||||
|
|
||||||
<StageBox
|
|
||||||
data-after-content='Completed'
|
|
||||||
active={stage.name === 'completed'}
|
|
||||||
>
|
|
||||||
{stage.name === 'completed' &&
|
|
||||||
stage.status === 'discarded' ? (
|
|
||||||
<CompletedDiscardedStageIcon />
|
|
||||||
) : (
|
|
||||||
<CompletedStageIcon />
|
|
||||||
)}
|
|
||||||
</StageBox>
|
|
||||||
|
|
||||||
<Line />
|
|
||||||
|
|
||||||
<StageBox
|
|
||||||
data-after-content='Archived'
|
|
||||||
active={stage.name === 'archived'}
|
|
||||||
>
|
|
||||||
<ArchivedStageIcon />
|
|
||||||
</StageBox>
|
|
||||||
</IconsRow>
|
|
||||||
</Box>
|
</Box>
|
||||||
<ColorFill>
|
<ColorFill>
|
||||||
<InfoText>
|
{stage.name === 'initial' && <InitialStageDescription />}
|
||||||
This feature toggle is currently in the initial phase of
|
{(stage.name === 'pre-live' || stage.name === 'live') && (
|
||||||
it's life cycle.
|
<LiveStageDescription
|
||||||
</InfoText>
|
name={stage.name}
|
||||||
<InfoText>
|
environments={stage.environments}
|
||||||
This means that the flag has been created, but it has
|
/>
|
||||||
not yet been seen in any environment.
|
)}
|
||||||
</InfoText>
|
|
||||||
<InfoText>
|
|
||||||
Once we detect metrics for a non-production environment
|
|
||||||
it will move into pre-live.
|
|
||||||
</InfoText>
|
|
||||||
</ColorFill>
|
</ColorFill>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user