Skip to content

Commit

Permalink
feat(web-analytics): Add web analytics tabs (#17981)
Browse files Browse the repository at this point in the history
* Add web analytics tabs

* Add stats table query runner and use it for pathname

* Add stats query runner, replacing page and source

* Remove classes for now-unused queries

* Add remaining breakdowns

* Prefer unknown to any

* Remove unused ctes

* Fix typing issues

* Fix python typing

* Switch to hogql filters for set_once properties

* Use cpp parser

* Move web analytics to the right navbar section

* Fix typing issues
  • Loading branch information
robbie-c authored and daibhin committed Oct 23, 2023
1 parent edb1077 commit d6d0d29
Show file tree
Hide file tree
Showing 21 changed files with 750 additions and 476 deletions.
17 changes: 8 additions & 9 deletions frontend/src/layout/navigation/SideBar/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ function Pages(): JSX.Element {
onClick: hideSideBarMobile,
}}
/>
<FlaggedFeature flag={FEATURE_FLAGS.WEB_ANALYTICS}>
<PageButton
icon={<IconWeb />}
identifier={Scene.WebAnalytics}
to={urls.webAnalytics()}
highlight="alpha"
/>
</FlaggedFeature>
<PageButton icon={<IconRecording />} identifier={Scene.Replay} to={urls.replay()} />

