mirror of
https://github.com/BillyOutlast/posthog.git
synced 2026-02-04 03:01:23 +01:00
feat(web-analytics): allow stats table to be displayed as trend (#29497)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Robbie <robbie.coomber@gmail.com>
This commit is contained in:
@@ -245,6 +245,7 @@ export const FEATURE_FLAGS = {
|
||||
DELAYED_LOADING_ANIMATION: 'delayed-loading-animation', // owner: @raquelmsmith
|
||||
PROJECTED_TOTAL_AMOUNT: 'projected-total-amount', // owner: @zach
|
||||
SESSION_RECORDINGS_PLAYLIST_COUNT_COLUMN: 'session-recordings-playlist-count-column', // owner: @pauldambra #team-replay
|
||||
WEB_ANALYTICS_TREND_VIZ_TOGGLE: 'web-analytics-trend-viz-toggle', // owner: @lricoy #team-web-analytics
|
||||
} as const
|
||||
export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS]
|
||||
|
||||
|
||||
@@ -6,15 +6,17 @@ import React from 'react'
|
||||
import { useSliderPositioning } from '../hooks'
|
||||
import { LemonButton, LemonButtonProps } from '../LemonButton'
|
||||
|
||||
export interface LemonSegmentedButtonOption<T extends React.Key> {
|
||||
value: T
|
||||
label: string | JSX.Element
|
||||
icon?: React.ReactElement
|
||||
/** Like plain `disabled`, except we enforce a reason to be shown in the tooltip. */
|
||||
disabledReason?: string
|
||||
tooltip?: string | JSX.Element
|
||||
'data-attr'?: string
|
||||
}
|
||||
// Expects at least one of label or icon to be provided
|
||||
export type LemonSegmentedButtonOption<T extends React.Key> = { value: T } & (
|
||||
| { label: string | JSX.Element }
|
||||
| { icon: JSX.Element }
|
||||
) & {
|
||||
label?: string | JSX.Element
|
||||
icon?: JSX.Element
|
||||
disabledReason?: string
|
||||
tooltip?: string | JSX.Element
|
||||
'data-attr'?: string
|
||||
}
|
||||
|
||||
export interface LemonSegmentedButtonProps<T extends React.Key> {
|
||||
value?: T
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { IconExpand45, IconInfo, IconOpenSidebar, IconX } from '@posthog/icons'
|
||||
import { IconExpand45, IconInfo, IconLineGraph, IconOpenSidebar, IconX } from '@posthog/icons'
|
||||
import { LemonSegmentedButton } from '@posthog/lemon-ui'
|
||||
import clsx from 'clsx'
|
||||
import { BindLogic, useActions, useValues } from 'kea'
|
||||
import { VersionCheckerBanner } from 'lib/components/VersionChecker/VersionCheckerBanner'
|
||||
import { IconOpenInNew } from 'lib/lemon-ui/icons'
|
||||
import { FEATURE_FLAGS } from 'lib/constants'
|
||||
import { IconOpenInNew, IconTableChart } from 'lib/lemon-ui/icons'
|
||||
import { LemonButton } from 'lib/lemon-ui/LemonButton'
|
||||
import { LemonSegmentedSelect } from 'lib/lemon-ui/LemonSegmentedSelect/LemonSegmentedSelect'
|
||||
import { LemonTabs } from 'lib/lemon-ui/LemonTabs'
|
||||
import { PostHogComDocsURL } from 'lib/lemon-ui/Link/Link'
|
||||
import { Popover } from 'lib/lemon-ui/Popover'
|
||||
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
|
||||
import { isNotNil } from 'lib/utils'
|
||||
import { addProductIntentForCrossSell, ProductIntentContext } from 'lib/utils/product-intents'
|
||||
import React, { useState } from 'react'
|
||||
@@ -20,6 +23,7 @@ import {
|
||||
QueryTile,
|
||||
TabsTile,
|
||||
TileId,
|
||||
TileVsualizationOption,
|
||||
WEB_ANALYTICS_DATA_COLLECTION_NODE_ID,
|
||||
webAnalyticsLogic,
|
||||
} from 'scenes/web-analytics/webAnalyticsLogic'
|
||||
@@ -113,6 +117,7 @@ const QueryTileItem = ({ tile }: { tile: QueryTile }): JSX.Element => {
|
||||
insightProps={insightProps}
|
||||
control={control}
|
||||
showIntervalSelect={showIntervalSelect}
|
||||
tileId={tile.tileId}
|
||||
/>
|
||||
|
||||
{buttonsRow.length > 0 ? (
|
||||
@@ -148,6 +153,7 @@ const TabsTileItem = ({ tile }: { tile: TabsTile }): JSX.Element => {
|
||||
showIntervalSelect={tab.showIntervalSelect}
|
||||
control={tab.control}
|
||||
insightProps={tab.insightProps}
|
||||
tileId={tile.tileId}
|
||||
/>
|
||||
),
|
||||
linkText: tab.linkText,
|
||||
@@ -193,6 +199,16 @@ export const WebTabs = ({
|
||||
const activeTab = tabs.find((t) => t.id === activeTabId)
|
||||
const newInsightUrl = getNewInsightUrl(tileId, activeTabId)
|
||||
|
||||
const { featureFlags } = useValues(featureFlagLogic)
|
||||
|
||||
const { setTileVisualization } = useActions(webAnalyticsLogic)
|
||||
const { tileVisualizations } = useValues(webAnalyticsLogic)
|
||||
const visualization = tileVisualizations[tileId]
|
||||
|
||||
const isVisualizationToggleEnabled =
|
||||
featureFlags[FEATURE_FLAGS.WEB_ANALYTICS_TREND_VIZ_TOGGLE] &&
|
||||
[TileId.SOURCES, TileId.DEVICES, TileId.PATHS].includes(tileId)
|
||||
|
||||
const buttonsRow = [
|
||||
activeTab?.canOpenInsight && newInsightUrl ? (
|
||||
<LemonButton
|
||||
@@ -239,6 +255,25 @@ export const WebTabs = ({
|
||||
)}
|
||||
</h2>
|
||||
|
||||
{isVisualizationToggleEnabled && (
|
||||
<LemonSegmentedButton
|
||||
value={visualization || 'table'}
|
||||
onChange={(value) => setTileVisualization(tileId, value as TileVsualizationOption)}
|
||||
options={[
|
||||
{
|
||||
value: 'table',
|
||||
icon: <IconTableChart />,
|
||||
},
|
||||
{
|
||||
value: 'graph',
|
||||
icon: <IconLineGraph />,
|
||||
},
|
||||
]}
|
||||
size="small"
|
||||
className="mr-2"
|
||||
/>
|
||||
)}
|
||||
|
||||
<LemonSegmentedSelect
|
||||
shrinkOn={7}
|
||||
size="small"
|
||||
|
||||
@@ -43,6 +43,7 @@ export const WebAnalyticsModal = (): JSX.Element | null => {
|
||||
insightProps={modal.insightProps}
|
||||
showIntervalSelect={modal.showIntervalSelect}
|
||||
control={modal.control}
|
||||
tileId={modal.tileId}
|
||||
/>
|
||||
</LemonModal.Content>
|
||||
<div className="flex flex-row justify-end">
|
||||
|
||||
@@ -17,7 +17,12 @@ import { countryCodeToFlag, countryCodeToName } from 'scenes/insights/views/Worl
|
||||
import { languageCodeToFlag, languageCodeToName } from 'scenes/insights/views/WorldMap/countryCodes'
|
||||
import { urls } from 'scenes/urls'
|
||||
import { userLogic } from 'scenes/userLogic'
|
||||
import { GeographyTab, webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic'
|
||||
import {
|
||||
GeographyTab,
|
||||
TileId,
|
||||
webAnalyticsLogic,
|
||||
webStatsBreakdownToPropertyName,
|
||||
} from 'scenes/web-analytics/webAnalyticsLogic'
|
||||
|
||||
import { actionsModel } from '~/models/actionsModel'
|
||||
import { Query } from '~/queries/Query/Query'
|
||||
@@ -315,61 +320,6 @@ const SortableCell = (name: string, orderByField: WebAnalyticsOrderByFields): Qu
|
||||
)
|
||||
}
|
||||
|
||||
export const webStatsBreakdownToPropertyName = (
|
||||
breakdownBy: WebStatsBreakdown
|
||||
):
|
||||
| { key: string; type: PropertyFilterType.Person | PropertyFilterType.Event | PropertyFilterType.Session }
|
||||
| undefined => {
|
||||
switch (breakdownBy) {
|
||||
case WebStatsBreakdown.Page:
|
||||
return { key: '$pathname', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.InitialPage:
|
||||
return { key: '$entry_pathname', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.ExitPage:
|
||||
return { key: '$end_pathname', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.ExitClick:
|
||||
return { key: '$last_external_click_url', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.ScreenName:
|
||||
return { key: '$screen_name', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.InitialChannelType:
|
||||
return { key: '$channel_type', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialReferringDomain:
|
||||
return { key: '$entry_referring_domain', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialUTMSource:
|
||||
return { key: '$entry_utm_source', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialUTMCampaign:
|
||||
return { key: '$entry_utm_campaign', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialUTMMedium:
|
||||
return { key: '$entry_utm_medium', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialUTMContent:
|
||||
return { key: '$entry_utm_content', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialUTMTerm:
|
||||
return { key: '$entry_utm_term', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.Browser:
|
||||
return { key: '$browser', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.OS:
|
||||
return { key: '$os', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.Viewport:
|
||||
return { key: '$viewport', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.DeviceType:
|
||||
return { key: '$device_type', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.Country:
|
||||
return { key: '$geoip_country_code', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.Region:
|
||||
return { key: '$geoip_subdivision_1_code', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.City:
|
||||
return { key: '$geoip_city_name', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.Timezone:
|
||||
return { key: '$timezone', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.Language:
|
||||
return { key: '$geoip_language', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.InitialUTMSourceMediumCampaign:
|
||||
return undefined
|
||||
default:
|
||||
throw new UnexpectedNeverError(breakdownBy)
|
||||
}
|
||||
}
|
||||
|
||||
export const webAnalyticsDataTableQueryContext: QueryContext = {
|
||||
columns: {
|
||||
breakdown_value: {
|
||||
@@ -526,6 +476,7 @@ export const WebStatsTableTile = ({
|
||||
}: QueryWithInsightProps<DataTableNode> & {
|
||||
breakdownBy: WebStatsBreakdown
|
||||
control?: JSX.Element
|
||||
tileId: TileId
|
||||
}): JSX.Element => {
|
||||
const { togglePropertyFilter } = useActions(webAnalyticsLogic)
|
||||
|
||||
@@ -711,9 +662,11 @@ export const WebQuery = ({
|
||||
showIntervalSelect,
|
||||
control,
|
||||
insightProps,
|
||||
tileId,
|
||||
}: QueryWithInsightProps<QuerySchema> & {
|
||||
showIntervalSelect?: boolean
|
||||
control?: JSX.Element
|
||||
tileId: TileId
|
||||
}): JSX.Element => {
|
||||
if (query.kind === NodeKind.DataTableNode && query.source.kind === NodeKind.WebStatsTableQuery) {
|
||||
return (
|
||||
@@ -722,6 +675,7 @@ export const WebQuery = ({
|
||||
breakdownBy={query.source.breakdownBy}
|
||||
insightProps={insightProps}
|
||||
control={control}
|
||||
tileId={tileId}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { FEATURE_FLAGS, RETENTION_FIRST_TIME } from 'lib/constants'
|
||||
import { LemonButton } from 'lib/lemon-ui/LemonButton'
|
||||
import { Link, PostHogComDocsURL } from 'lib/lemon-ui/Link/Link'
|
||||
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
|
||||
import { getDefaultInterval, isNotNil, objectsEqual, updateDatesWithInterval } from 'lib/utils'
|
||||
import { getDefaultInterval, isNotNil, objectsEqual, UnexpectedNeverError, updateDatesWithInterval } from 'lib/utils'
|
||||
import { isDefinitionStale } from 'lib/utils/definitions'
|
||||
import { errorTrackingQuery } from 'scenes/error-tracking/queries'
|
||||
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
ActionConversionGoal,
|
||||
ActionsNode,
|
||||
AnyEntityNode,
|
||||
BreakdownFilter,
|
||||
CompareFilter,
|
||||
CustomEventConversionGoal,
|
||||
EventsNode,
|
||||
@@ -113,6 +114,8 @@ const loadPriorityMap: Record<TileId, number> = {
|
||||
[TileId.WEB_VITALS_PATH_BREAKDOWN]: 12,
|
||||
}
|
||||
|
||||
export type TileVsualizationOption = 'table' | 'graph'
|
||||
|
||||
export interface BaseTile {
|
||||
tileId: TileId
|
||||
layout: WebTileLayout
|
||||
@@ -236,6 +239,76 @@ export interface WebAnalyticsStatusCheck {
|
||||
hasAuthorizedUrls: boolean
|
||||
}
|
||||
|
||||
export type TileVisualizationOption = 'table' | 'graph'
|
||||
|
||||
export const webStatsBreakdownToPropertyName = (
|
||||
breakdownBy: WebStatsBreakdown
|
||||
):
|
||||
| { key: string; type: PropertyFilterType.Person | PropertyFilterType.Event | PropertyFilterType.Session }
|
||||
| undefined => {
|
||||
switch (breakdownBy) {
|
||||
case WebStatsBreakdown.Page:
|
||||
return { key: '$pathname', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.InitialPage:
|
||||
return { key: '$entry_pathname', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.ExitPage:
|
||||
return { key: '$end_pathname', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.ExitClick:
|
||||
return { key: '$last_external_click_url', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.ScreenName:
|
||||
return { key: '$screen_name', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.InitialChannelType:
|
||||
return { key: '$channel_type', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialReferringDomain:
|
||||
return { key: '$entry_referring_domain', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialUTMSource:
|
||||
return { key: '$entry_utm_source', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialUTMCampaign:
|
||||
return { key: '$entry_utm_campaign', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialUTMMedium:
|
||||
return { key: '$entry_utm_medium', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialUTMContent:
|
||||
return { key: '$entry_utm_content', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.InitialUTMTerm:
|
||||
return { key: '$entry_utm_term', type: PropertyFilterType.Session }
|
||||
case WebStatsBreakdown.Browser:
|
||||
return { key: '$browser', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.OS:
|
||||
return { key: '$os', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.Viewport:
|
||||
return { key: '$viewport', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.DeviceType:
|
||||
return { key: '$device_type', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.Country:
|
||||
return { key: '$geoip_country_code', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.Region:
|
||||
return { key: '$geoip_subdivision_1_code', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.City:
|
||||
return { key: '$geoip_city_name', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.Timezone:
|
||||
return { key: '$timezone', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.Language:
|
||||
return { key: '$geoip_language', type: PropertyFilterType.Event }
|
||||
case WebStatsBreakdown.InitialUTMSourceMediumCampaign:
|
||||
return undefined
|
||||
default:
|
||||
throw new UnexpectedNeverError(breakdownBy)
|
||||
}
|
||||
}
|
||||
|
||||
export const getWebAnalyticsBreakdownFilter = (breakdown: WebStatsBreakdown): BreakdownFilter | undefined => {
|
||||
const property = webStatsBreakdownToPropertyName(breakdown)
|
||||
|
||||
if (!property) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
breakdown_type: property.type,
|
||||
breakdown: property.key,
|
||||
}
|
||||
}
|
||||
|
||||
const GEOIP_TEMPLATE_IDS = ['template-geoip', 'plugin-posthog-plugin-geoip']
|
||||
|
||||
export const WEB_ANALYTICS_DATA_COLLECTION_NODE_ID = 'web-analytics'
|
||||
@@ -313,6 +386,7 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
setProductTab: (tab: ProductTab) => ({ tab }),
|
||||
setWebVitalsPercentile: (percentile: WebVitalsPercentile) => ({ percentile }),
|
||||
setWebVitalsTab: (tab: WebVitalsMetric) => ({ tab }),
|
||||
setTileVisualization: (tileId: TileId, visualization: TileVsualizationOption) => ({ tileId, visualization }),
|
||||
}),
|
||||
reducers({
|
||||
rawWebAnalyticsFilters: [
|
||||
@@ -586,6 +660,15 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
setWebVitalsTab: (_, { tab }) => tab,
|
||||
},
|
||||
],
|
||||
tileVisualizations: [
|
||||
{} as Record<TileId, TileVisualizationOption>,
|
||||
{
|
||||
setTileVisualization: (state, { tileId, visualization }) => ({
|
||||
...state,
|
||||
[tileId]: visualization,
|
||||
}),
|
||||
},
|
||||
],
|
||||
}),
|
||||
selectors(({ actions, values }) => ({
|
||||
breadcrumbs: [
|
||||
@@ -749,6 +832,7 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
() => values.featureFlags,
|
||||
() => values.isGreaterThanMd,
|
||||
() => values.currentTeam,
|
||||
() => values.tileVisualizations,
|
||||
],
|
||||
(
|
||||
productTab,
|
||||
@@ -766,7 +850,8 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
},
|
||||
featureFlags,
|
||||
isGreaterThanMd,
|
||||
currentTeam
|
||||
currentTeam,
|
||||
tileVisualizations
|
||||
): WebAnalyticsTile[] => {
|
||||
const dateRange = { date_from: dateFrom, date_to: dateTo }
|
||||
const sampling = { enabled: false, forceSamplingRate: { numerator: 1, denominator: 10 } }
|
||||
@@ -900,10 +985,48 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
'cross_sell',
|
||||
].filter(isNotNil)
|
||||
|
||||
return {
|
||||
// Check if this tile has a visualization preference
|
||||
const visualization = tileVisualizations[tileId]
|
||||
|
||||
const baseTabProps = {
|
||||
id: tabId,
|
||||
title,
|
||||
linkText,
|
||||
insightProps: createInsightProps(tileId, tabId),
|
||||
canOpenModal: true,
|
||||
...(tab || {}),
|
||||
}
|
||||
|
||||
// In case of a graph, we need to use the breakdownFilter and a InsightsVizNode,
|
||||
// which will actually be handled by a WebStatsTrendTile instead of a WebStatsTableTile
|
||||
if (featureFlags[FEATURE_FLAGS.WEB_ANALYTICS_TREND_VIZ_TOGGLE] && visualization === 'graph') {
|
||||
return {
|
||||
...baseTabProps,
|
||||
query: {
|
||||
kind: NodeKind.InsightVizNode,
|
||||
source: {
|
||||
kind: NodeKind.TrendsQuery,
|
||||
dateRange,
|
||||
interval,
|
||||
series: [uniqueUserSeries],
|
||||
trendsFilter: {
|
||||
display: ChartDisplayType.ActionsLineGraph,
|
||||
},
|
||||
breakdownFilter: getWebAnalyticsBreakdownFilter(breakdownBy),
|
||||
filterTestAccounts,
|
||||
conversionGoal,
|
||||
properties: webAnalyticsFilters,
|
||||
},
|
||||
hidePersonsModal: true,
|
||||
embedded: true,
|
||||
},
|
||||
canOpenInsight: true,
|
||||
canOpenModal: false,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...baseTabProps,
|
||||
query: {
|
||||
full: true,
|
||||
kind: NodeKind.DataTableNode,
|
||||
@@ -924,9 +1047,6 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
showActions: true,
|
||||
columns,
|
||||
},
|
||||
insightProps: createInsightProps(tileId, tabId),
|
||||
canOpenModal: true,
|
||||
...(tab || {}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2125,6 +2245,7 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
webVitalsPercentile,
|
||||
domainFilter,
|
||||
deviceTypeFilter,
|
||||
tileVisualizations,
|
||||
} = values
|
||||
|
||||
// Make sure we're storing the raw filters only, or else we'll have issues with the domain/device type filters
|
||||
@@ -2180,6 +2301,9 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
if (deviceTypeFilter) {
|
||||
urlParams.set('device_type', deviceTypeFilter)
|
||||
}
|
||||
if (tileVisualizations) {
|
||||
urlParams.set('tile_visualizations', JSON.stringify(tileVisualizations))
|
||||
}
|
||||
|
||||
const basePath = productTab === ProductTab.WEB_VITALS ? '/web/web-vitals' : '/web'
|
||||
return `${basePath}${urlParams.toString() ? '?' + urlParams.toString() : ''}`
|
||||
@@ -2202,6 +2326,7 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
setIsPathCleaningEnabled: stateToUrl,
|
||||
setDomainFilter: stateToUrl,
|
||||
setDeviceTypeFilter: stateToUrl,
|
||||
setTileVisualization: stateToUrl,
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -2226,6 +2351,7 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
percentile,
|
||||
domain,
|
||||
device_type,
|
||||
tile_visualizations,
|
||||
}: Record<string, any>
|
||||
): void => {
|
||||
if (![ProductTab.ANALYTICS, ProductTab.WEB_VITALS].includes(productTab)) {
|
||||
@@ -2291,6 +2417,11 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
if (device_type && device_type !== values.deviceTypeFilter) {
|
||||
actions.setDeviceTypeFilter(device_type)
|
||||
}
|
||||
if (tile_visualizations && !objectsEqual(tile_visualizations, values.tileVisualizations)) {
|
||||
for (const [tileId, visualization] of Object.entries(tile_visualizations)) {
|
||||
actions.setTileVisualization(tileId as TileId, visualization as TileVisualizationOption)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { '/web': toAction, '/web/:productTab': toAction }
|
||||
|
||||
Reference in New Issue
Block a user