mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-31 13:47:02 +02:00
feat: lifecycle stage dates (#6926)
This commit is contained in:
parent
68e7a3164e
commit
0eaf725e82
@ -23,6 +23,9 @@ import {
|
|||||||
} from 'component/providers/AccessProvider/permissions';
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { isSafeToArchive } from './isSafeToArchive';
|
import { isSafeToArchive } from './isSafeToArchive';
|
||||||
|
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||||
|
import { formatDateYMDHMS } from 'utils/formatDate';
|
||||||
|
import { formatDistanceToNow, parseISO } from 'date-fns';
|
||||||
|
|
||||||
const TimeLabel = styled('span')(({ theme }) => ({
|
const TimeLabel = styled('span')(({ theme }) => ({
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
@ -330,6 +333,18 @@ const CompletedStageDescription: FC<{
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const FormatTime: FC<{ time: string }> = ({ time }) => {
|
||||||
|
const { locationSettings } = useLocationSettings();
|
||||||
|
|
||||||
|
return <span>{formatDateYMDHMS(time, locationSettings.locale)}</span>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FormatElapsedTime: FC<{ time: string }> = ({ time }) => {
|
||||||
|
const pastTime = parseISO(time);
|
||||||
|
const elapsedTime = formatDistanceToNow(pastTime, { addSuffix: false });
|
||||||
|
return <span>{elapsedTime}</span>;
|
||||||
|
};
|
||||||
|
|
||||||
export const FeatureLifecycleTooltip: FC<{
|
export const FeatureLifecycleTooltip: FC<{
|
||||||
children: React.ReactElement<any, any>;
|
children: React.ReactElement<any, any>;
|
||||||
stage: LifecycleStage;
|
stage: LifecycleStage;
|
||||||
@ -358,11 +373,12 @@ export const FeatureLifecycleTooltip: FC<{
|
|||||||
</MainLifecycleRow>
|
</MainLifecycleRow>
|
||||||
<TimeLifecycleRow>
|
<TimeLifecycleRow>
|
||||||
<TimeLabel>Stage entered at</TimeLabel>
|
<TimeLabel>Stage entered at</TimeLabel>
|
||||||
<span>14/01/2024</span>
|
|
||||||
|
<FormatTime time={stage.enteredStageAt} />
|
||||||
</TimeLifecycleRow>
|
</TimeLifecycleRow>
|
||||||
<TimeLifecycleRow>
|
<TimeLifecycleRow>
|
||||||
<TimeLabel>Time spent in stage</TimeLabel>
|
<TimeLabel>Time spent in stage</TimeLabel>
|
||||||
<span>3 days</span>
|
<FormatElapsedTime time={stage.enteredStageAt} />
|
||||||
</TimeLifecycleRow>
|
</TimeLifecycleRow>
|
||||||
<StageTimeline stage={stage} />
|
<StageTimeline stage={stage} />
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
export type LifecycleStage =
|
type TimedStage = { enteredStageAt: string };
|
||||||
| { name: 'initial' }
|
export type LifecycleStage = TimedStage &
|
||||||
| {
|
(
|
||||||
name: 'pre-live';
|
| { name: 'initial' }
|
||||||
environments: Array<{ name: string; lastSeenAt: string }>;
|
| {
|
||||||
}
|
name: 'pre-live';
|
||||||
| {
|
environments: Array<{ name: string; lastSeenAt: string }>;
|
||||||
name: 'live';
|
}
|
||||||
environments: Array<{ name: string; lastSeenAt: string }>;
|
| {
|
||||||
}
|
name: 'live';
|
||||||
| {
|
environments: Array<{ name: string; lastSeenAt: string }>;
|
||||||
name: 'completed';
|
}
|
||||||
environments: Array<{ name: string; lastSeenAt: string }>;
|
| {
|
||||||
status: 'kept' | 'discarded';
|
name: 'completed';
|
||||||
}
|
environments: Array<{ name: string; lastSeenAt: string }>;
|
||||||
| { name: 'archived' };
|
status: 'kept' | 'discarded';
|
||||||
|
}
|
||||||
|
| { name: 'archived' }
|
||||||
|
);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { populateCurrentStage } from './populateCurrentStage';
|
import { populateCurrentStage } from './populateCurrentStage';
|
||||||
import type { IFeatureToggle } from '../../../../../interfaces/featureToggle';
|
import type { IFeatureToggle } from '../../../../../interfaces/featureToggle';
|
||||||
|
|
||||||
|
const enteredStageAt = 'date';
|
||||||
|
|
||||||
describe('populateCurrentStage', () => {
|
describe('populateCurrentStage', () => {
|
||||||
it('should return undefined if lifecycle is not defined', () => {
|
it('should return undefined if lifecycle is not defined', () => {
|
||||||
const feature = {};
|
const feature = {};
|
||||||
@ -10,16 +12,16 @@ describe('populateCurrentStage', () => {
|
|||||||
|
|
||||||
it('should return initial stage when lifecycle stage is initial', () => {
|
it('should return initial stage when lifecycle stage is initial', () => {
|
||||||
const feature = {
|
const feature = {
|
||||||
lifecycle: { stage: 'initial' },
|
lifecycle: { stage: 'initial', enteredStageAt },
|
||||||
};
|
};
|
||||||
const expected = { name: 'initial' };
|
const expected = { name: 'initial', enteredStageAt };
|
||||||
const result = populateCurrentStage(feature as IFeatureToggle);
|
const result = populateCurrentStage(feature as IFeatureToggle);
|
||||||
expect(result).toEqual(expected);
|
expect(result).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly populate pre-live stage with dev environments', () => {
|
it('should correctly populate pre-live stage with dev environments', () => {
|
||||||
const feature = {
|
const feature = {
|
||||||
lifecycle: { stage: 'pre-live' },
|
lifecycle: { stage: 'pre-live', enteredStageAt },
|
||||||
environments: [
|
environments: [
|
||||||
{ name: 'test', type: 'development', lastSeenAt: null },
|
{ name: 'test', type: 'development', lastSeenAt: null },
|
||||||
{ name: 'test1', type: 'production', lastSeenAt: '2022-08-01' },
|
{ name: 'test1', type: 'production', lastSeenAt: '2022-08-01' },
|
||||||
@ -29,6 +31,7 @@ describe('populateCurrentStage', () => {
|
|||||||
const expected = {
|
const expected = {
|
||||||
name: 'pre-live',
|
name: 'pre-live',
|
||||||
environments: [{ name: 'dev', lastSeenAt: '2022-08-01' }],
|
environments: [{ name: 'dev', lastSeenAt: '2022-08-01' }],
|
||||||
|
enteredStageAt,
|
||||||
};
|
};
|
||||||
const result = populateCurrentStage(feature);
|
const result = populateCurrentStage(feature);
|
||||||
expect(result).toEqual(expected);
|
expect(result).toEqual(expected);
|
||||||
@ -36,7 +39,7 @@ describe('populateCurrentStage', () => {
|
|||||||
|
|
||||||
it('should handle live stage with production environments', () => {
|
it('should handle live stage with production environments', () => {
|
||||||
const feature = {
|
const feature = {
|
||||||
lifecycle: { stage: 'live' },
|
lifecycle: { stage: 'live', enteredStageAt },
|
||||||
environments: [
|
environments: [
|
||||||
{ name: 'prod', type: 'production', lastSeenAt: '2022-08-01' },
|
{ name: 'prod', type: 'production', lastSeenAt: '2022-08-01' },
|
||||||
],
|
],
|
||||||
@ -44,6 +47,7 @@ describe('populateCurrentStage', () => {
|
|||||||
const expected = {
|
const expected = {
|
||||||
name: 'live',
|
name: 'live',
|
||||||
environments: [{ name: 'prod', lastSeenAt: '2022-08-01' }],
|
environments: [{ name: 'prod', lastSeenAt: '2022-08-01' }],
|
||||||
|
enteredStageAt,
|
||||||
};
|
};
|
||||||
const result = populateCurrentStage(feature);
|
const result = populateCurrentStage(feature);
|
||||||
expect(result).toEqual(expected);
|
expect(result).toEqual(expected);
|
||||||
@ -51,7 +55,7 @@ describe('populateCurrentStage', () => {
|
|||||||
|
|
||||||
it('should return completed stage with production environments', () => {
|
it('should return completed stage with production environments', () => {
|
||||||
const feature = {
|
const feature = {
|
||||||
lifecycle: { stage: 'completed' },
|
lifecycle: { stage: 'completed', enteredStageAt },
|
||||||
environments: [
|
environments: [
|
||||||
{ name: 'prod', type: 'production', lastSeenAt: '2022-08-01' },
|
{ name: 'prod', type: 'production', lastSeenAt: '2022-08-01' },
|
||||||
],
|
],
|
||||||
@ -60,6 +64,7 @@ describe('populateCurrentStage', () => {
|
|||||||
name: 'completed',
|
name: 'completed',
|
||||||
status: 'kept',
|
status: 'kept',
|
||||||
environments: [{ name: 'prod', lastSeenAt: '2022-08-01' }],
|
environments: [{ name: 'prod', lastSeenAt: '2022-08-01' }],
|
||||||
|
enteredStageAt,
|
||||||
};
|
};
|
||||||
const result = populateCurrentStage(feature);
|
const result = populateCurrentStage(feature);
|
||||||
expect(result).toEqual(expected);
|
expect(result).toEqual(expected);
|
||||||
@ -67,9 +72,9 @@ describe('populateCurrentStage', () => {
|
|||||||
|
|
||||||
it('should return archived stage when lifecycle stage is archived', () => {
|
it('should return archived stage when lifecycle stage is archived', () => {
|
||||||
const feature = {
|
const feature = {
|
||||||
lifecycle: { stage: 'archived' },
|
lifecycle: { stage: 'archived', enteredStageAt },
|
||||||
} as IFeatureToggle;
|
} as IFeatureToggle;
|
||||||
const expected = { name: 'archived' };
|
const expected = { name: 'archived', enteredStageAt };
|
||||||
const result = populateCurrentStage(feature);
|
const result = populateCurrentStage(feature);
|
||||||
expect(result).toEqual(expected);
|
expect(result).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
@ -15,15 +15,18 @@ export const populateCurrentStage = (
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const enteredStageAt = feature.lifecycle.enteredStageAt;
|
||||||
|
|
||||||
switch (feature.lifecycle.stage) {
|
switch (feature.lifecycle.stage) {
|
||||||
case 'initial':
|
case 'initial':
|
||||||
return { name: 'initial' };
|
return { name: 'initial', enteredStageAt };
|
||||||
case 'pre-live':
|
case 'pre-live':
|
||||||
return {
|
return {
|
||||||
name: 'pre-live',
|
name: 'pre-live',
|
||||||
environments: getFilteredEnvironments(
|
environments: getFilteredEnvironments(
|
||||||
(type) => type !== 'production',
|
(type) => type !== 'production',
|
||||||
),
|
),
|
||||||
|
enteredStageAt,
|
||||||
};
|
};
|
||||||
case 'live':
|
case 'live':
|
||||||
return {
|
return {
|
||||||
@ -31,6 +34,7 @@ export const populateCurrentStage = (
|
|||||||
environments: getFilteredEnvironments(
|
environments: getFilteredEnvironments(
|
||||||
(type) => type === 'production',
|
(type) => type === 'production',
|
||||||
),
|
),
|
||||||
|
enteredStageAt,
|
||||||
};
|
};
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return {
|
return {
|
||||||
@ -39,9 +43,10 @@ export const populateCurrentStage = (
|
|||||||
environments: getFilteredEnvironments(
|
environments: getFilteredEnvironments(
|
||||||
(type) => type === 'production',
|
(type) => type === 'production',
|
||||||
),
|
),
|
||||||
|
enteredStageAt,
|
||||||
};
|
};
|
||||||
case 'archived':
|
case 'archived':
|
||||||
return { name: 'archived' };
|
return { name: 'archived', enteredStageAt };
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user