diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx
index 53c28f8e2e..31fd43c059 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx
@@ -17,7 +17,12 @@ import { StyledIconWrapper } from '../../FeatureEnvironmentSeen/FeatureEnvironme
import { useLastSeenColors } from '../../FeatureEnvironmentSeen/useLastSeenColors';
import type { LifecycleStage } from './LifecycleStage';
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 }) => ({
color: theme.palette.text.secondary,
@@ -279,6 +284,52 @@ const LiveStageDescription: FC = ({ children }) => {
);
};
+const SafeToArchive: FC = () => {
+ return (
+ <>
+ Safe to archive
+
+ 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.
+
+
+ Archive feature
+
+ >
+ );
+};
+
+const ActivelyUsed: FC = ({ children }) => {
+ return (
+ <>
+
+ 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:
+
+ {children}
+ >
+ );
+};
+
+const CompletedStageDescription: FC<{
+ environments: Array<{ name: string; lastSeenAt: string }>;
+}> = ({ children, environments }) => {
+ return (
+ }
+ elseShow={{children}}
+ />
+ );
+};
+
export const FeatureLifecycleTooltip: FC<{
children: React.ReactElement;
stage: LifecycleStage;
@@ -327,6 +378,13 @@ export const FeatureLifecycleTooltip: FC<{
)}
+ {stage.name === 'completed' && (
+
+
+
+ )}
}
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/LifecycleStage.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/LifecycleStage.tsx
index 79a1bf78e7..c9abaa6aa5 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/LifecycleStage.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/LifecycleStage.tsx
@@ -10,6 +10,7 @@ export type LifecycleStage =
}
| {
name: 'completed';
+ environments: Array<{ name: string; lastSeenAt: string }>;
status: 'kept' | 'discarded';
}
| { name: 'archived' };
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/isSafeToArchive.test.ts b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/isSafeToArchive.test.ts
new file mode 100644
index 0000000000..43867d4b5b
--- /dev/null
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/isSafeToArchive.test.ts
@@ -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);
+ });
+});
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/isSafeToArchive.ts b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/isSafeToArchive.ts
new file mode 100644
index 0000000000..7a04953378
--- /dev/null
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/isSafeToArchive.ts
@@ -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);
+ });
+}
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewMetaData.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewMetaData.tsx
index f391dfd8c1..3d01a9b58b 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewMetaData.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewMetaData.tsx
@@ -83,7 +83,8 @@ const FeatureOverviewMetaData = () => {
const IconComponent = getFeatureTypeIcons(type);
const currentStage: LifecycleStage = {
- name: 'live',
+ name: 'completed',
+ status: 'kept',
environments: [
{ name: 'production', lastSeenAt: new Date().toISOString() },
{