Files
zeekay 159e825022 merge: take upstream master fa1a9a60 onto the converged tree
The five days of upstream evolution since the 2026-08-01 import land through
bin/debrand: new query schema, billing counters, ducklake compaction, the
desktop sandbox default, capture evolution. Ours' invariants hold everywhere
they met it: IAM-native auth only, no ee, no upsell surfaces, one sessions
model, org-is-the-tenant, v1-first serving, fail-closed capture. Crates and
workflows the fork retired stay retired; the Lemon prefix finishes dissolving
into elements.
2026-08-06 05:33:45 -07:00

1138 lines
47 KiB
TypeScript

import { MakeLogicType, actions, connect, kea, path, reducers, selectors } from 'kea'
import { router, urlToAction } from 'kea-router'
import { FunnelLayout } from 'lib/constants'
import { trackedActionToUrl } from 'lib/logic/scenes/trackedActionToUrl'
import { getDefaultInterval } from 'lib/utils/dateFilters'
import { capitalizeFirstLetter, wordPluralize } from 'lib/utils/strings'
import { sceneConfigurations } from 'scenes/scenes'
import { Scene } from 'scenes/sceneTypes'
import { urls } from 'scenes/urls'
import { groupsModel } from '~/models/groupsModel'
import {
ActionsNode,
AnyEntityNode,
EventsNode,
InsightVizNode,
LifecycleDataWarehouseNode,
FunnelsDataWarehouseNode,
NodeKind,
} from '~/queries/schema/schema-general'
import { sceneLogic } from '~/scenes/sceneLogic'
import {
BaseMathType,
Breadcrumb,
BreakdownAttributionType,
ChartDisplayType,
FunnelConversionWindowTimeUnit,
FunnelStepReference,
FunnelVizType,
GroupMathType,
GroupTypeIndex,
PropertyMathType,
SimpleIntervalType,
StepOrderValue,
} from '~/types'
import type { Noun } from '../../../frontend/src/models/groupsModel'
import type { CustomerAnalyticsConfig } from '../../../frontend/src/queries/schema/schema-general'
import type { GroupType } from '../../../frontend/src/types'
import { CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS } from './constants'
import { customerAnalyticsConfigLogic } from './customerAnalyticsConfigLogic'
export type BusinessType = 'b2c' | 'b2b'
type ActiveTab = 'dashboard' | 'journeys' | 'accounts' | 'notes' | 'announcements' | 'feed'
const SCENE_KEY_TO_TAB: Record<string, ActiveTab> = {
customerAnalyticsJourneys: 'journeys',
customerAnalyticsAccounts: 'accounts',
customerAnalyticsNotes: 'notes',
customerAnalyticsAnnouncements: 'announcements',
customerAnalyticsFeed: 'feed',
}
export interface InsightDefinition {
name: string
description?: string
query: InsightVizNode
requiredSeries?: Record<string, AnyEntityNode | null>
className?: string
}
const getDefaultCustomerAnalyticsInterval = (dateFrom: string | null, dateTo: string | null): SimpleIntervalType => {
const interval = getDefaultInterval(dateFrom, dateTo)
return interval === 'day' ? 'day' : 'month'
}
const INITIAL_DATE_FROM = '-30d' as string | null
const INITIAL_DATE_TO = null as string | null
const INITIAL_INTERVAL: SimpleIntervalType = getDefaultCustomerAnalyticsInterval(INITIAL_DATE_FROM, INITIAL_DATE_TO)
const INITIAL_DATE_FILTER = {
dateFrom: INITIAL_DATE_FROM,
dateTo: INITIAL_DATE_TO,
interval: INITIAL_INTERVAL,
}
const teamId = window.INSIGHTS_APP_CONTEXT?.current_team?.id
const persistConfig = { persist: true, prefix: `${teamId}_customer_analytics__` }
const setQueryParams = (params: Record<string, string>): string => {
const searchParams = { ...router.values.searchParams }
const urlParams = new URLSearchParams(searchParams)
Object.entries(params).forEach(([key, value]) => {
urlParams.set(key, value)
})
const currentPath = router.values.location.pathname
return `${currentPath}${urlParams.toString() ? '?' + urlParams.toString() : ''}`
}
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
export interface customerAnalyticsSceneLogicValues {
activityEvent: AnyEntityNode // customerAnalyticsConfigLogic
customerAnalyticsConfig: CustomerAnalyticsConfig // customerAnalyticsConfigLogic
paymentEvent: AnyEntityNode // customerAnalyticsConfigLogic
signupEvent: AnyEntityNode // customerAnalyticsConfigLogic
signupPageviewEvent: AnyEntityNode // customerAnalyticsConfigLogic
subscriptionEvent: AnyEntityNode // customerAnalyticsConfigLogic
aggregationLabel: (groupTypeIndex: number | null | undefined, deferToUserWording?: boolean) => Noun // groupsModel
groupTypesRaw: Array<GroupType> // groupsModel
groupsEnabled: boolean // groupsModel
sceneKey: string | null // sceneLogic
activeTab: ActiveTab
activeUsersInsights: InsightDefinition[]
breadcrumbs: Breadcrumb[]
businessType: BusinessType
customerLabel: {
plural: string
singular: string
}
dateFilter: {
dateFrom: string | null
dateTo: string | null
interval: SimpleIntervalType
}
dateRange: {
date_from: string | null
date_to: string | null
}
dauSeries: AnyEntityNode | null
filterTestAccounts: boolean
groupOptions: {
label: string
value: number
}[]
mauSeries: AnyEntityNode | null
mineOnly: boolean
paymentSeries: AnyEntityNode | null
selectedGroupType: number
sessionInsights: InsightDefinition[]
signupInsights: InsightDefinition[]
signupPageviewSeries: AnyEntityNode | null
signupSeries: AnyEntityNode | null
subscriptionSeries: AnyEntityNode | null
wauSeries: AnyEntityNode | null
}
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
export interface customerAnalyticsSceneLogicActions {
setBusinessType: (businessType: BusinessType) => {
businessType: BusinessType
}
setDates: (
dateFrom: string | null,
dateTo: string | null
) => {
dateFrom: string | null
dateTo: string | null
}
setFilterTestAccounts: (filterTestAccounts: boolean) => {
filterTestAccounts: boolean
}
setMineOnly: (mineOnly: boolean) => {
mineOnly: boolean
}
setSelectedGroupType: (selectedGroupType: number) => {
selectedGroupType: number
}
}
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
export interface customerAnalyticsSceneLogicMeta {
__keaTypeGenInternalSelectorTypes: {
activeTab: (sceneKey: string | null) => ActiveTab
customerLabel: (
aggregationLabel: (groupTypeIndex: number | null | undefined, deferToUserWording?: boolean) => Noun, // groupsModel
businessType: BusinessType,
selectedGroupType: number
) => {
plural: string
singular: string
}
dateRange: (dateFilter: { dateFrom: string | null; dateTo: string | null; interval: SimpleIntervalType }) => {
date_from: string | null
date_to: string | null
}
groupOptions: (groupTypesRaw: GroupType[]) => {
label: string
value: number
}[]
dauSeries: (
activityEvent: AnyEntityNode,
businessType: BusinessType,
selectedGroupType: number
) => AnyEntityNode | null
wauSeries: (
activityEvent: AnyEntityNode,
businessType: BusinessType,
selectedGroupType: number
) => AnyEntityNode | null
mauSeries: (
activityEvent: AnyEntityNode,
businessType: BusinessType,
selectedGroupType: number
) => AnyEntityNode | null
signupSeries: (
businessType: BusinessType,
selectedGroupType: number,
signupEvent: AnyEntityNode
) => AnyEntityNode | null
signupPageviewSeries: (
businessType: BusinessType,
selectedGroupType: number,
signupPageviewEvent: AnyEntityNode
) => AnyEntityNode | null
subscriptionSeries: (
businessType: BusinessType,
selectedGroupType: number,
subscriptionEvent: AnyEntityNode
) => AnyEntityNode | null
paymentSeries: (
businessType: BusinessType,
selectedGroupType: number,
paymentEvent: AnyEntityNode
) => AnyEntityNode | null
activeUsersInsights: (
customerLabel: {
plural: string
singular: string
},
dauSeries: AnyEntityNode | null,
wauSeries: AnyEntityNode | null,
mauSeries: AnyEntityNode | null,
dateRange: {
date_from: string | null
date_to: string | null
},
filterTestAccounts: boolean
) => InsightDefinition[]
sessionInsights: (
customerLabel: {
plural: string
singular: string
},
filterTestAccounts: boolean
) => InsightDefinition[]
signupInsights: (
businessType: BusinessType,
customerLabel: {
plural: string
singular: string
},
signupSeries: AnyEntityNode | null,
paymentSeries: AnyEntityNode | null,
selectedGroupType: number,
subscriptionSeries: AnyEntityNode | null,
signupPageviewSeries: AnyEntityNode | null,
dauSeries: AnyEntityNode | null,
dateRange: {
date_from: string | null
date_to: string | null
},
filterTestAccounts: boolean
) => InsightDefinition[]
}
}
export type customerAnalyticsSceneLogicType = MakeLogicType<
customerAnalyticsSceneLogicValues,
customerAnalyticsSceneLogicActions,
Record<string, any>,
customerAnalyticsSceneLogicMeta
>
export const customerAnalyticsSceneLogic = kea<customerAnalyticsSceneLogicType>([
path(['scenes', 'customerAnalytics', 'customerAnalyticsScene']),
connect(() => ({
values: [
customerAnalyticsConfigLogic,
[
'customerAnalyticsConfig',
'activityEvent',
'signupEvent',
'signupPageviewEvent',
'subscriptionEvent',
'paymentEvent',
],
groupsModel,
['aggregationLabel', 'groupsEnabled', 'groupTypesRaw'],
sceneLogic,
['sceneKey'],
],
})),
actions({
setDates: (dateFrom: string | null, dateTo: string | null) => ({ dateFrom, dateTo }),
setBusinessType: (businessType: BusinessType) => ({ businessType }),
setSelectedGroupType: (selectedGroupType: number) => ({ selectedGroupType }),
setFilterTestAccounts: (filterTestAccounts: boolean) => ({ filterTestAccounts }),
// Shared "My accounts" (assigned-to-me) toggle backing the checkbox on both the
// Accounts and Notes tabs. Held here (not per-tab) so the choice persists as the
// user switches between them.
setMineOnly: (mineOnly: boolean) => ({ mineOnly }),
}),
reducers(() => ({
dateFilter: [
INITIAL_DATE_FILTER,
persistConfig,
{
setDates: (_, { dateTo, dateFrom }) => ({
dateTo,
dateFrom,
interval: getDefaultCustomerAnalyticsInterval(dateFrom, dateTo),
}),
},
],
businessType: [
'b2c' as BusinessType,
persistConfig,
{
setBusinessType: (_, { businessType }) => businessType,
},
],
selectedGroupType: [
0,
persistConfig,
{
setSelectedGroupType: (_, { selectedGroupType }) => selectedGroupType,
},
],
filterTestAccounts: [
true,
persistConfig,
{
setFilterTestAccounts: (_, { filterTestAccounts }) => filterTestAccounts,
},
],
mineOnly: [
false,
persistConfig,
{
setMineOnly: (_, { mineOnly }) => mineOnly,
},
],
})),
selectors({
activeTab: [
(s) => [s.sceneKey],
(sceneKey: string | null): ActiveTab => (sceneKey && SCENE_KEY_TO_TAB[sceneKey]) || 'dashboard',
],
breadcrumbs: [
() => [],
(): Breadcrumb[] => [
{
key: Scene.CustomerAnalytics,
name: sceneConfigurations[Scene.CustomerAnalytics].name,
path: urls.customerAnalyticsDashboard(),
iconType: sceneConfigurations[Scene.CustomerAnalytics].iconType || 'default_icon_type',
},
],
],
customerLabel: [
(s) => [s.aggregationLabel, s.businessType, s.selectedGroupType],
(
aggregationLabel: any,
businessType: BusinessType,
selectedGroupType: number
): { singular: string; plural: string } => {
if (!aggregationLabel || typeof aggregationLabel !== 'function') {
return { singular: 'user', plural: 'users' }
}
if (businessType === 'b2c') {
return aggregationLabel(undefined, true)
}
return aggregationLabel(selectedGroupType)
},
],
dateRange: [
(s) => [s.dateFilter],
(dateFilter: {
dateFrom: string | null
dateTo: string | null
}): { date_from: string | null; date_to: string | null } => ({
date_from: dateFilter.dateFrom,
date_to: dateFilter.dateTo,
}),
],
groupOptions: [
(s) => [s.groupTypesRaw],
(groupTypesRaw: any[]): { label: string; value: number }[] => {
return groupTypesRaw.map((groupType) => ({
label: capitalizeFirstLetter(groupType.name_plural || wordPluralize(groupType.group_type)),
value: groupType.group_type_index,
}))
},
],
dauSeries: [
(s) => [s.activityEvent, s.businessType, s.selectedGroupType],
(
activityEvent: EventsNode | ActionsNode | null,
businessType: BusinessType,
selectedGroupType: GroupTypeIndex
): AnyEntityNode | null => {
if (!activityEvent) {
return null
}
if (businessType === 'b2c') {
return {
...activityEvent,
math: BaseMathType.UniqueUsers,
}
}
return {
...activityEvent,
math: GroupMathType.UniqueGroup,
math_group_type_index: selectedGroupType,
}
},
],
wauSeries: [
(s) => [s.activityEvent, s.businessType, s.selectedGroupType],
(
activityEvent: EventsNode | ActionsNode | null,
businessType: BusinessType,
selectedGroupType: GroupTypeIndex
): AnyEntityNode | null => {
if (!activityEvent) {
return null
}
if (businessType === 'b2b') {
return {
...activityEvent,
math: BaseMathType.WeeklyActiveUsers,
math_group_type_index: selectedGroupType,
}
}
return {
...activityEvent,
math: BaseMathType.WeeklyActiveUsers,
}
},
],
mauSeries: [
(s) => [s.activityEvent, s.businessType, s.selectedGroupType],
(
activityEvent: EventsNode | ActionsNode | null,
businessType: BusinessType,
selectedGroupType: GroupTypeIndex
): AnyEntityNode | null => {
if (!activityEvent) {
return null
}
if (businessType === 'b2b') {
return {
...activityEvent,
math: BaseMathType.MonthlyActiveUsers,
math_group_type_index: selectedGroupType,
}
}
return {
...activityEvent,
math: BaseMathType.MonthlyActiveUsers,
}
},
],
signupSeries: [
(s) => [s.businessType, s.selectedGroupType, s.signupEvent],
(
businessType: BusinessType,
selectedGroupType: GroupTypeIndex,
signupEvent: AnyEntityNode
): AnyEntityNode | null => {
if (Object.keys(signupEvent).length === 0) {
return null
}
if (businessType === 'b2c') {
return {
...signupEvent,
math: BaseMathType.UniqueUsers,
}
}
return {
...signupEvent,
math: GroupMathType.UniqueGroup,
math_group_type_index: selectedGroupType,
}
},
],
signupPageviewSeries: [
(s) => [s.businessType, s.selectedGroupType, s.signupPageviewEvent],
(
businessType: BusinessType,
selectedGroupType: GroupTypeIndex,
signupPageviewEvent: AnyEntityNode
): AnyEntityNode | null => {
if (Object.keys(signupPageviewEvent).length === 0) {
return null
}
if (businessType === 'b2c') {
return {
...signupPageviewEvent,
math: BaseMathType.UniqueUsers,
}
}
return {
...signupPageviewEvent,
math: GroupMathType.UniqueGroup,
math_group_type_index: selectedGroupType,
}
},
],
subscriptionSeries: [
(s) => [s.businessType, s.selectedGroupType, s.subscriptionEvent],
(
businessType: BusinessType,
selectedGroupType: GroupTypeIndex,
subscriptionEvent: AnyEntityNode
): AnyEntityNode | null => {
if (Object.keys(subscriptionEvent).length === 0) {
return null
}
if (businessType === 'b2c') {
return {
...subscriptionEvent,
math: BaseMathType.UniqueUsers,
}
}
return {
...subscriptionEvent,
math: GroupMathType.UniqueGroup,
math_group_type_index: selectedGroupType,
}
},
],
paymentSeries: [
(s) => [s.businessType, s.selectedGroupType, s.paymentEvent],
(
businessType: BusinessType,
selectedGroupType: GroupTypeIndex,
paymentEvent: AnyEntityNode
): AnyEntityNode | null => {
if (Object.keys(paymentEvent).length === 0) {
return null
}
if (businessType === 'b2c') {
return {
...paymentEvent,
math: BaseMathType.UniqueUsers,
}
}
return {
...paymentEvent,
math: GroupMathType.UniqueGroup,
math_group_type_index: selectedGroupType,
}
},
],
activeUsersInsights: [
(s) => [s.customerLabel, s.dauSeries, s.wauSeries, s.mauSeries, s.dateRange, s.filterTestAccounts],
(
customerLabel: Record<string, string>,
dauSeries: AnyEntityNode | null,
wauSeries: AnyEntityNode | null,
mauSeries: AnyEntityNode | null,
dateRange: { date_from: string | null; date_to: string | null },
filterTestAccounts: boolean
): InsightDefinition[] => {
// Backend guarantees activity event exists, but add safety check
if (!dauSeries || !wauSeries || !mauSeries) {
return []
}
return [
{
name: `Active ${customerLabel.plural} (daily/weekly/monthly)`,
className: 'row-span-2 h-[576px]',
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
// Label each line distinctly here rather than on the shared
// selectors, which are reused by other single-metric charts.
series: [
{ ...dauSeries, custom_name: 'Daily active' },
{ ...wauSeries, custom_name: 'Weekly active' },
{ ...mauSeries, custom_name: 'Monthly active' },
],
interval: 'day',
dateRange: {
date_from: dateRange.date_from,
date_to: dateRange.date_to,
explicitDate: false,
},
properties: [],
trendsFilter: {
display: ChartDisplayType.ActionsLineGraph,
showLegend: false,
yAxisScaleType: 'linear',
showValuesOnSeries: false,
smoothingIntervals: 1,
showPercentStackView: false,
aggregationAxisFormat: 'numeric',
showAlertThresholdLines: false,
},
breakdownFilter: {
breakdown_type: 'event',
},
filterTestAccounts,
},
},
},
{
name: `Weekly active ${customerLabel.plural}`,
className: 'h-[284px]',
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
series: [wauSeries],
interval: 'day',
dateRange: {
date_from: '-7d',
explicitDate: false,
},
properties: [],
trendsFilter: {
display: ChartDisplayType.BoldNumber,
showLegend: false,
yAxisScaleType: 'linear',
showValuesOnSeries: false,
smoothingIntervals: 1,
showPercentStackView: false,
aggregationAxisFormat: 'numeric',
showAlertThresholdLines: false,
},
compareFilter: {
compare: true,
},
breakdownFilter: {
breakdown_type: 'event',
},
filterTestAccounts,
},
},
},
{
name: `Monthly active ${customerLabel.plural}`,
className: 'h-[284px]',
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
series: [mauSeries],
interval: 'day',
dateRange: {
date_to: null,
date_from: '-30d',
explicitDate: false,
},
properties: [],
trendsFilter: {
display: ChartDisplayType.BoldNumber,
showLegend: false,
yAxisScaleType: 'linear',
showValuesOnSeries: false,
smoothingIntervals: 1,
showPercentStackView: false,
aggregationAxisFormat: 'numeric',
showAlertThresholdLines: false,
},
compareFilter: {
compare: true,
},
breakdownFilter: {
breakdown_type: 'event',
},
filterTestAccounts,
},
},
},
]
},
],
sessionInsights: [
(s) => [s.customerLabel, s.filterTestAccounts],
(
customerLabel: {
plural: string
singular: string
},
filterTestAccounts: boolean
): InsightDefinition[] => [
{
name: 'Unique sessions',
description: 'Events without session IDs are excluded.',
className: 'h-[284px]',
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
series: [
{
kind: NodeKind.EventsNode,
math: BaseMathType.UniqueSessions,
name: 'All events',
event: null,
},
],
interval: 'minute',
dateRange: {
date_to: '',
date_from: '-1h',
explicitDate: false,
},
properties: [],
trendsFilter: {
display: ChartDisplayType.BoldNumber,
showLegend: false,
yAxisScaleType: 'linear',
showValuesOnSeries: false,
showPercentStackView: false,
aggregationAxisFormat: 'numeric',
showAlertThresholdLines: false,
},
compareFilter: {
compare: true,
},
breakdownFilter: undefined,
filterTestAccounts,
},
},
},
{
name: `Unique ${customerLabel.plural}`,
className: 'h-[284px]',
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
series: [
{
kind: NodeKind.EventsNode,
math: BaseMathType.UniqueUsers,
name: 'All events',
event: null,
},
],
interval: 'hour',
dateRange: {
date_to: '',
date_from: '-1h',
explicitDate: false,
},
properties: [],
trendsFilter: {
display: ChartDisplayType.BoldNumber,
showLegend: false,
yAxisScaleType: 'linear',
showValuesOnSeries: false,
showPercentStackView: false,
aggregationAxisFormat: 'numeric',
showAlertThresholdLines: false,
},
compareFilter: {
compare: true,
},
breakdownFilter: {
breakdown_type: 'event',
},
filterTestAccounts,
},
},
},
{
name: 'Average session duration',
className: 'h-[284px]',
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
series: [
{
kind: NodeKind.EventsNode,
math: PropertyMathType.Average,
name: '$pageview',
event: '$pageview',
math_property: '$session_duration',
},
],
interval: 'minute',
dateRange: {
date_to: '',
date_from: '-1h',
explicitDate: false,
},
properties: [],
trendsFilter: {
display: ChartDisplayType.BoldNumber,
showLegend: false,
yAxisScaleType: 'linear',
showValuesOnSeries: false,
showPercentStackView: false,
aggregationAxisFormat: 'duration',
showAlertThresholdLines: false,
},
compareFilter: {
compare: true,
},
breakdownFilter: {
breakdown_type: 'event',
},
filterTestAccounts,
},
},
},
],
],
signupInsights: [
(s) => [
s.businessType,
s.customerLabel,
s.signupSeries,
s.paymentSeries,
s.selectedGroupType,
s.subscriptionSeries,
s.signupPageviewSeries,
s.dauSeries,
s.dateRange,
s.filterTestAccounts,
],
(
businessType: BusinessType,
customerLabel: {
plural: string
singular: string
},
signupSeries: AnyEntityNode | null,
paymentSeries: AnyEntityNode | null,
selectedGroupType: number,
subscriptionSeries: AnyEntityNode | null,
signupPageviewSeries: AnyEntityNode | null,
dauSeries: AnyEntityNode | null,
dateRange: {
date_from: string | null
date_to: string | null
},
filterTestAccounts: boolean
): InsightDefinition[] => [
{
name: `${capitalizeFirstLetter(customerLabel.singular)} signups`,
requiredSeries: { signupSeries },
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
series: [signupSeries as AnyEntityNode],
interval: 'day',
dateRange: {
date_to: dateRange.date_to,
date_from: dateRange.date_from,
explicitDate: false,
},
properties: [],
trendsFilter: {
display: ChartDisplayType.BoldNumber,
showLegend: false,
yAxisScaleType: 'linear',
showValuesOnSeries: false,
smoothingIntervals: 1,
showPercentStackView: false,
aggregationAxisFormat: 'numeric',
showAlertThresholdLines: false,
},
compareFilter: {
compare: true,
},
breakdownFilter: {
breakdown_type: 'event',
},
filterTestAccounts,
},
},
},
{
name: `Total paying ${customerLabel.plural}`,
requiredSeries: { paymentSeries },
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
series: [paymentSeries as AnyEntityNode],
interval: 'day',
dateRange: {
date_to: dateRange.date_to,
date_from: dateRange.date_from,
explicitDate: false,
},
properties: [],
trendsFilter: {
display: ChartDisplayType.BoldNumber,
showLegend: false,
yAxisScaleType: 'linear',
showValuesOnSeries: false,
showPercentStackView: false,
aggregationAxisFormat: 'numeric',
showAlertThresholdLines: false,
},
compareFilter: {
compare: true,
},
filterTestAccounts,
},
},
},
{
name: `${capitalizeFirstLetter(customerLabel.singular)} signups and subscriptions`,
requiredSeries: { signupSeries, subscriptionSeries },
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
series: [signupSeries as AnyEntityNode, subscriptionSeries as AnyEntityNode],
interval: 'day',
dateRange: {
date_from: dateRange.date_from,
date_to: dateRange.date_to,
explicitDate: false,
},
properties: [],
trendsFilter: {
display: ChartDisplayType.ActionsLineGraph,
showLegend: false,
yAxisScaleType: 'linear',
showMultipleYAxes: false,
showValuesOnSeries: false,
smoothingIntervals: 7,
showPercentStackView: false,
aggregationAxisFormat: 'numeric',
showAlertThresholdLines: false,
},
breakdownFilter: {
breakdown_type: 'event',
},
filterTestAccounts,
},
},
},
{
name: 'New signups',
requiredSeries: { signupSeries },
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
series: [signupSeries as AnyEntityNode],
interval: 'week',
dateRange: {
date_to: dateRange.date_to,
date_from: dateRange.date_from,
explicitDate: false,
},
properties: [],
trendsFilter: {
display: ChartDisplayType.ActionsAreaGraph,
showLegend: false,
yAxisScaleType: 'linear',
showValuesOnSeries: false,
showPercentStackView: false,
aggregationAxisFormat: 'numeric',
showAlertThresholdLines: false,
},
breakdownFilter: {
breakdown_type: 'event',
},
filterTestAccounts,
},
},
},
{
name: 'Cumulative signups (adoption)',
requiredSeries: { signupSeries },
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
series: [signupSeries as AnyEntityNode],
interval: 'day',
dateRange: {
date_from: dateRange.date_from,
date_to: dateRange.date_to,
explicitDate: false,
},
properties: [],
trendsFilter: {
display: ChartDisplayType.ActionsLineGraphCumulative,
showLegend: false,
showTrendLines: false,
yAxisScaleType: 'linear',
showMultipleYAxes: false,
showValuesOnSeries: false,
showPercentStackView: false,
aggregationAxisFormat: 'numeric',
showAlertThresholdLines: false,
},
breakdownFilter: {
breakdown_type: 'event',
},
filterTestAccounts,
},
},
},
{
name: 'Signup conversion',
requiredSeries: { signupSeries, signupPageviewSeries },
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.FunnelsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
...(businessType === 'b2c' ? {} : { aggregation_group_type_index: selectedGroupType }),
series: [
signupPageviewSeries as AnyEntityNode<FunnelsDataWarehouseNode>,
signupSeries as AnyEntityNode<FunnelsDataWarehouseNode>,
],
interval: 'week',
dateRange: {
date_from: dateRange.date_from,
date_to: dateRange.date_to,
explicitDate: false,
},
properties: [],
funnelsFilter: {
layout: FunnelLayout.horizontal,
exclusions: [],
funnelVizType: FunnelVizType.Steps,
funnelOrderType: StepOrderValue.ORDERED,
funnelStepReference: FunnelStepReference.total,
funnelWindowInterval: 14,
breakdownAttributionType: BreakdownAttributionType.FirstTouch,
funnelWindowIntervalUnit: FunnelConversionWindowTimeUnit.Day,
},
breakdownFilter: {
breakdown_type: 'event',
},
filterTestAccounts,
},
},
},
{
name: `Which ${customerLabel.plural} are highly engaged?`,
requiredSeries: { dauSeries },
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.LifecycleQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
...(businessType === 'b2c' ? {} : { aggregation_group_type_index: selectedGroupType }),
series: [dauSeries as AnyEntityNode<LifecycleDataWarehouseNode>],
interval: 'week',
dateRange: {
date_from: dateRange.date_from,
date_to: dateRange.date_to,
explicitDate: false,
},
properties: [],
lifecycleFilter: {
showLegend: false,
},
filterTestAccounts,
},
},
},
{
name: 'Free-to-paid conversion',
requiredSeries: { signupSeries, paymentSeries },
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.FunnelsQuery,
tags: CUSTOMER_ANALYTICS_DEFAULT_QUERY_TAGS,
...(businessType === 'b2c' ? {} : { aggregation_group_type_index: selectedGroupType }),
series: [
signupSeries as AnyEntityNode<FunnelsDataWarehouseNode>,
paymentSeries as AnyEntityNode<FunnelsDataWarehouseNode>,
],
dateRange: {
date_from: dateRange.date_from,
date_to: dateRange.date_to,
explicitDate: false,
},
properties: [],
funnelsFilter: {
layout: FunnelLayout.horizontal,
exclusions: [],
funnelVizType: FunnelVizType.Steps,
funnelOrderType: StepOrderValue.ORDERED,
funnelStepReference: FunnelStepReference.total,
funnelWindowInterval: 6,
breakdownAttributionType: BreakdownAttributionType.FirstTouch,
funnelWindowIntervalUnit: FunnelConversionWindowTimeUnit.Week,
},
breakdownFilter: {
breakdown_type: 'event',
},
filterTestAccounts,
},
},
},
],
],
}),
trackedActionToUrl(() => ({
setDates: ({ dateFrom, dateTo }): string =>
setQueryParams({ date_from: dateFrom ?? '', date_to: dateTo ?? '' }),
setFilterTestAccounts: ({ filterTestAccounts }): string =>
setQueryParams({ filter_test_accounts: String(filterTestAccounts) }),
})),
urlToAction(({ actions, values }) => ({
'*': (_, { date_from, date_to, filter_test_accounts }) => {
if (
(date_from && date_from !== values.dateFilter.dateFrom) ||
(date_to && date_to !== values.dateFilter.dateTo)
) {
actions.setDates(date_from, date_to)
}
const filterTestAccountsValue =
filter_test_accounts === undefined
? values.filterTestAccounts
: [true, 'true', 1, '1'].includes(filter_test_accounts as string | number | boolean)
if (filterTestAccountsValue !== values.filterTestAccounts) {
actions.setFilterTestAccounts(filterTestAccountsValue)
}
},
})),
])