Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update web stats table query to include a date breakdown #29473

Closed
wants to merge 11 commits into from
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,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_TRENDS_BREAKDOWN: 'web-trends-breakdown', // owner: @lricoy #team-web-analytics
} as const
export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS]

Expand Down
3 changes: 3 additions & 0 deletions frontend/src/queries/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -15819,6 +15819,9 @@
"includeBounceRate": {
"type": "boolean"
},
"includeDateBreakdown": {
"type": "boolean"
},
"includeRevenue": {
"type": "boolean"
},
Expand Down
1 change: 1 addition & 0 deletions frontend/src/queries/schema/schema-general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,7 @@ export enum WebStatsBreakdown {
export interface WebStatsTableQuery extends WebAnalyticsQueryBase<WebStatsTableQueryResponse> {
kind: NodeKind.WebStatsTableQuery
breakdownBy: WebStatsBreakdown
includeDateBreakdown?: boolean // include date in the results for breakdown visualization
includeScrollDepth?: boolean // automatically sets includeBounceRate to true
includeBounceRate?: boolean
limit?: integer
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/scenes/insights/sharedUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ import {
export const keyForInsightLogicProps =
(defaultKey = 'new') =>
(props: InsightLogicProps): string => {
if (!('dashboardItemId' in props)) {
throw new Error('Must init with dashboardItemId, even if undefined')
}
// TODO: UNCOMMENT AND CHECK WITH TEAM HOW TO PROVIDE THIS CORRECTLY
// if (!('dashboardItemId' in props)) {
// throw new Error('Must init with dashboardItemId, even if undefined')
// }
return props.dashboardItemId
? `${props.dashboardItemId}${props.dashboardId ? `/on-dashboard-${props.dashboardId}` : ''}`
: defaultKey
Expand Down
39 changes: 37 additions & 2 deletions frontend/src/scenes/web-analytics/WebAnalyticsDashboard.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -113,6 +116,7 @@ const QueryTileItem = ({ tile }: { tile: QueryTile }): JSX.Element => {
insightProps={insightProps}
control={control}
showIntervalSelect={showIntervalSelect}
tileId={tile.tileId}
/>

{buttonsRow.length > 0 ? <div className="flex justify-end my-2 space-x-2">{buttonsRow}</div> : null}
Expand Down Expand Up @@ -146,6 +150,7 @@ const TabsTileItem = ({ tile }: { tile: TabsTile }): JSX.Element => {
showIntervalSelect={tab.showIntervalSelect}
control={tab.control}
insightProps={tab.insightProps}
tileId={tile.tileId}
/>
),
linkText: tab.linkText,
Expand Down Expand Up @@ -191,6 +196,11 @@ export const WebTabs = ({
const activeTab = tabs.find((t) => t.id === activeTabId)
const newInsightUrl = getNewInsightUrl(tileId, activeTabId)

const { setTileVisualization } = useActions(webAnalyticsLogic)
const { tileVisualizations } = useValues(webAnalyticsLogic)
const { featureFlags } = useValues(featureFlagLogic)
const visualization = tileVisualizations[tileId]

const buttonsRow = [
activeTab?.canOpenInsight && newInsightUrl ? (
<LemonButton
Expand Down Expand Up @@ -223,6 +233,10 @@ export const WebTabs = ({
) : null,
].filter(isNotNil)

const isVisualizationToggleEnabled =
featureFlags[FEATURE_FLAGS.WEB_TRENDS_BREAKDOWN] &&
[TileId.SOURCES, TileId.DEVICES, TileId.PATHS].includes(tileId)

return (
<div className={clsx(className, 'flex flex-col')}>
<div className="flex flex-row items-center self-stretch mb-3">
Expand All @@ -237,6 +251,27 @@ export const WebTabs = ({
)}
</h2>

{isVisualizationToggleEnabled && (
<LemonSegmentedButton
value={visualization || 'table'}
onChange={(value) => setTileVisualization(tileId, value as 'table' | 'graph')}
options={[
{
value: 'table',
label: '',
icon: <IconTableChart />,
},
{
value: 'graph',
label: '',
icon: <IconLineGraph />,
},
]}
size="small"
className="mr-2"
/>
)}

<LemonSegmentedSelect
shrinkOn={7}
size="small"
Expand Down
1 change: 1 addition & 0 deletions frontend/src/scenes/web-analytics/WebAnalyticsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
74 changes: 11 additions & 63 deletions frontend/src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -330,61 +335,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: {
Expand Down Expand Up @@ -537,10 +487,10 @@ export const WebStatsTableTile = ({
query,
breakdownBy,
insightProps,
control,
}: QueryWithInsightProps<DataTableNode> & {
breakdownBy: WebStatsBreakdown
control?: JSX.Element
tileId: TileId
}): JSX.Element => {
const { togglePropertyFilter } = useActions(webAnalyticsLogic)

Expand Down Expand Up @@ -585,12 +535,7 @@ export const WebStatsTableTile = ({
}
}, [onClick, insightProps])

return (
<div className="border rounded bg-surface-primary flex-1 flex flex-col">
{control != null && <div className="flex flex-row items-center justify-end m-2 mr-4">{control}</div>}
<Query query={query} readOnly={true} context={context} />
</div>
)
return <Query query={query} readOnly={true} context={context} />
}

const getBreakdownValue = (record: unknown, breakdownBy: WebStatsBreakdown): string | null | undefined => {
Expand Down Expand Up @@ -726,9 +671,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 (
Expand All @@ -737,6 +684,7 @@ export const WebQuery = ({
breakdownBy={query.source.breakdownBy}
insightProps={insightProps}
control={control}
tileId={tileId}
/>
)
}
Expand Down
Loading