mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: Completed stage UI (#6917)
This commit is contained in:
parent
e91d471d17
commit
9c883ca37d
@ -17,7 +17,12 @@ import { StyledIconWrapper } from '../../FeatureEnvironmentSeen/FeatureEnvironme
|
|||||||
import { useLastSeenColors } from '../../FeatureEnvironmentSeen/useLastSeenColors';
|
import { useLastSeenColors } from '../../FeatureEnvironmentSeen/useLastSeenColors';
|
||||||
import type { LifecycleStage } from './LifecycleStage';
|
import type { LifecycleStage } from './LifecycleStage';
|
||||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||||
import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
import {
|
||||||
|
DELETE_FEATURE,
|
||||||
|
UPDATE_FEATURE,
|
||||||
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import { isSafeToArchive } from './isSafeToArchive';
|
||||||
|
|
||||||
const TimeLabel = styled('span')(({ theme }) => ({
|
const TimeLabel = styled('span')(({ theme }) => ({
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
@ -279,6 +284,52 @@ const LiveStageDescription: FC = ({ children }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SafeToArchive: FC = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BoldTitle>Safe to archive</BoldTitle>
|
||||||
|
<InfoText sx={{ mt: 2, mb: 1 }}>
|
||||||
|
We haven’t seen this feature flag in production for at least two
|
||||||
|
days. It’s likely that it’s safe to archive this flag.
|
||||||
|
</InfoText>
|
||||||
|
<PermissionButton
|
||||||
|
color='inherit'
|
||||||
|
variant='outlined'
|
||||||
|
permission={DELETE_FEATURE}
|
||||||
|
size='small'
|
||||||
|
sx={{ mb: 2 }}
|
||||||
|
>
|
||||||
|
Archive feature
|
||||||
|
</PermissionButton>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ActivelyUsed: FC = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<InfoText sx={{ mt: 1, mb: 1 }}>
|
||||||
|
This feature has been successfully completed, but we are still
|
||||||
|
seeing usage in production. Clean up the feature flag from your
|
||||||
|
code before archiving it:
|
||||||
|
</InfoText>
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CompletedStageDescription: FC<{
|
||||||
|
environments: Array<{ name: string; lastSeenAt: string }>;
|
||||||
|
}> = ({ children, environments }) => {
|
||||||
|
return (
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={isSafeToArchive(environments)}
|
||||||
|
show={<SafeToArchive />}
|
||||||
|
elseShow={<ActivelyUsed>{children}</ActivelyUsed>}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const FeatureLifecycleTooltip: FC<{
|
export const FeatureLifecycleTooltip: FC<{
|
||||||
children: React.ReactElement<any, any>;
|
children: React.ReactElement<any, any>;
|
||||||
stage: LifecycleStage;
|
stage: LifecycleStage;
|
||||||
@ -327,6 +378,13 @@ export const FeatureLifecycleTooltip: FC<{
|
|||||||
<Environments environments={stage.environments} />
|
<Environments environments={stage.environments} />
|
||||||
</LiveStageDescription>
|
</LiveStageDescription>
|
||||||
)}
|
)}
|
||||||
|
{stage.name === 'completed' && (
|
||||||
|
<CompletedStageDescription
|
||||||
|
environments={stage.environments}
|
||||||
|
>
|
||||||
|
<Environments environments={stage.environments} />
|
||||||
|
</CompletedStageDescription>
|
||||||
|
)}
|
||||||
</ColorFill>
|
</ColorFill>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ export type LifecycleStage =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
name: 'completed';
|
name: 'completed';
|
||||||
|
environments: Array<{ name: string; lastSeenAt: string }>;
|
||||||
status: 'kept' | 'discarded';
|
status: 'kept' | 'discarded';
|
||||||
}
|
}
|
||||||
| { name: 'archived' };
|
| { name: 'archived' };
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
import { isSafeToArchive } from './isSafeToArchive'; // Update the import path accordingly
|
||||||
|
import { subDays } from 'date-fns';
|
||||||
|
|
||||||
|
describe('isSafeToArchive', () => {
|
||||||
|
it('should return true if all environments were last seen more than two days ago', () => {
|
||||||
|
const now = new Date();
|
||||||
|
const environments = [
|
||||||
|
{ name: 'Production', lastSeenAt: subDays(now, 3).toISOString() },
|
||||||
|
{ name: 'Staging', lastSeenAt: subDays(now, 4).toISOString() },
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = isSafeToArchive(environments);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if any environment was seen within the last two days', () => {
|
||||||
|
const now = new Date();
|
||||||
|
const environments = [
|
||||||
|
{ name: 'Production', lastSeenAt: subDays(now, 3).toISOString() },
|
||||||
|
{ name: 'Staging', lastSeenAt: subDays(now, 1).toISOString() },
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = isSafeToArchive(environments);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if all environments were seen within the last two days', () => {
|
||||||
|
const now = new Date();
|
||||||
|
const environments = [
|
||||||
|
{ name: 'Production', lastSeenAt: subDays(now, 0).toISOString() },
|
||||||
|
{ name: 'Staging', lastSeenAt: subDays(now, 1).toISOString() },
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = isSafeToArchive(environments);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true for an empty array of environments', () => {
|
||||||
|
const environments: Array<{ name: string; lastSeenAt: string }> = [];
|
||||||
|
|
||||||
|
const result = isSafeToArchive(environments);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,13 @@
|
|||||||
|
import { isBefore, parseISO, subDays } from 'date-fns';
|
||||||
|
|
||||||
|
export function isSafeToArchive(
|
||||||
|
environments: Array<{ name: string; lastSeenAt: string }>,
|
||||||
|
) {
|
||||||
|
const twoDaysAgo = subDays(new Date(), 2);
|
||||||
|
|
||||||
|
return environments.every((env) => {
|
||||||
|
const lastSeenDate = parseISO(env.lastSeenAt);
|
||||||
|
|
||||||
|
return isBefore(lastSeenDate, twoDaysAgo);
|
||||||
|
});
|
||||||
|
}
|
@ -83,7 +83,8 @@ const FeatureOverviewMetaData = () => {
|
|||||||
const IconComponent = getFeatureTypeIcons(type);
|
const IconComponent = getFeatureTypeIcons(type);
|
||||||
|
|
||||||
const currentStage: LifecycleStage = {
|
const currentStage: LifecycleStage = {
|
||||||
name: 'live',
|
name: 'completed',
|
||||||
|
status: 'kept',
|
||||||
environments: [
|
environments: [
|
||||||
{ name: 'production', lastSeenAt: new Date().toISOString() },
|
{ name: 'production', lastSeenAt: new Date().toISOString() },
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user