From c3adb7378dfe87436de6bbf295630bee36515ec4 Mon Sep 17 00:00:00 2001 From: Juraj Majerik Date: Sat, 28 Dec 2024 22:10:19 +0100 Subject: [PATCH] wip --- .../ExperimentView/ExperimentView.tsx | 28 ++++- .../experiments/ExperimentView/Overview.tsx | 1 - .../experiments/ExperimentView/Results.tsx | 2 +- .../experiments/ExperimentView/components.tsx | 101 ++++-------------- .../Metrics/SecondaryMetricChartModal.tsx | 2 +- .../experiments/MetricsView/DeltaChart.tsx | 72 ++++++++++--- .../experiments/MetricsView/MetricsView.tsx | 49 ++++++++- .../Nodes/NotebookNodeExperiment.tsx | 2 +- 8 files changed, 156 insertions(+), 101 deletions(-) diff --git a/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx b/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx index c884d555f0ce0..06c1ba1707b22 100644 --- a/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx +++ b/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx @@ -12,10 +12,12 @@ import { SavedMetricModal } from '../Metrics/SavedMetricModal' import { MetricsView } from '../MetricsView/MetricsView' import { ExperimentLoadingAnimation, + ExploreButton, LoadingState, NoResultsEmptyState, PageHeaderCustom, ResultsHeader, + ResultsQuery, } from './components' import { CumulativeExposuresChart } from './CumulativeExposuresChart' import { DataCollection } from './DataCollection' @@ -26,11 +28,14 @@ import { Overview } from './Overview' import { ReleaseConditionsModal, ReleaseConditionsTable } from './ReleaseConditionsTable' import { Results } from './Results' import { SecondaryMetricsTable } from './SecondaryMetricsTable' +import { SummaryTable } from './SummaryTable' const NewResultsTab = (): JSX.Element => { const { experiment, metricResults } = useValues(experimentLogic) const hasSomeResults = metricResults?.some((result) => result?.insight) + const hasSinglePrimaryMetric = experiment.metrics.length === 1 + return ( <> {!hasSomeResults && ( @@ -42,7 +47,27 @@ const NewResultsTab = (): JSX.Element => { )} )} - + {/* Show overview if there's only a single primary metric */} + {hasSinglePrimaryMetric && ( +
+ +
+ )} + + {/* Show detailed results if there's only a single primary metric */} + {hasSomeResults && hasSinglePrimaryMetric && ( +
+
+ +
+
+ +
+
+ +
+
+ )} ) @@ -124,6 +149,7 @@ export function ExperimentView(): JSX.Element { <> {hasSomeResults && !featureFlags[FEATURE_FLAGS.EXPERIMENTS_MULTIPLE_METRICS] ? (
+

Summary

diff --git a/frontend/src/scenes/experiments/ExperimentView/Overview.tsx b/frontend/src/scenes/experiments/ExperimentView/Overview.tsx index 351e58e946646..a6765a64f3f9f 100644 --- a/frontend/src/scenes/experiments/ExperimentView/Overview.tsx +++ b/frontend/src/scenes/experiments/ExperimentView/Overview.tsx @@ -71,7 +71,6 @@ export function Overview({ metricIndex = 0 }: { metricIndex?: number }): JSX.Ele return (
-

Summary

diff --git a/frontend/src/scenes/experiments/ExperimentView/Results.tsx b/frontend/src/scenes/experiments/ExperimentView/Results.tsx index b0b291554ef90..b031ae5bdf594 100644 --- a/frontend/src/scenes/experiments/ExperimentView/Results.tsx +++ b/frontend/src/scenes/experiments/ExperimentView/Results.tsx @@ -15,7 +15,7 @@ export function Results(): JSX.Element {
- +
) } diff --git a/frontend/src/scenes/experiments/ExperimentView/components.tsx b/frontend/src/scenes/experiments/ExperimentView/components.tsx index 1cd2158dbadd4..b52df2d7207a8 100644 --- a/frontend/src/scenes/experiments/ExperimentView/components.tsx +++ b/frontend/src/scenes/experiments/ExperimentView/components.tsx @@ -20,35 +20,23 @@ import { FEATURE_FLAGS } from 'lib/constants' import { dayjs } from 'lib/dayjs' import { IconAreaChart } from 'lib/lemon-ui/icons' import { More } from 'lib/lemon-ui/LemonButton/More' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { useEffect, useState } from 'react' import { urls } from 'scenes/urls' import { groupsModel } from '~/models/groupsModel' -import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' -import { queryFromFilters } from '~/queries/nodes/InsightViz/utils' import { Query } from '~/queries/Query/Query' import { - CachedExperimentFunnelsQueryResponse, - CachedExperimentTrendsQueryResponse, ExperimentFunnelsQueryResponse, ExperimentTrendsQueryResponse, InsightQueryNode, InsightVizNode, NodeKind, } from '~/queries/schema' -import { - Experiment, - Experiment as ExperimentType, - ExperimentIdType, - ExperimentResults, - InsightShortId, - InsightType, -} from '~/types' +import { Experiment, Experiment as ExperimentType, ExperimentIdType, InsightShortId, InsightType } from '~/types' import { experimentLogic } from '../experimentLogic' import { getExperimentStatus, getExperimentStatusColor } from '../experimentsLogic' -import { getExperimentInsightColour, transformResultFilters } from '../utils' +import { getExperimentInsightColour } from '../utils' export function VariantTag({ experimentId, @@ -128,79 +116,39 @@ export function ResultsTag({ metricIndex = 0 }: { metricIndex?: number }): JSX.E } export function ResultsQuery({ - targetResults, + result, showTable, }: { - targetResults: ExperimentResults['result'] | ExperimentTrendsQueryResponse | ExperimentFunnelsQueryResponse | null + result: ExperimentTrendsQueryResponse | ExperimentFunnelsQueryResponse | null showTable: boolean }): JSX.Element { - const { featureFlags } = useValues(featureFlagLogic) - if (featureFlags[FEATURE_FLAGS.EXPERIMENTS_HOGQL]) { - const newQueryResults = targetResults as unknown as - | CachedExperimentTrendsQueryResponse - | CachedExperimentFunnelsQueryResponse - - const query = - newQueryResults.kind === NodeKind.ExperimentTrendsQuery - ? newQueryResults.count_query - : newQueryResults.funnels_query - const fakeInsightId = Math.random().toString(36).substring(2, 15) - - return ( - - ) - } - - const oldQueryResults = targetResults as ExperimentResults['result'] - - if (!oldQueryResults?.filters) { + if (!result) { return <> } + const query = result.kind === NodeKind.ExperimentTrendsQuery ? result.count_query : result.funnels_query + const fakeInsightId = Math.random().toString(36).substring(2, 15) + return ( } @@ -234,7 +179,7 @@ export function ExploreButton({ return ( } to={urls.insightNew(undefined, undefined, query)} @@ -260,7 +205,7 @@ export function ResultsHeader(): JSX.Element {
-
{result && }
+
{result && }
) @@ -586,7 +531,7 @@ export function ShipVariantModal({ experimentId }: { experimentId: Experiment['i const [selectedVariantKey, setSelectedVariantKey] = useState() useEffect(() => { - if (experiment.parameters?.feature_flag_variants?.length > 0) { + if (experiment.parameters?.feature_flag_variants?.length > 1) { // First test variant selected by default setSelectedVariantKey(experiment.parameters.feature_flag_variants[1].key) } diff --git a/frontend/src/scenes/experiments/Metrics/SecondaryMetricChartModal.tsx b/frontend/src/scenes/experiments/Metrics/SecondaryMetricChartModal.tsx index ec540aa43c056..184137bb59be6 100644 --- a/frontend/src/scenes/experiments/Metrics/SecondaryMetricChartModal.tsx +++ b/frontend/src/scenes/experiments/Metrics/SecondaryMetricChartModal.tsx @@ -32,7 +32,7 @@ export function SecondaryMetricChartModal({ } > - + ) } diff --git a/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx b/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx index 92be36e7f8331..88d24827fa63a 100644 --- a/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx +++ b/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx @@ -1,4 +1,12 @@ -import { IconActivity, IconGraph, IconMinus, IconPencil, IconTrending } from '@posthog/icons' +import { + IconActivity, + IconArrowRight, + IconFunnels, + IconGraph, + IconMinus, + IconPencil, + IconTrending, +} from '@posthog/icons' import { LemonBanner, LemonButton, LemonModal, LemonTag, LemonTagType, Tooltip } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { LemonProgress } from 'lib/lemon-ui/LemonProgress' @@ -35,6 +43,34 @@ function formatTickValue(value: number): string { return `${(value * 100).toFixed(decimals)}%` } +const getMetricTitle = (metric: any, metricType: InsightType): JSX.Element => { + if (metric.name) { + return {metric.name} + } + + if (metricType === InsightType.TRENDS && metric.count_query?.series?.[0]?.name) { + return {metric.count_query.series[0].name} + } + + if (metricType === InsightType.FUNNELS && metric.funnels_query?.series) { + const series = metric.funnels_query.series + if (series.length > 0) { + const firstStep = series[0]?.name + const lastStep = series[series.length - 1]?.name + + return ( + + + {firstStep} + + {lastStep} + + ) + } + } + + return Untitled metric +} export function DeltaChart({ isSecondary, @@ -183,9 +219,9 @@ export function DeltaChart({
-
- {metricIndex + 1}.{' '} - {metric.name || Untitled metric} +
+ {metricIndex + 1}. + {getMetricTitle(metric, metricType)}
-
- } - onClick={() => setIsModalOpen(true)} - > - Detailed results - -
+ {experiment.metrics.length > 1 && ( +
+ } + onClick={() => setIsModalOpen(true)} + > + Detailed results + +
+ )}
)}
@@ -789,7 +827,7 @@ export function DeltaChart({ } >
- +
@@ -798,7 +836,7 @@ export function DeltaChart({
- +
) diff --git a/frontend/src/scenes/experiments/MetricsView/MetricsView.tsx b/frontend/src/scenes/experiments/MetricsView/MetricsView.tsx index 9a362d9961974..2b5735778e1c3 100644 --- a/frontend/src/scenes/experiments/MetricsView/MetricsView.tsx +++ b/frontend/src/scenes/experiments/MetricsView/MetricsView.tsx @@ -1,5 +1,5 @@ import { IconInfo, IconPlus } from '@posthog/icons' -import { LemonButton, Tooltip } from '@posthog/lemon-ui' +import { LemonButton, LemonDivider, Tooltip } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { IconAreaChart } from 'lib/lemon-ui/icons' @@ -101,6 +101,7 @@ export function MetricsView({ isSecondary }: { isSecondary?: boolean }): JSX.Ele const variants = experiment.parameters.feature_flag_variants const results = isSecondary ? secondaryMetricResults : metricResults const errors = isSecondary ? secondaryMetricsResultErrors : primaryMetricsResultErrors + const hasSomeResults = results?.some((result) => result?.insight) let metrics = isSecondary ? experiment.metrics_secondary : experiment.metrics const savedMetrics = experiment.saved_metrics @@ -155,6 +156,52 @@ export function MetricsView({ isSecondary }: { isSecondary?: boolean }): JSX.Ele )} + {hasSomeResults && !isSecondary && ( + <> + + +

+ Each bar shows how a variant is performing compared to the control (the + gray bar) for this metric, using a{' '} + 95% credible interval. That means there's a 95% chance + the true difference for that variant falls within this range. The + vertical "0%" line is your baseline: +

+
    +
  • + To the right (green): The metric is higher (an + improvement). +
  • +
  • + To the left (red): The metric is lower (a + decrease). +
  • +
+

+ The width of the bar represents uncertainty. A{' '} + narrower bar means we're more confident in that result, + while a wider bar means it could shift either way. +

+

+ The control (baseline) is always shown in gray. Other bars will be green + or red—or even a mix—depending on whether the change is positive or + negative. +

+ How to read metrics +
+ } + > + How to read + + + )} diff --git a/frontend/src/scenes/notebooks/Nodes/NotebookNodeExperiment.tsx b/frontend/src/scenes/notebooks/Nodes/NotebookNodeExperiment.tsx index c87e65370cd23..eb980c068129e 100644 --- a/frontend/src/scenes/notebooks/Nodes/NotebookNodeExperiment.tsx +++ b/frontend/src/scenes/notebooks/Nodes/NotebookNodeExperiment.tsx @@ -82,7 +82,7 @@ const Component = ({ attributes }: NotebookNodeProps
- +
)}