Skip to content

Commit

Permalink
feat(web-analytics): Add a error tracking tile to web analytics (#24273)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
robbie-c and github-actions[bot] authored Aug 9, 2024
1 parent 1c58b49 commit 037509e
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 10 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 12 additions & 1 deletion frontend/src/queries/nodes/DataTable/dataTableLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,18 @@ export const dataTableLogic = kea<dataTableLogicType>([
})),
selectors({
sourceKind: [(_, p) => [p.query], (query): NodeKind | null => query.source?.kind],
sourceFeatures: [(_, p) => [p.query], (query): Set<QueryFeature> => getQueryFeatures(query.source)],
sourceFeatures: [
(_, p) => [p.query, (_, props) => props.context],
(query, context): Set<QueryFeature> => {
const sourceFeatures = getQueryFeatures(query.source)
if (context?.extraDataTableQueryFeatures) {
for (const feature of context.extraDataTableQueryFeatures) {
sourceFeatures.add(feature)
}
}
return sourceFeatures
},
],
orderBy: [
(s, p) => [p.query, s.sourceFeatures],
(query, sourceFeatures): string[] | null =>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/queries/nodes/DataTable/renderColumnMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export function renderColumnMeta(key: string, query: DataTableNode, context?: Qu
let align: ColumnMeta['align']

const queryContextColumnName = key.startsWith('context.columns.') ? trimQuotes(key.substring(16)) : undefined
const queryContextColumn = queryContextColumnName ? context?.columns?.[queryContextColumnName] : undefined
const queryContextColumn =
(queryContextColumnName ? context?.columns?.[queryContextColumnName] : undefined) ?? context?.columns?.[key]

if (queryContextColumnName && queryContextColumn && (queryContextColumn.title || queryContextColumn.renderTitle)) {
const Component = queryContextColumn.renderTitle
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/scenes/error-tracking/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,19 @@ export const errorTrackingQuery = ({
filterTestAccounts,
filterGroup,
sparklineSelectedPeriod,
columns,
}: {
order: ErrorTrackingQuery['order']
dateRange: DateRange
filterTestAccounts: boolean
filterGroup: UniversalFiltersGroup
sparklineSelectedPeriod: string | null
columns?: ('error' | 'volume' | 'occurrences' | 'sessions' | 'users' | 'assignee')[]
}): DataTableNode => {
const select: string[] = []

const columns = ['error', 'occurrences', 'sessions', 'users', 'assignee']
if (!columns) {
columns = ['error', 'occurrences', 'sessions', 'users', 'assignee']
}

if (sparklineSelectedPeriod) {
const { value, displayAs, offsetHours } = parseSparklineSelection(sparklineSelectedPeriod)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/scenes/web-analytics/WebAnalyticsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { IconOpenInNew } from 'lib/lemon-ui/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonModal } from 'lib/lemon-ui/LemonModal'
import { urls } from 'scenes/urls'
import { WebQuery } from 'scenes/web-analytics/tiles/WebAnalyticsTile'
import { webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic'
import { WebQuery } from 'scenes/web-analytics/WebAnalyticsTile'
import { WebPropertyFilters } from 'scenes/web-analytics/WebPropertyFilters'

export const WebAnalyticsModal = (): JSX.Element | null => {
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/scenes/web-analytics/WebDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import { PostHogComDocsURL } from 'lib/lemon-ui/Link/Link'
import { Popover } from 'lib/lemon-ui/Popover'
import { isNotNil } from 'lib/utils'
import React, { useState } from 'react'
import { WebAnalyticsErrorTrackingTile } from 'scenes/web-analytics/tiles/WebAnalyticsErrorTracking'
import { WebAnalyticsRecordingsTile } from 'scenes/web-analytics/tiles/WebAnalyticsRecordings'
import { WebQuery } from 'scenes/web-analytics/tiles/WebAnalyticsTile'
import { WebAnalyticsHealthCheck } from 'scenes/web-analytics/WebAnalyticsHealthCheck'
import {
QueryTile,
Expand All @@ -21,8 +24,6 @@ import {
webAnalyticsLogic,
} from 'scenes/web-analytics/webAnalyticsLogic'
import { WebAnalyticsModal } from 'scenes/web-analytics/WebAnalyticsModal'
import { WebAnalyticsRecordingsTile } from 'scenes/web-analytics/WebAnalyticsRecordings'
import { WebQuery } from 'scenes/web-analytics/WebAnalyticsTile'
import { WebPropertyFilters } from 'scenes/web-analytics/WebPropertyFilters'

import { navigationLogic } from '~/layout/navigation/navigationLogic'
Expand Down Expand Up @@ -72,6 +73,8 @@ const Tiles = (): JSX.Element => {
return <TabsTileItem key={i} tile={tile} />
} else if (tile.kind === 'replay') {
return <WebAnalyticsRecordingsTile key={i} tile={tile} />
} else if (tile.kind === 'error_tracking') {
return <WebAnalyticsErrorTrackingTile key={i} tile={tile} />
}
return null
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import clsx from 'clsx'
import { TZLabel } from 'lib/components/TZLabel'
import { IconOpenInNew } from 'lib/lemon-ui/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonTableLink } from 'lib/lemon-ui/LemonTable/LemonTableLink'
import { urls } from 'scenes/urls'
import { ErrorTrackingTile } from 'scenes/web-analytics/webAnalyticsLogic'

import { QueryFeature } from '~/queries/nodes/DataTable/queryFeatures'
import { Query } from '~/queries/Query/Query'
import { ErrorTrackingGroup } from '~/queries/schema'
import { QueryContext, QueryContextColumnComponent } from '~/queries/types'

export const CustomGroupTitleColumn: QueryContextColumnComponent = (props) => {
const record = props.record as ErrorTrackingGroup

return (
<div className="flex items-start space-x-1.5 group">
<LemonTableLink
title={record.exception_type || record.fingerprint}
description={
<div className="space-y-1">
<div className="line-clamp-1">{record.description}</div>
<div className="space-x-1">
<TZLabel time={record.last_seen} className="border-dotted border-b" />
</div>
</div>
}
className="flex-1"
to={urls.errorTrackingGroup(record.fingerprint)}
/>
</div>
)
}

const context: QueryContext = {
extraDataTableQueryFeatures: [QueryFeature.hideLoadNextButton],
showOpenEditorButton: false,
showQueryEditor: false,
columns: {
error: {
width: '50%',
render: CustomGroupTitleColumn,
},
users: {
align: 'right',
},
occurrences: {
align: 'right',
},
},
}

export const WebAnalyticsErrorTrackingTile = ({ tile }: { tile: ErrorTrackingTile }): JSX.Element => {
const { layout, query } = tile
const to = urls.errorTracking()

return (
<div
className={clsx(
'col-span-1 row-span-1 flex flex-col',
layout.colSpanClassName ?? 'md:col-span-6',
layout.rowSpanClassName ?? 'md:row-span-1',
layout.orderWhenLargeClassName ?? 'xxl:order-12',
layout.className
)}
>
<h2 className="m-0 mb-3">Error tracking</h2>
<div className="border rounded bg-bg-light flex-1 flex flex-col py-2 px-1">
<Query query={query} embedded={true} context={context} />
</div>
<div className="flex flex-row-reverse my-2">
<LemonButton to={to} icon={<IconOpenInNew />} size="small" type="secondary">
View all
</LemonButton>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IconRewindPlay } from '@posthog/icons'
import clsx from 'clsx'
import { useValues } from 'kea'
import { EmptyMessage } from 'lib/components/EmptyMessage/EmptyMessage'
import { IconOpenInNew } from 'lib/lemon-ui/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton'
import { RecordingRow } from 'scenes/project-homepage/RecentRecordings'
Expand Down Expand Up @@ -73,7 +73,7 @@ export function WebAnalyticsRecordingsTile({ tile }: { tile: ReplayTile }): JSX.
)}
</div>
<div className="flex flex-row-reverse my-2">
<LemonButton to={to} icon={<IconRewindPlay />} size="small" type="secondary">
<LemonButton to={to} icon={<IconOpenInNew />} size="small" type="secondary">
View all
</LemonButton>
</div>
Expand Down
29 changes: 28 additions & 1 deletion frontend/src/scenes/web-analytics/webAnalyticsLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { dayjs } from 'lib/dayjs'
import { Link, PostHogComDocsURL } from 'lib/lemon-ui/Link/Link'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { getDefaultInterval, isNotNil, updateDatesWithInterval } from 'lib/utils'
import { errorTrackingQuery } from 'scenes/error-tracking/queries'
import { urls } from 'scenes/urls'

import {
Expand Down Expand Up @@ -57,6 +58,7 @@ export enum TileId {
GEOGRAPHY = 'GEOGRAPHY',
RETENTION = 'RETENTION',
REPLAY = 'REPLAY',
ERROR_TRACKING = 'ERROR_TRACKING',
}

const loadPriorityMap: Record<TileId, number> = {
Expand All @@ -68,6 +70,7 @@ const loadPriorityMap: Record<TileId, number> = {
[TileId.GEOGRAPHY]: 6,
[TileId.RETENTION]: 7,
[TileId.REPLAY]: 8,
[TileId.ERROR_TRACKING]: 9,
}

interface BaseTile {
Expand Down Expand Up @@ -116,7 +119,12 @@ export interface ReplayTile extends BaseTile {
kind: 'replay'
}

export type WebDashboardTile = QueryTile | TabsTile | ReplayTile
export interface ErrorTrackingTile extends BaseTile {
kind: 'error_tracking'
query: QuerySchema
}

export type WebDashboardTile = QueryTile | TabsTile | ReplayTile | ErrorTrackingTile

export interface WebDashboardModalQuery {
tileId: TileId
Expand Down Expand Up @@ -441,6 +449,7 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
tiles: [
(s) => [
s.webAnalyticsFilters,
s.replayFilters,
s.tabs,
s.dateFilter,
s.isPathCleaningEnabled,
Expand All @@ -452,6 +461,7 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
],
(
webAnalyticsFilters,
replayFilters,
{ graphsTab, sourceTab, deviceTab, pathTab, geographyTab },
{ dateFrom, dateTo, interval },
isPathCleaningEnabled,
Expand Down Expand Up @@ -1132,6 +1142,23 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
},
}
: null,
featureFlags[FEATURE_FLAGS.ERROR_TRACKING]
? {
kind: 'error_tracking',
tileId: TileId.ERROR_TRACKING,
layout: {
colSpanClassName: 'md:col-span-1',
},
query: errorTrackingQuery({
order: 'users',
dateRange: dateRange,
filterTestAccounts: filterTestAccounts,
filterGroup: replayFilters.filter_group,
sparklineSelectedPeriod: null,
columns: ['error', 'users', 'occurrences'],
}),
}
: null,
]
return allTiles.filter(isNotNil)
},
Expand Down

0 comments on commit 037509e

Please sign in to comment.