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, };