<div className="SideBar__heading">Feature Management</div>
Expand All @@ -200,15 +208,6 @@ function Pages(): JSX.Element {
to={urls.surveys()}
highlight="beta"
/>

<FlaggedFeature flag={FEATURE_FLAGS.WEB_ANALYTICS}>
<PageButton
icon={<IconWeb />}
identifier={Scene.WebAnalytics}
to={urls.webAnalytics()}
highlight="alpha"
/>
</FlaggedFeature>
<div className="SideBar__heading">Data</div>

<PageButton
Expand Down
48 changes: 48 additions & 0 deletions frontend/src/lib/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1603,6 +1603,54 @@ export function isNotNil<T>(arg: T): arg is Exclude<T, null | undefined> {
return arg !== null && arg !== undefined
}

/** An error signaling that a value of type `never` in TypeScript was used unexpectedly at runtime.
*
* Useful for type-narrowing, will give a compile-time error if the type of x is not `never`.
* See the example below where it catches a missing branch at compile-time.
*
* @example
*
* enum MyEnum {
* a,
* b,
* }
*
* function handleEnum(x: MyEnum) {
* switch (x) {
* case MyEnum.a:
* return
* // missing branch
* default:
* throw new UnexpectedNeverError(x) // TS2345: Argument of type MyEnum is not assignable to parameter of type never
* }
* }
*
* function handleEnum(x: MyEnum) {
* switch (x) {
* case MyEnum.a:
* return
* case MyEnum.b:
* return
* default:
* throw new UnexpectedNeverError(x) // no type error
* }
* }
*
*/
export class UnexpectedNeverError extends Error {
constructor(x: never, message?: string) {
message = message ?? 'Unexpected never: ' + String(x)
super(message)

// restore prototype chain, which is broken by Error
// see https://stackoverflow.com/questions/41102060/typescript-extending-error-class
const actualProto = new.target.prototype
if (Object.setPrototypeOf) {
Object.setPrototypeOf(this, actualProto)
}
}
}

export function calculateDays(timeValue: number, timeUnit: TimeUnitType): number {
if (timeUnit === TimeUnitType.Year) {
return timeValue * 365
Expand Down
10 changes: 2 additions & 8 deletions frontend/src/queries/nodes/DataTable/queryFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import {
isPersonsNode,
isPersonsQuery,
isWebOverviewStatsQuery,
isWebStatsTableQuery,
isWebTopClicksQuery,
isWebTopPagesQuery,
isWebTopSourcesQuery,
} from '~/queries/utils'
import { Node } from '~/queries/schema'

Expand Down Expand Up @@ -55,12 +54,7 @@ export function getQueryFeatures(query: Node): Set<QueryFeature> {
}
}

if (
isWebOverviewStatsQuery(query) ||
isWebTopSourcesQuery(query) ||
isWebTopPagesQuery(query) ||
isWebTopClicksQuery(query)
) {
if (isWebOverviewStatsQuery(query) || isWebTopClicksQuery(query) || isWebStatsTableQuery(query)) {
features.add(QueryFeature.columnsInResponse)
features.add(QueryFeature.resultIsArrayOfArrays)
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/queries/nodes/DataTable/renderColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export function renderColumn(
} else if (key.startsWith('context.columns.')) {
const columnName = trimQuotes(key.substring(16)) // 16 = "context.columns.".length
const Component = context?.columns?.[columnName]?.render
return Component ? <Component record={record} columnName={columnName} value={value} /> : ''
return Component ? <Component record={record} columnName={columnName} value={value} query={query} /> : ''
} else if (key === 'id' && (isPersonsNode(query.source) || isPersonsQuery(query.source))) {
return (
<CopyToClipboardInline
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/queries/nodes/DataTable/renderColumnMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ export function renderColumnMeta(key: string, query: DataTableNode, context?: Qu
title = <PropertyKeyInfo value={trimQuotes(key.substring(11))} type={PropertyFilterType.Event} disableIcon />
} else if (key.startsWith('context.columns.')) {
const column = trimQuotes(key.substring(16))
title = context?.columns?.[column]?.title ?? column.replace('_', ' ')
const queryContextColumn = context?.columns?.[column]
const Component = queryContextColumn?.renderTitle
title = Component ? (
<Component columnName={column} query={query} />
) : (
queryContextColumn?.title ?? column.replace('_', ' ')
)
} else if (key === 'person.$delete') {
title = ''
width = 0
Expand Down
106 changes: 33 additions & 73 deletions frontend/src/queries/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,10 @@
"$ref": "#/definitions/WebOverviewStatsQuery"
},
{
"$ref": "#/definitions/WebTopSourcesQuery"
"$ref": "#/definitions/WebStatsTableQuery"
},
{
"$ref": "#/definitions/WebTopClicksQuery"
},
{
"$ref": "#/definitions/WebTopPagesQuery"
}
]
},
Expand Down Expand Up @@ -412,13 +409,10 @@
"$ref": "#/definitions/WebOverviewStatsQuery"
},
{
"$ref": "#/definitions/WebTopSourcesQuery"
"$ref": "#/definitions/WebStatsTableQuery"
},
{
"$ref": "#/definitions/WebTopClicksQuery"
},
{
"$ref": "#/definitions/WebTopPagesQuery"
}
],
"description": "Source of the events"
Expand Down Expand Up @@ -2422,7 +2416,14 @@
},
"WebAnalyticsPropertyFilters": {
"items": {
"$ref": "#/definitions/EventPropertyFilter"
"anyOf": [
{
"$ref": "#/definitions/EventPropertyFilter"
},
{
"$ref": "#/definitions/HogQLPropertyFilter"
}
]
},
"type": "array"
},
Expand Down Expand Up @@ -2483,84 +2484,43 @@
"required": ["results"],
"type": "object"
},
"WebTopClicksQuery": {
"additionalProperties": false,
"properties": {
"dateRange": {
"$ref": "#/definitions/DateRange"
},
"kind": {
"const": "WebTopClicksQuery",
"type": "string"
},
"properties": {
"$ref": "#/definitions/WebAnalyticsPropertyFilters"
},
"response": {
"$ref": "#/definitions/WebTopClicksQueryResponse"
}
},
"required": ["kind", "properties"],
"type": "object"
"WebStatsBreakdown": {
"enum": [
"Page",
"InitialPage",
"InitialReferringDomain",
"InitialUTMSource",
"InitialUTMCampaign",
"Browser",
"OS",
"DeviceType"
],
"type": "string"
},
"WebTopClicksQueryResponse": {
"WebStatsTableQuery": {
"additionalProperties": false,
"properties": {
"columns": {
"items": {},
"type": "array"
},
"hogql": {
"type": "string"
"breakdownBy": {
"$ref": "#/definitions/WebStatsBreakdown"
},
"is_cached": {
"type": "boolean"
},
"last_refresh": {
"type": "string"
},
"next_allowed_client_refresh": {
"type": "string"
},
"results": {
"items": {},
"type": "array"
},
"timings": {
"items": {
"$ref": "#/definitions/QueryTiming"
},
"type": "array"
},
"types": {
"items": {},
"type": "array"
}
},
"required": ["results"],
"type": "object"
},
"WebTopPagesQuery": {
"additionalProperties": false,
"properties": {
"dateRange": {
"$ref": "#/definitions/DateRange"
},
"kind": {
"const": "WebTopPagesQuery",
"const": "WebStatsTableQuery",
"type": "string"
},
"properties": {
"$ref": "#/definitions/WebAnalyticsPropertyFilters"
},
"response": {
"$ref": "#/definitions/WebTopPagesQueryResponse"
"$ref": "#/definitions/WebStatsTableQueryResponse"
}
},
"required": ["kind", "properties"],
"required": ["kind", "properties", "breakdownBy"],
"type": "object"
},
"WebTopPagesQueryResponse": {
"WebStatsTableQueryResponse": {
"additionalProperties": false,
"properties": {
"columns": {
Expand Down Expand Up @@ -2597,27 +2557,27 @@
"required": ["results"],
"type": "object"
},
"WebTopSourcesQuery": {
"WebTopClicksQuery": {
"additionalProperties": false,
"properties": {
"dateRange": {
"$ref": "#/definitions/DateRange"
},
"kind": {
"const": "WebTopSourcesQuery",
"const": "WebTopClicksQuery",
"type": "string"
},
"properties": {
"$ref": "#/definitions/WebAnalyticsPropertyFilters"
},
"response": {
"$ref": "#/definitions/WebTopSourcesQueryResponse"
"$ref": "#/definitions/WebTopClicksQueryResponse"
}
},
"required": ["kind", "properties"],
"type": "object"
},
"WebTopSourcesQueryResponse": {
"WebTopClicksQueryResponse": {
"additionalProperties": false,
"properties": {
"columns": {
Expand Down
Loading

0 comments on commit d6d0d29

Please sign in to comment.