diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.test.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.test.tsx
index f982268f7c..843305c2a6 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.test.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.test.tsx
@@ -15,6 +15,7 @@ const renderOpenTooltip = (
stage: LifecycleStage,
onArchive = () => {},
onComplete = () => {},
+ onUncomplete = () => {},
loading = true,
) => {
render(
@@ -22,6 +23,7 @@ const renderOpenTooltip = (
stage={stage}
onArchive={onArchive}
onComplete={onComplete}
+ onUncomplete={onUncomplete}
loading={loading}
>
child
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx
index fd86746412..70765edf96 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx
@@ -62,7 +62,9 @@ const Line = styled(Box)(({ theme }) => ({
const StageBox = styled(Box, {
shouldForwardProp: (prop) => prop !== 'active',
-})<{ active?: boolean }>(({ theme, active }) => ({
+})<{
+ active?: boolean;
+}>(({ theme, active }) => ({
position: 'relative',
// speech bubble triangle for active stage
...(active && {
@@ -108,7 +110,9 @@ const ColorFill = styled(Box)(({ theme }) => ({
padding: theme.spacing(2, 3),
}));
-const LastSeenIcon: FC<{ lastSeen: string }> = ({ lastSeen }) => {
+const LastSeenIcon: FC<{
+ lastSeen: string;
+}> = ({ lastSeen }) => {
const getColor = useLastSeenColors();
return (
@@ -147,7 +151,9 @@ const InitialStageDescription: FC = () => {
);
};
-const StageTimeline: FC<{ stage: LifecycleStage }> = ({ stage }) => {
+const StageTimeline: FC<{
+ stage: LifecycleStage;
+}> = ({ stage }) => {
return (
({
}));
const Environments: FC<{
- environments: Array<{ name: string; lastSeenAt: string }>;
+ environments: Array<{
+ name: string;
+ lastSeenAt: string;
+ }>;
}> = ({ environments }) => {
return (
@@ -280,7 +289,7 @@ const LiveStageDescription: FC<{
onClick={onComplete}
disabled={loading}
>
- Mark Completed
+ Mark completed
Users have been exposed to this feature in the following
@@ -292,61 +301,124 @@ const LiveStageDescription: FC<{
);
};
-const SafeToArchive: FC<{ onArchive: () => void }> = ({ onArchive }) => {
+const SafeToArchive: FC<{
+ onArchive: () => void;
+ onUncomplete: () => void;
+ loading: boolean;
+}> = ({ onArchive, onUncomplete, loading }) => {
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
-
+
+ Revert to live
+
+
+ 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 ActivelyUsed: FC<{
+ onUncomplete: () => void;
+ loading: boolean;
+}> = ({ children, onUncomplete, loading }) => (
+ <>
+
+ 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:
+
+
+ Revert to live
+
+ {children}
+ >
+);
const CompletedStageDescription: FC<{
onArchive: () => void;
- environments: Array<{ name: string; lastSeenAt: string }>;
-}> = ({ children, environments, onArchive }) => {
+ onUncomplete: () => void;
+ loading: boolean;
+ environments: Array<{
+ name: string;
+ lastSeenAt: string;
+ }>;
+}> = ({ children, environments, onArchive, onUncomplete, loading }) => {
return (
}
- elseShow={{children}}
+ show={
+
+ }
+ elseShow={
+
+ {children}
+
+ }
/>
);
};
-const FormatTime: FC<{ time: string }> = ({ time }) => {
+const FormatTime: FC<{
+ time: string;
+}> = ({ time }) => {
const { locationSettings } = useLocationSettings();
return {formatDateYMDHMS(time, locationSettings.locale)};
};
-const FormatElapsedTime: FC<{ time: string }> = ({ time }) => {
+const FormatElapsedTime: FC<{
+ time: string;
+}> = ({ time }) => {
const pastTime = parseISO(time);
const elapsedTime = formatDistanceToNow(pastTime, { addSuffix: false });
return {elapsedTime};
@@ -357,8 +429,9 @@ export const FeatureLifecycleTooltip: FC<{
stage: LifecycleStage;
onArchive: () => void;
onComplete: () => void;
+ onUncomplete: () => void;
loading: boolean;
-}> = ({ children, stage, onArchive, onComplete, loading }) => (
+}> = ({ children, stage, onArchive, onComplete, onUncomplete, loading }) => (
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewMetaData.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewMetaData.tsx
index dc6dffb313..300a2d8c1f 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 { feature, refetchFeature } = useFeature(projectId, featureId);
const { project, description, type } = feature;
const featureLifecycleEnabled = useUiFlag('featureLifecycle');
- const { markFeatureCompleted, loading } = useFeatureLifecycleApi();
+ const { markFeatureCompleted, markFeatureUncompleted, loading } =
+ useFeatureLifecycleApi();
const navigate = useNavigate();
const [showDelDialog, setShowDelDialog] = useState(false);
@@ -96,6 +97,11 @@ const FeatureOverviewMetaData = () => {
refetchFeature();
};
+ const onUncomplete = async () => {
+ await markFeatureUncompleted(featureId, projectId);
+ refetchFeature();
+ };
+
return (
@@ -130,6 +136,7 @@ const FeatureOverviewMetaData = () => {
stage={currentStage!}
onArchive={() => setShowDelDialog(true)}
onComplete={onComplete}
+ onUncomplete={onUncomplete}
loading={loading}
>
{
return makeRequest(req.caller, req.id);
};
+ const markFeatureUncompleted = async (name: string, project: string) => {
+ const path = `api/admin/projects/${project}/features/${name}/lifecycle/uncomplete`;
+ const req = createRequest(path, {
+ method: 'POST',
+ });
+
+ return makeRequest(req.caller, req.id);
+ };
+
return {
markFeatureCompleted,
+ markFeatureUncompleted,
errors,
loading,
};