diff --git a/frontend/src/component/InitialRedirect.tsx b/frontend/src/component/InitialRedirect.tsx index 5c0ebd201a..0589aad019 100644 --- a/frontend/src/component/InitialRedirect.tsx +++ b/frontend/src/component/InitialRedirect.tsx @@ -6,10 +6,12 @@ import Loader from './common/Loader/Loader'; import { useLocalStorageState } from 'hooks/useLocalStorageState'; import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser'; +const defaultPage = '/personal'; + export const useLastViewedPage = (location?: Location) => { const [state, setState] = useLocalStorageState( 'lastViewedPage', - '/personal', + defaultPage, 7 * 24 * 60 * 60 * 1000, // 7 days, left to promote seeing Personal dashboard from time to time ); @@ -55,5 +57,9 @@ export const InitialRedirect = () => { return ; } - return ; + if (lastViewedPage && lastViewedPage !== '/') { + return ; + } + + return ; }; diff --git a/frontend/src/component/demo/DemoSteps/DemoStepTooltip/DemoStepTooltip.tsx b/frontend/src/component/demo/DemoSteps/DemoStepTooltip/DemoStepTooltip.tsx index 5d6ecd4edd..950b40f6ff 100644 --- a/frontend/src/component/demo/DemoSteps/DemoStepTooltip/DemoStepTooltip.tsx +++ b/frontend/src/component/demo/DemoSteps/DemoStepTooltip/DemoStepTooltip.tsx @@ -63,12 +63,18 @@ const StyledTooltipTitle = styled('div')(({ theme }) => ({ const StyledTooltipActions = styled('div')(({ theme }) => ({ display: 'flex', justifyContent: 'space-between', + alignItems: 'center', marginTop: theme.spacing(3), '&&& button': { fontSize: theme.fontSizes.smallBody, }, })); +const StyledBackButton = styled(Button)({ + padding: 0, + minWidth: 0, +}); + // @ts-ignore export interface IDemoStepTooltipProps extends TooltipRenderProps { step: ITutorialTopicStep; @@ -92,7 +98,7 @@ export const DemoStepTooltip = ({ }: IDemoStepTooltipProps) => { const nextLabel = stepIndex === 0 - ? 'Start' + ? 'Start tutorial' : stepIndex === topics[topic].steps.length - 1 ? 'Finish' : 'Next'; @@ -112,28 +118,23 @@ export const DemoStepTooltip = ({ - - {topics[topic].title} - - } - /> + + {step.title || topics[topic].title} + {step.content}
0 || stepIndex > 0} + condition={ + !step.hideBackButton && stepIndex > 0 + } show={ - + } />
@@ -164,25 +165,19 @@ export const DemoStepTooltip = ({ - - {topics[topic].title} - - } - /> + + {step.title || topics[topic].title} + {step.content}
0 || stepIndex > 0} + condition={!step.hideBackButton && stepIndex > 0} show={ - + } />
diff --git a/frontend/src/component/demo/DemoSteps/DemoSteps.tsx b/frontend/src/component/demo/DemoSteps/DemoSteps.tsx index 7e51695b7a..07f59d22ba 100644 --- a/frontend/src/component/demo/DemoSteps/DemoSteps.tsx +++ b/frontend/src/component/demo/DemoSteps/DemoSteps.tsx @@ -118,6 +118,8 @@ export const DemoSteps = ({ } if (action === ACTIONS.UPDATE) { + if (step.target === 'body') return; + const el = document.querySelector( step.target as string, ) as HTMLElement | null; diff --git a/frontend/src/component/demo/demo-topics.tsx b/frontend/src/component/demo/demo-topics.tsx index 4771cb29ea..69ccb92afc 100644 --- a/frontend/src/component/demo/demo-topics.tsx +++ b/frontend/src/component/demo/demo-topics.tsx @@ -37,10 +37,10 @@ const ENVIRONMENT = 'dev'; export const TOPICS: ITutorialTopic[] = [ { - title: 'Enable/disable a feature flag', + title: 'How to enable/disable a feature flag', steps: [ { - title: 'Enable/disable a feature flag', + title: 'How to enable/disable a feature flag', href: `/projects/${PROJECT}?sort=name`, target: 'body', placement: 'center', @@ -91,11 +91,11 @@ export const TOPICS: ITutorialTopic[] = [ ], }, { - title: 'Enable for a specific user', + title: 'Next: How to enable for a specific user', setup: specificUser, steps: [ { - title: 'Enable for a specific user', + title: 'Next: How to enable for a specific user', href: `/projects/${PROJECT}?sort=name`, target: 'body', placement: 'center', @@ -335,11 +335,11 @@ export const TOPICS: ITutorialTopic[] = [ ], }, { - title: 'Adjust gradual rollout', + title: 'Next: How to adjust gradual rollout', setup: gradualRollout, steps: [ { - title: 'Adjust gradual rollout', + title: 'Next: How to adjust gradual rollout', href: `/projects/${PROJECT}?sort=name`, target: 'body', placement: 'center', @@ -468,11 +468,11 @@ export const TOPICS: ITutorialTopic[] = [ ], }, { - title: 'Adjust variants', + title: 'Next: How to adjust variants', setup: variants, steps: [ { - title: 'Adjust variants', + title: 'Next: How to adjust variants', href: `/projects/${PROJECT}?sort=name`, target: 'body', placement: 'center', diff --git a/frontend/src/component/feature/FeatureView/CleanupReminder/CleanupReminder.test.tsx b/frontend/src/component/feature/FeatureView/CleanupReminder/CleanupReminder.test.tsx index c34676bac8..3e26454534 100644 --- a/frontend/src/component/feature/FeatureView/CleanupReminder/CleanupReminder.test.tsx +++ b/frontend/src/component/feature/FeatureView/CleanupReminder/CleanupReminder.test.tsx @@ -59,6 +59,7 @@ test('render remove flag from code reminder', async () => { }); await screen.findByText('Time to remove flag from code?'); + await screen.findByText('Revert to production'); const reminder = await screen.findByText('Remind me later'); reminder.click(); diff --git a/frontend/src/component/feature/FeatureView/CleanupReminder/CleanupReminder.tsx b/frontend/src/component/feature/FeatureView/CleanupReminder/CleanupReminder.tsx index 56c3c9c835..5f7cfac9c9 100644 --- a/frontend/src/component/feature/FeatureView/CleanupReminder/CleanupReminder.tsx +++ b/frontend/src/component/feature/FeatureView/CleanupReminder/CleanupReminder.tsx @@ -18,6 +18,8 @@ import { FeatureArchiveNotAllowedDialog } from 'component/common/FeatureArchiveD import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog'; import { useNavigate } from 'react-router-dom'; import { useFlagReminders } from './useFlagReminders'; +import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +import { useUncomplete } from '../FeatureOverview/FeatureLifecycle/useUncomplete'; const StyledBox = styled(Box)(({ theme }) => ({ marginRight: theme.spacing(2), @@ -37,10 +39,16 @@ export const CleanupReminder: FC<{ onChange: () => void; }> = ({ feature, onChange }) => { const navigate = useNavigate(); + const { trackEvent } = usePlausibleTracker(); const [markCompleteDialogueOpen, setMarkCompleteDialogueOpen] = useState(false); const [archiveDialogueOpen, setArchiveDialogueOpen] = useState(false); + const { onUncompleteHandler, loading } = useUncomplete({ + feature: feature.name, + project: feature.project, + onChange, + }); const currentStage = populateCurrentStage(feature); const isRelevantType = @@ -123,7 +131,14 @@ export const CleanupReminder: FC<{ @@ -171,12 +186,31 @@ export const CleanupReminder: FC<{ severity='warning' icon={} action={ - + + + + Revert to production + + } > Time to remove flag from code? diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycle.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycle.tsx index 191a8a507b..7b932b5e88 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycle.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycle.tsx @@ -1,12 +1,11 @@ import { FeatureLifecycleStageIcon } from 'component/common/FeatureLifecycle/FeatureLifecycleStageIcon'; import { FeatureLifecycleTooltip } from './FeatureLifecycleTooltip'; -import useFeatureLifecycleApi from 'hooks/api/actions/useFeatureLifecycleApi/useFeatureLifecycleApi'; import { populateCurrentStage } from './populateCurrentStage'; import type { FC } from 'react'; import type { Lifecycle } from 'interfaces/featureToggle'; -import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { getFeatureLifecycleName } from 'component/common/FeatureLifecycle/getFeatureLifecycleName'; import { Box } from '@mui/material'; +import { useUncomplete } from './useUncomplete'; export interface LifecycleFeature { lifecycle?: Lifecycle; @@ -28,18 +27,12 @@ export const FeatureLifecycle: FC<{ expanded?: boolean; }> = ({ feature, expanded, onComplete, onUncomplete, onArchive }) => { const currentStage = populateCurrentStage(feature); - const { markFeatureUncompleted, loading } = useFeatureLifecycleApi(); - const { trackEvent } = usePlausibleTracker(); - const onUncompleteHandler = async () => { - await markFeatureUncompleted(feature.name, feature.project); - onUncomplete?.(); - trackEvent('feature-lifecycle', { - props: { - eventType: 'uncomplete', - }, - }); - }; + const { onUncompleteHandler, loading } = useUncomplete({ + feature: feature.name, + project: feature.project, + onChange: onUncomplete, + }); return currentStage ? ( ({ display: 'flex', gap: theme.spacing(0.5) })}> diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/useUncomplete.ts b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/useUncomplete.ts new file mode 100644 index 0000000000..ecbc143428 --- /dev/null +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/useUncomplete.ts @@ -0,0 +1,31 @@ +import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +import useToast from 'hooks/useToast'; +import useFeatureLifecycleApi from 'hooks/api/actions/useFeatureLifecycleApi/useFeatureLifecycleApi'; +import { formatUnknownError } from 'utils/formatUnknownError'; + +export const useUncomplete = ({ + feature, + project, + onChange, +}: { feature: string; project: string; onChange?: () => void }) => { + const { trackEvent } = usePlausibleTracker(); + const { setToastApiError } = useToast(); + const { markFeatureUncompleted, loading } = useFeatureLifecycleApi(); + + const onUncompleteHandler = async () => { + try { + await markFeatureUncompleted(feature, project); + onChange?.(); + + trackEvent('feature-lifecycle', { + props: { + eventType: 'uncomplete', + }, + }); + } catch (e) { + setToastApiError(formatUnknownError(e)); + } + }; + + return { onUncompleteHandler, loading }; +};