diff --git a/frontend/__snapshots__/scenes-app-experiments--experiment-not-found.png b/frontend/__snapshots__/scenes-app-experiments--experiment-not-found.png new file mode 100644 index 0000000000000..770fd04035f09 Binary files /dev/null and b/frontend/__snapshots__/scenes-app-experiments--experiment-not-found.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--view-experiment-pay-gate.png b/frontend/__snapshots__/scenes-app-experiments--view-experiment-pay-gate.png index beb357ac8a6a6..520840cd0f18a 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--view-experiment-pay-gate.png and b/frontend/__snapshots__/scenes-app-experiments--view-experiment-pay-gate.png differ diff --git a/frontend/__snapshots__/scenes-app-feature-flags--feature-flag-not-found.png b/frontend/__snapshots__/scenes-app-feature-flags--feature-flag-not-found.png new file mode 100644 index 0000000000000..4455d9ad6aa26 Binary files /dev/null and b/frontend/__snapshots__/scenes-app-feature-flags--feature-flag-not-found.png differ diff --git a/frontend/__snapshots__/scenes-app-features--not-found-early-access.png b/frontend/__snapshots__/scenes-app-features--not-found-early-access.png new file mode 100644 index 0000000000000..6e683eff04a96 Binary files /dev/null and b/frontend/__snapshots__/scenes-app-features--not-found-early-access.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--survey-not-found.png b/frontend/__snapshots__/scenes-app-surveys--survey-not-found.png new file mode 100644 index 0000000000000..f0a3188f10cbd Binary files /dev/null and b/frontend/__snapshots__/scenes-app-surveys--survey-not-found.png differ diff --git a/frontend/src/scenes/early-access-features/EarlyAccessFeature.tsx b/frontend/src/scenes/early-access-features/EarlyAccessFeature.tsx index ffc9851bf8530..2677d3fb1ccec 100644 --- a/frontend/src/scenes/early-access-features/EarlyAccessFeature.tsx +++ b/frontend/src/scenes/early-access-features/EarlyAccessFeature.tsx @@ -1,4 +1,4 @@ -import { LemonButton, LemonDivider, LemonInput, LemonTag, LemonTextArea } from '@posthog/lemon-ui' +import { LemonButton, LemonDivider, LemonInput, LemonSkeleton, LemonTag, LemonTextArea } from '@posthog/lemon-ui' import { useActions, useValues, BindLogic } from 'kea' import { PageHeader } from 'lib/components/PageHeader' import { Field, PureField } from 'lib/forms/Field' @@ -30,6 +30,7 @@ import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' import { PersonsSearch } from 'scenes/persons/PersonsSearch' import { LemonDialog } from 'lib/lemon-ui/LemonDialog' import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { NotFound } from 'lib/components/NotFound' export const scene: SceneExport = { component: EarlyAccessFeature, @@ -40,12 +41,26 @@ export const scene: SceneExport = { } export function EarlyAccessFeature({ id }: { id?: string } = {}): JSX.Element { - const { earlyAccessFeature, earlyAccessFeatureLoading, isEarlyAccessFeatureSubmitting, isEditingFeature } = - useValues(earlyAccessFeatureLogic) + const { + earlyAccessFeature, + earlyAccessFeatureLoading, + isEarlyAccessFeatureSubmitting, + isEditingFeature, + earlyAccessFeatureMissing, + } = useValues(earlyAccessFeatureLogic) const { submitEarlyAccessFeatureRequest, cancel, editFeature, updateStage, deleteEarlyAccessFeature } = useActions(earlyAccessFeatureLogic) const isNewEarlyAccessFeature = id === 'new' || id === undefined + + if (earlyAccessFeatureMissing) { + return + } + + if (earlyAccessFeatureLoading) { + return + } + return (
} + +export function NotFoundEarlyAccess(): JSX.Element { + useFeatureFlags([FEATURE_FLAGS.EARLY_ACCESS_FEATURE]) + useEffect(() => { + router.actions.push(urls.earlyAccessFeature('not-found')) + }, []) + return +} diff --git a/frontend/src/scenes/early-access-features/earlyAccessFeatureLogic.ts b/frontend/src/scenes/early-access-features/earlyAccessFeatureLogic.ts index b94797646a8a6..96ce8ab5eac2d 100644 --- a/frontend/src/scenes/early-access-features/earlyAccessFeatureLogic.ts +++ b/frontend/src/scenes/early-access-features/earlyAccessFeatureLogic.ts @@ -38,6 +38,7 @@ export const earlyAccessFeatureLogic = kea([ actions: [earlyAccessFeaturesLogic, ['loadEarlyAccessFeatures', 'loadEarlyAccessFeaturesSuccess']], })), actions({ + setEarlyAccessFeatureMissing: true, toggleImplementOptInInstructionsModal: true, cancel: true, editFeature: (editing: boolean) => ({ editing }), @@ -45,12 +46,17 @@ export const earlyAccessFeatureLogic = kea([ deleteEarlyAccessFeature: (earlyAccessFeatureId: EarlyAccessFeatureType['id']) => ({ earlyAccessFeatureId }), setActiveTab: (activeTab: EarlyAccessFeatureTabs) => ({ activeTab }), }), - loaders(({ props }) => ({ + loaders(({ props, actions }) => ({ earlyAccessFeature: { loadEarlyAccessFeature: async () => { if (props.id && props.id !== 'new') { - const response = await api.earlyAccessFeatures.get(props.id) - return response + try { + const response = await api.earlyAccessFeatures.get(props.id) + return response + } catch (error: any) { + actions.setEarlyAccessFeatureMissing() + throw error + } } return NEW_EARLY_ACCESS_FEATURE }, @@ -85,6 +91,12 @@ export const earlyAccessFeatureLogic = kea([ }, })), reducers({ + earlyAccessFeatureMissing: [ + false, + { + setEarlyAccessFeatureMissing: () => true, + }, + ], isEditingFeature: [ false, { diff --git a/frontend/src/scenes/experiments/Experiment.stories.tsx b/frontend/src/scenes/experiments/Experiment.stories.tsx index 2eea048444525..f4befaccc04fe 100644 --- a/frontend/src/scenes/experiments/Experiment.stories.tsx +++ b/frontend/src/scenes/experiments/Experiment.stories.tsx @@ -636,3 +636,11 @@ export function ViewExperimentPayGate(): JSX.Element { }, []) return } + +export function ExperimentNotFound(): JSX.Element { + useAvailableFeatures([AvailableFeature.EXPERIMENTATION]) + useEffect(() => { + router.actions.push(urls.experiment('1200000')) + }, []) + return +} diff --git a/frontend/src/scenes/experiments/Experiment.tsx b/frontend/src/scenes/experiments/Experiment.tsx index 0256b46967d39..2717a1f523750 100644 --- a/frontend/src/scenes/experiments/Experiment.tsx +++ b/frontend/src/scenes/experiments/Experiment.tsx @@ -64,6 +64,7 @@ export function Experiment(): JSX.Element { props, aggregationLabel, groupTypes, + experimentMissing, } = useValues(experimentLogic) const { launchExperiment, @@ -145,7 +146,7 @@ export function Experiment(): JSX.Element { return } - if (!experiment && experimentId !== 'new') { + if (experimentMissing) { return } diff --git a/frontend/src/scenes/experiments/experimentLogic.tsx b/frontend/src/scenes/experiments/experimentLogic.tsx index 1e19fd66030ca..9e9068689e1fc 100644 --- a/frontend/src/scenes/experiments/experimentLogic.tsx +++ b/frontend/src/scenes/experiments/experimentLogic.tsx @@ -104,6 +104,7 @@ export const experimentLogic = kea([ ], })), actions({ + setExperimentMissing: true, setExperiment: (experiment: Partial) => ({ experiment }), createExperiment: (draft?: boolean, runningTime?: number, sampleSize?: number) => ({ draft, @@ -198,6 +199,12 @@ export const experimentLogic = kea([ }, }, ], + experimentMissing: [ + false, + { + setExperimentMissing: () => true, + }, + ], editingExistingExperiment: [ false, { @@ -541,12 +548,8 @@ export const experimentLogic = kea([ ) return response as Experiment } catch (error: any) { - if (error.status === 404) { - throw error - } else { - lemonToast.error(`Failed to load experiment ${props.experimentId}`) - throw new Error(`Failed to load experiment ${props.experimentId}`) - } + actions.setExperimentMissing() + throw error } } return NEW_EXPERIMENT diff --git a/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx b/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx index 74805efa2f042..eb74c9fe61248 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx +++ b/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx @@ -22,6 +22,14 @@ const meta: Meta = { mswDecorator({ get: { '/api/projects/:team_id/feature_flags': featureFlags, + '/api/projects/:team_id/feature_flags/1111111111111/': [ + 404, + { + type: 'invalid', + code: 'not_found', + detail: 'Not found.', + }, + ], '/api/projects/:team_id/feature_flags/:flagId/': (req) => [ 200, featureFlags.results.find((r) => r.id === Number(req.params['flagId'])), @@ -59,3 +67,10 @@ export function EditMultiVariateFeatureFlag(): JSX.Element { }, []) return } + +export function FeatureFlagNotFound(): JSX.Element { + useEffect(() => { + router.actions.push(urls.featureFlag(1111111111111)) + }, []) + return +} diff --git a/frontend/src/scenes/surveys/Survey.tsx b/frontend/src/scenes/surveys/Survey.tsx index 954fd47db7807..052c561061726 100644 --- a/frontend/src/scenes/surveys/Survey.tsx +++ b/frontend/src/scenes/surveys/Survey.tsx @@ -36,6 +36,7 @@ import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' import { defaultSurveyFieldValues, defaultSurveyAppearance, NewSurvey, SurveyUrlMatchTypeLabels } from './constants' import { FEATURE_FLAGS } from 'lib/constants' import { FeatureFlagReleaseConditions } from 'scenes/feature-flags/FeatureFlagReleaseConditions' +import { NotFound } from 'lib/components/NotFound' export const scene: SceneExport = { component: SurveyComponent, @@ -46,8 +47,13 @@ export const scene: SceneExport = { } export function SurveyComponent({ id }: { id?: string } = {}): JSX.Element { - const { isEditingSurvey } = useValues(surveyLogic) + const { isEditingSurvey, surveyMissing } = useValues(surveyLogic) const showSurveyForm = id === 'new' || isEditingSurvey + + if (surveyMissing) { + return + } + return (
{!id ? ( diff --git a/frontend/src/scenes/surveys/Surveys.stories.tsx b/frontend/src/scenes/surveys/Surveys.stories.tsx index 7bd9755f5b841..fd8c833477348 100644 --- a/frontend/src/scenes/surveys/Surveys.stories.tsx +++ b/frontend/src/scenes/surveys/Surveys.stories.tsx @@ -206,3 +206,11 @@ export function SurveyView(): JSX.Element { }, []) return } + +export function SurveyNotFound(): JSX.Element { + useFeatureFlags([FEATURE_FLAGS.SURVEYS]) + useEffect(() => { + router.actions.push(urls.survey('1234566789')) + }, []) + return +} diff --git a/frontend/src/scenes/surveys/surveyLogic.tsx b/frontend/src/scenes/surveys/surveyLogic.tsx index a8f745bc03893..a6594783aae0f 100644 --- a/frontend/src/scenes/surveys/surveyLogic.tsx +++ b/frontend/src/scenes/surveys/surveyLogic.tsx @@ -70,6 +70,7 @@ export const surveyLogic = kea([ ], })), actions({ + setSurveyMissing: true, editingSurvey: (editing: boolean) => ({ editing }), setDefaultForQuestionType: ( idx: number, @@ -84,19 +85,21 @@ export const surveyLogic = kea([ isEditingDescription, isEditingThankYouMessage, }), - launchSurvey: true, - stopSurvey: true, archiveSurvey: true, - resumeSurvey: true, setCurrentQuestionIndexAndType: (idx: number, type: SurveyQuestionType) => ({ idx, type }), }), loaders(({ props, actions }) => ({ survey: { loadSurvey: async () => { if (props.id && props.id !== 'new') { - const survey = await api.surveys.get(props.id) - actions.reportSurveyViewed(survey) - return survey + try { + const survey = await api.surveys.get(props.id) + actions.reportSurveyViewed(survey) + return survey + } catch (error: any) { + actions.setSurveyMissing() + throw error + } } return { ...NEW_SURVEY } }, @@ -158,6 +161,12 @@ export const surveyLogic = kea([ editingSurvey: (_, { editing }) => editing, }, ], + surveyMissing: [ + false, + { + setSurveyMissing: () => true, + }, + ], survey: [ { ...NEW_SURVEY } as NewSurvey | Survey, {