- {allFilters.display === ChartDisplayType.FunnelViz && (
+ {allFilters.funnel_viz_type === FunnelVizType.Steps && (
<>
@@ -43,10 +43,10 @@ export function FunnelCanvasLabel(): JSX.Element | null {
setChartFilter(FunnelVizType.TimeToConvert)}
disabled={
- !clickhouseFeaturesEnabled || allFilters.display === ChartDisplayType.FunnelsTimeToConvert
+ !clickhouseFeaturesEnabled || allFilters.funnel_viz_type === FunnelVizType.TimeToConvert
}
- onClick={() => setChartFilter(ChartDisplayType.FunnelsTimeToConvert)}
>
{humanFriendlyDuration(conversionMetrics.averageTime)}
diff --git a/frontend/src/scenes/funnels/FunnelHistogram.tsx b/frontend/src/scenes/funnels/FunnelHistogram.tsx
index d7611babd156c..c380d848ef4b7 100644
--- a/frontend/src/scenes/funnels/FunnelHistogram.tsx
+++ b/frontend/src/scenes/funnels/FunnelHistogram.tsx
@@ -7,9 +7,9 @@ import { calcPercentage, getReferenceStep } from './funnelUtils'
import { funnelLogic } from './funnelLogic'
import { Histogram } from 'scenes/insights/Histogram'
import { insightLogic } from 'scenes/insights/insightLogic'
-import { ChartDisplayType } from '~/types'
import './FunnelHistogram.scss'
+import { FunnelVizType } from '~/types'
import { ChartParams } from '~/types'
export function FunnelHistogramHeader(): JSX.Element | null {
@@ -17,7 +17,7 @@ export function FunnelHistogramHeader(): JSX.Element | null {
const { changeHistogramStep } = useActions(funnelLogic)
const { allFilters } = useValues(insightLogic)
- if (allFilters.display !== ChartDisplayType.FunnelsTimeToConvert) {
+ if (allFilters.funnel_viz_type !== FunnelVizType.TimeToConvert) {
return null
}
diff --git a/frontend/src/scenes/funnels/FunnelViz.tsx b/frontend/src/scenes/funnels/FunnelViz.tsx
index bda5d473decf7..ae71d553e3ee4 100644
--- a/frontend/src/scenes/funnels/FunnelViz.tsx
+++ b/frontend/src/scenes/funnels/FunnelViz.tsx
@@ -4,15 +4,15 @@ import FunnelGraph from 'funnel-graph-js'
import { Loading, humanFriendlyDuration } from 'lib/utils'
import { useActions, useValues, BindLogic } from 'kea'
import { funnelLogic } from './funnelLogic'
-import { ACTIONS_LINE_GRAPH_LINEAR } from 'lib/constants'
import { LineGraph } from 'scenes/insights/LineGraph'
import { router } from 'kea-router'
-import { InputNumber } from 'antd'
+import { InputNumber, Row } from 'antd'
import { preflightLogic } from 'scenes/PreflightCheck/logic'
-import { ChartParams } from '~/types'
+import { ChartParams, FunnelVizType } from '~/types'
import { FunnelEmptyState } from 'scenes/insights/EmptyStates'
import './FunnelViz.scss'
+import { personsModalLogic } from 'scenes/trends/personsModalLogic'
export function FunnelViz({
filters: defaultFilters,
@@ -32,6 +32,7 @@ export function FunnelViz({
areFiltersValid,
} = useValues(logic)
const { loadResults: loadFunnel, loadConversionWindow } = useActions(logic)
+ const { loadPeople } = useActions(personsModalLogic)
const {
hashParams: { fromItem },
} = useValues(router)
@@ -40,7 +41,7 @@ export function FunnelViz({
function buildChart(): void {
// Build and mount graph for default "flow" visualization.
// If steps are empty, new bargraph view is active, or linechart is visible, don't render flow graph.
- if (!steps || steps.length === 0 || filters.display === ACTIONS_LINE_GRAPH_LINEAR) {
+ if (!steps || steps.length === 0 || filters.funnel_viz_type === FunnelVizType.Trends) {
return
}
if (container.current) {
@@ -103,10 +104,10 @@ export function FunnelViz({
)
}
- if (filters.display === ACTIONS_LINE_GRAPH_LINEAR) {
+ if (filters.funnel_viz_type === FunnelVizType.Trends) {
return steps && steps.length > 0 && steps[0].labels ? (
<>
-
+
{preflight?.is_clickhouse_enabled && (
<>
converted within
@@ -121,7 +122,7 @@ export function FunnelViz({
>
)}
% converted from first to last step
-
+
{
+ loadPeople({
+ action: { id: point.index, name: point.label, properties: [], type: 'actions' },
+ label: `Persons converted on ${point.label}`,
+ date_from: point.day,
+ date_to: point.day,
+ filters: filters,
+ saveOriginal: true,
+ })
+ }
+ }
/>
>
) : null
diff --git a/frontend/src/scenes/funnels/funnelLogic.ts b/frontend/src/scenes/funnels/funnelLogic.ts
index efc35cdcc7fa3..62aad35f72d66 100644
--- a/frontend/src/scenes/funnels/funnelLogic.ts
+++ b/frontend/src/scenes/funnels/funnelLogic.ts
@@ -10,7 +10,7 @@ import { funnelLogicType } from './funnelLogicType'
import {
EntityTypes,
FilterType,
- ChartDisplayType,
+ FunnelVizType,
FunnelResult,
FunnelStep,
FunnelsTimeConversionBins,
@@ -96,6 +96,8 @@ export const cleanFunnelParams = (filters: Partial): FilterType => {
...(filters.funnel_step ? { funnel_step: filters.funnel_step } : {}),
...(filters.funnel_viz_type ? { funnel_viz_type: filters.funnel_viz_type } : {}),
...(filters.funnel_step ? { funnel_to_step: filters.funnel_step } : {}),
+ ...(filters.entrance_period_start ? { entrance_period_start: filters.entrance_period_start } : {}),
+ ...(filters.drop_off ? { drop_off: filters.drop_off } : {}),
interval: autocorrectInterval(filters),
breakdown: filters.breakdown || undefined,
breakdown_type: filters.breakdown_type || undefined,
@@ -178,7 +180,7 @@ export const funnelLogic = kea({
}
async function loadBinsResults(): Promise {
- if (filters.display === ChartDisplayType.FunnelsTimeToConvert) {
+ if (filters.funnel_viz_type === FunnelVizType.TimeToConvert) {
try {
// API specs (#5110) require neither funnel_{from|to}_step to be provided if querying
// for all steps
@@ -187,7 +189,6 @@ export const funnelLogic = kea({
const binsResult = await pollFunnel({
...apiParams,
...(refresh ? { refresh } : {}),
- funnel_viz_type: 'time_to_convert',
...(!isAllSteps ? { funnel_from_step: histogramStep.from_step } : {}),
...(!isAllSteps ? { funnel_to_step: histogramStep.to_step } : {}),
})
@@ -307,8 +308,8 @@ export const funnelLogic = kea({
],
showBarGraph: [
() => [selectors.filters],
- ({ display }: { display: ChartDisplayType }) =>
- display === ChartDisplayType.FunnelViz || display === ChartDisplayType.FunnelsTimeToConvert,
+ ({ funnel_viz_type }: { funnel_viz_type: FunnelVizType }) =>
+ funnel_viz_type === FunnelVizType.Steps || funnel_viz_type === FunnelVizType.TimeToConvert,
],
clickhouseFeaturesEnabled: [
() => [featureFlagLogic.selectors.featureFlags, selectors.preflight],
@@ -426,10 +427,14 @@ export const funnelLogic = kea({
],
steps: [
() => [selectors.results, selectors.stepsWithNestedBreakdown, selectors.filters],
- (results, stepsWithNestedBreakdown, filters): FunnelStepWithNestedBreakdown[] =>
- !!filters.breakdown
+ (results, stepsWithNestedBreakdown, filters): FunnelStepWithNestedBreakdown[] => {
+ if (!Array.isArray(results)) {
+ return []
+ }
+ return !!filters.breakdown
? stepsWithNestedBreakdown
- : ([...results] as FunnelStep[]).sort((a, b) => a.order - b.order),
+ : ([...results] as FunnelStep[]).sort((a, b) => a.order - b.order)
+ },
],
stepsWithCount: [() => [selectors.steps], (steps) => steps.filter((step) => typeof step.count === 'number')],
}),
diff --git a/frontend/src/scenes/funnels/funnelUtils.ts b/frontend/src/scenes/funnels/funnelUtils.ts
index 9f28a4c033836..111f97c2a4f41 100644
--- a/frontend/src/scenes/funnels/funnelUtils.ts
+++ b/frontend/src/scenes/funnels/funnelUtils.ts
@@ -71,7 +71,7 @@ export function getSeriesPositionName(
}
export function humanizeStepCount(count: number): string {
- return count > 9999 ? humanizeNumber(count, 2) : count.toLocaleString()
+ return count > 9999 ? humanizeNumber(count, 2) : count?.toLocaleString()
}
export function cleanBinResult(binsResult: FunnelsTimeConversionBins): FunnelsTimeConversionBins {
diff --git a/frontend/src/scenes/insights/InsightTabs/FunnelTab/ToggleButtonChartFilter.tsx b/frontend/src/scenes/insights/InsightTabs/FunnelTab/ToggleButtonChartFilter.tsx
index c7b20d3b10139..f2bc3d5cbe90e 100644
--- a/frontend/src/scenes/insights/InsightTabs/FunnelTab/ToggleButtonChartFilter.tsx
+++ b/frontend/src/scenes/insights/InsightTabs/FunnelTab/ToggleButtonChartFilter.tsx
@@ -1,12 +1,12 @@
import React from 'react'
import { useActions, useValues } from 'kea'
import { Radio, Tooltip } from 'antd'
-import { ChartDisplayType } from '~/types'
+import { FunnelVizType } from '~/types'
import { chartFilterLogic } from 'lib/components/ChartFilter/chartFilterLogic'
import { funnelLogic } from 'scenes/funnels/funnelLogic'
interface ToggleButtonChartFilterProps {
- onChange?: (chartFilter: ChartDisplayType) => void
+ onChange?: (chartFilter: FunnelVizType) => void
disabled?: boolean
}
@@ -19,21 +19,21 @@ export function ToggleButtonChartFilter({
const { clickhouseFeaturesEnabled } = useValues(funnelLogic())
const { chartFilter } = useValues(chartFilterLogic)
const { setChartFilter } = useActions(chartFilterLogic)
- const defaultDisplay = ChartDisplayType.FunnelViz
+ const defaultDisplay = FunnelVizType.Steps
const options = [
{
- value: ChartDisplayType.FunnelViz,
+ value: FunnelVizType.Steps,
label: Conversion steps ,
visible: true,
},
{
- value: ChartDisplayType.FunnelsTimeToConvert,
+ value: FunnelVizType.TimeToConvert,
label: Time to convert ,
visible: clickhouseFeaturesEnabled,
},
{
- value: ChartDisplayType.ActionsLineGraphLinear,
+ value: FunnelVizType.Trends,
label: Historical ,
visible: true,
},
@@ -44,7 +44,7 @@ export function ToggleButtonChartFilter({
key="2"
defaultValue={defaultDisplay}
value={chartFilter || defaultDisplay}
- onChange={({ target: { value } }: { target: { value?: ChartDisplayType } }) => {
+ onChange={({ target: { value } }: { target: { value?: FunnelVizType } }) => {
if (value) {
setChartFilter(value)
onChange(value)
diff --git a/frontend/src/scenes/insights/InsightTabs/InsightDisplayConfig.tsx b/frontend/src/scenes/insights/InsightTabs/InsightDisplayConfig.tsx
index c62422df5c7f8..b68e7ede9b52d 100644
--- a/frontend/src/scenes/insights/InsightTabs/InsightDisplayConfig.tsx
+++ b/frontend/src/scenes/insights/InsightTabs/InsightDisplayConfig.tsx
@@ -2,15 +2,9 @@ import { ChartFilter } from 'lib/components/ChartFilter'
import { CompareFilter } from 'lib/components/CompareFilter/CompareFilter'
import { IntervalFilter } from 'lib/components/IntervalFilter'
import { TZIndicator } from 'lib/components/TimezoneAware'
-import {
- ACTIONS_BAR_CHART_VALUE,
- ACTIONS_LINE_GRAPH_LINEAR,
- ACTIONS_PIE_CHART,
- ACTIONS_TABLE,
- FEATURE_FLAGS,
-} from 'lib/constants'
+import { ACTIONS_BAR_CHART_VALUE, ACTIONS_PIE_CHART, ACTIONS_TABLE, FEATURE_FLAGS } from 'lib/constants'
import React from 'react'
-import { ChartDisplayType, FilterType, ViewType } from '~/types'
+import { ChartDisplayType, FilterType, FunnelVizType, ViewType } from '~/types'
import { CalendarOutlined } from '@ant-design/icons'
import { InsightDateFilter } from '../InsightDateFilter'
import { RetentionDatePicker } from '../RetentionDatePicker'
@@ -30,7 +24,7 @@ interface InsightDisplayConfigProps {
const showIntervalFilter = function (activeView: ViewType, filter: FilterType): boolean {
switch (activeView) {
case ViewType.FUNNELS:
- return filter.display === ACTIONS_LINE_GRAPH_LINEAR
+ return filter.funnel_viz_type === FunnelVizType.Trends
case ViewType.RETENTION:
case ViewType.PATHS:
return false
@@ -102,7 +96,7 @@ export function InsightDisplayConfig({
{showChartFilter(activeView) && (
{
+ onChange={(display: ChartDisplayType | FunnelVizType) => {
if (display === ACTIONS_TABLE || display === ACTIONS_PIE_CHART) {
clearAnnotationsToCreate()
}
@@ -115,7 +109,7 @@ export function InsightDisplayConfig({
{activeView === ViewType.RETENTION && }
- {showFunnelBarOptions && allFilters.display !== ChartDisplayType.FunnelsTimeToConvert && (
+ {showFunnelBarOptions && allFilters.funnel_viz_type === FunnelVizType.Steps && (
<>
diff --git a/frontend/src/scenes/insights/Insights.tsx b/frontend/src/scenes/insights/Insights.tsx
index 76f2fb7a9a7eb..ee847458919bd 100644
--- a/frontend/src/scenes/insights/Insights.tsx
+++ b/frontend/src/scenes/insights/Insights.tsx
@@ -6,13 +6,7 @@ import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { Tabs, Row, Col, Card, Button, Tooltip } from 'antd'
-import {
- FUNNEL_VIZ,
- ACTIONS_TABLE,
- ACTIONS_BAR_CHART_VALUE,
- FEATURE_FLAGS,
- ACTIONS_LINE_GRAPH_LINEAR,
-} from 'lib/constants'
+import { FUNNEL_VIZ, ACTIONS_TABLE, ACTIONS_BAR_CHART_VALUE, FEATURE_FLAGS } from 'lib/constants'
import { annotationsLogic } from '~/lib/components/Annotations'
import { router } from 'kea-router'
@@ -35,7 +29,7 @@ import { People } from 'scenes/funnels/People'
import { InsightsTable } from './InsightsTable'
import { TrendInsight } from 'scenes/trends/Trends'
import { trendsLogic } from 'scenes/trends/trendsLogic'
-import { HotKeys, ViewType } from '~/types'
+import { FunnelVizType, HotKeys, ViewType } from '~/types'
import { useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotkeys'
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import { InsightDisplayConfig } from './InsightTabs/InsightDisplayConfig'
@@ -434,8 +428,8 @@ function FunnelInsight(): JSX.Element {
const {
isValidFunnel,
isLoading,
+ filters: { funnel_viz_type },
areFiltersValid,
- filters: { display },
showBarGraph,
} = useValues(funnelLogic({}))
const { clickhouseFeaturesEnabled } = useValues(funnelLogic)
@@ -448,15 +442,15 @@ function FunnelInsight(): JSX.Element {
'non-empty-state':
isValidFunnel &&
areFiltersValid &&
- (!featureFlags[FEATURE_FLAGS.FUNNEL_BAR_VIZ] || display === ACTIONS_LINE_GRAPH_LINEAR),
+ (!featureFlags[FEATURE_FLAGS.FUNNEL_BAR_VIZ] || funnel_viz_type === FunnelVizType.Trends),
})}
>
{isLoading && }
{isValidFunnel ? (
featureFlags[FEATURE_FLAGS.FUNNEL_BAR_VIZ] && showBarGraph ? (
-
+
) : (
-
+
)
) : (
!isLoading && (
diff --git a/frontend/src/scenes/insights/LineGraph/LineGraph.js b/frontend/src/scenes/insights/LineGraph/LineGraph.js
index 1d3f4c7b89a9f..0e583fbeaa7f3 100644
--- a/frontend/src/scenes/insights/LineGraph/LineGraph.js
+++ b/frontend/src/scenes/insights/LineGraph/LineGraph.js
@@ -262,7 +262,6 @@ export function LineGraph({
// This could either be a color or an array of colors (`horizontalBar`)
const colorSet = entityData.backgroundColor || entityData.borderColor
-
return (
{
return localStorage.getItem('default_filter_test_accounts') === 'true' || false
}
+interface UrlParams {
+ insight: string
+ properties: PropertyFilter[] | undefined
+ filter_test_accounts: boolean
+ funnel_viz_type?: string
+ display?: string
+}
+
export const logicFromInsight = (insight: string, logicProps: Record): Logic & BuiltLogic => {
if (insight === ViewType.FUNNELS) {
return funnelLogic(logicProps)
@@ -228,11 +236,16 @@ export const insightLogic = kea({
return cachedUrl + '&' + toParams({ properties })
}
- const urlParams = {
+ const urlParams: UrlParams = {
insight: type,
properties: values.allFilters.properties,
filter_test_accounts: defaultFilterTestAccounts(),
}
+
+ if (type === ViewType.FUNNELS) {
+ urlParams.funnel_viz_type = FunnelVizType.Steps
+ urlParams.display = 'FunnelViz'
+ }
return ['/insights', urlParams]
},
}),
diff --git a/frontend/src/scenes/trends/personsModalLogic.ts b/frontend/src/scenes/trends/personsModalLogic.ts
index 5534c25a99538..339ab6b2a6097 100644
--- a/frontend/src/scenes/trends/personsModalLogic.ts
+++ b/frontend/src/scenes/trends/personsModalLogic.ts
@@ -1,9 +1,10 @@
+import dayjs from 'dayjs'
import { kea } from 'kea'
import api from 'lib/api'
import { errorToast, toParams } from 'lib/utils'
-import { cleanFunnelParams, funnelLogic } from 'scenes/funnels/funnelLogic'
+import { cleanFunnelParams } from 'scenes/funnels/funnelLogic'
import { cohortLogic } from 'scenes/persons/cohortLogic'
-import { ActionFilter, FilterType, ViewType } from '~/types'
+import { ActionFilter, FilterType, FunnelVizType, ViewType } from '~/types'
import { personsModalLogicType } from './personsModalLogicType'
import { parsePeopleParams, TrendPeople } from './trendsLogic'
@@ -146,8 +147,16 @@ export const personsModalLogic = kea>({
const filterParams = parsePeopleParams({ label, action, date_from, date_to, breakdown_value }, filters)
actions.setPeople(tempPeople)
people = await api.get(`api/person/stickiness/?${filterParams}${searchTermParam}`)
- } else if (funnelStep) {
- const params = { ...funnelLogic().values.filters, funnel_step: funnelStep }
+ } else if (funnelStep || filters.funnel_viz_type === FunnelVizType.Trends) {
+ let params
+ if (filters.funnel_viz_type === FunnelVizType.Trends) {
+ // funnel trends
+ const entrance_period_start = dayjs(date_from).format('YYYY-MM-DD HH:mm:ss')
+ params = { ...filters, entrance_period_start, drop_off: false }
+ } else {
+ // regular funnel steps
+ params = { ...filters, funnel_step: funnelStep }
+ }
const cleanedParams = cleanFunnelParams(params)
const funnelParams = toParams(cleanedParams)
people = await api.create(`api/person/funnel/?${funnelParams}${searchTermParam}`)
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index 13f94ce773321..43d7a012aceef 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -556,7 +556,6 @@ export enum ChartDisplayType {
ActionsBarChartValue = 'ActionsBarValue',
PathsViz = 'PathsViz',
FunnelViz = 'FunnelViz',
- FunnelsTimeToConvert = 'FunnelsTimeToConvert',
}
export type ShownAsType = ShownAsValue // DEPRECATED: Remove when releasing `remove-shownas`
@@ -584,6 +583,12 @@ export enum PathType {
CustomEvent = 'custom_event',
}
+export enum FunnelVizType {
+ Steps = 'steps',
+ TimeToConvert = 'time_to_convert',
+ Trends = 'trends',
+}
+
export type RetentionType = typeof RETENTION_RECURRING | typeof RETENTION_FIRST_TIME
export interface FilterType {
@@ -617,6 +622,8 @@ export interface FilterType {
filter_test_accounts?: boolean
from_dashboard?: boolean
funnel_step?: number
+ entrance_period_start?: string // this and drop_off is used for funnels time conversion date for the persons modal
+ drop_off?: boolean
funnel_viz_type?: string // parameter sent to funnels API for time conversion code path
funnel_from_step?: number // used in time to convert: initial step index to compute time to convert
funnel_to_step?: number // used in time to convert: ending step index to compute time to convert