diff --git a/assets/js/dashboard/dogfood.ts b/assets/js/dashboard/dogfood.ts new file mode 100644 index 000000000000..e6a5aa72d6e6 --- /dev/null +++ b/assets/js/dashboard/dogfood.ts @@ -0,0 +1,15 @@ +type PlausibleTracker = ( + event: string, + options?: { props?: Record } +) => void + +export function trackEvent( + event: string, + props?: Record +): void { + const plausible = (window as unknown as { plausible?: PlausibleTracker }) + .plausible + if (typeof plausible === 'function') { + plausible(event, props ? { props } : undefined) + } +} diff --git a/assets/js/dashboard/extra/exploration/index.tsx b/assets/js/dashboard/extra/exploration/index.tsx index 131fcf01e226..f0ee0403d676 100644 --- a/assets/js/dashboard/extra/exploration/index.tsx +++ b/assets/js/dashboard/extra/exploration/index.tsx @@ -14,6 +14,7 @@ import { ExplorationColumn, MaxDepthColumn } from './exploration-column' import { useExplorationData } from './exploration-state' import { DIRECTION, MIN_GRID_COLUMNS, ExplorationDirection } from './constants' import { getSelectedSuggestion } from './journey' +import { trackEvent } from '../../dogfood' // Column header label based on index and direction. function columnHeader(index: number, direction: ExplorationDirection): string { @@ -24,21 +25,10 @@ function columnHeader(index: number, direction: ExplorationDirection): string { return `${index} step${index === 1 ? '' : 's'} ${word}` } -type PlausibleTracker = ( - event: string, - options?: { props?: Record } -) => void - // Fires a custom event when the user picks an entry in the first column of the // Explore view, i.e. anchors a new journey from either a starting or end point. function trackExploreEntrySelected(direction: ExplorationDirection): void { - const plausible = (window as unknown as { plausible?: PlausibleTracker }) - .plausible - if (typeof plausible === 'function') { - plausible('Explore entry selected', { - props: { journey_direction: direction } - }) - } + trackEvent('Explore entry selected', { journey_direction: direction }) } // Scrolls the active column into view whenever the journey length changes. diff --git a/assets/js/dashboard/stats/csv-export/csv-export.tsx b/assets/js/dashboard/stats/csv-export/csv-export.tsx index f0d185361561..b246b5684c6b 100644 --- a/assets/js/dashboard/stats/csv-export/csv-export.tsx +++ b/assets/js/dashboard/stats/csv-export/csv-export.tsx @@ -12,6 +12,9 @@ import { useGraphIntervalContext } from '../graph/graph-interval-context' import { createCsvExportRequestBody } from './csv-export-body' import * as api from '../../api' import { DateRange } from '../../stats-query' +import { DashboardState } from '../../dashboard-state' +import { hasConversionGoalFilter, hasPageFilter } from '../../util/filters' +import { trackEvent } from '../../dogfood' import { Tooltip } from '../../util/tooltip' import { useBodyPortalRef } from '../breakdowns' @@ -21,6 +24,27 @@ export enum ExportStatus { error = 'error' } +function durationBucket(ms: number): string { + const s = ms / 1000 + if (s >= 20) return '20s+' + const floor = Math.floor(s / 2) * 2 + return `${floor}-${floor + 2}s` +} + +function dogfoodTrackCsvExport( + isSuccess: boolean, + dashboardState: DashboardState, + startedAt: number +): void { + trackEvent('csv_export', { + is_success: String(isSuccess), + goal_filter: String(hasConversionGoalFilter(dashboardState)), + page_filter: String(hasPageFilter(dashboardState)), + period: dashboardState.period, + duration_bucket: durationBucket(performance.now() - startedAt) + }) +} + export function CsvExport({ exportStatus, setExportStatus @@ -37,6 +61,7 @@ export function CsvExport({ const startExport = async () => { if (!canStartExport) return setExportStatus(ExportStatus.exporting) + const startedAt = performance.now() try { const body = createCsvExportRequestBody(dashboardState, selectedInterval) @@ -50,8 +75,10 @@ export function CsvExport({ document.body.removeChild(a) URL.revokeObjectURL(url) setExportStatus(ExportStatus.idle) + dogfoodTrackCsvExport(true, dashboardState, startedAt) } catch { setExportStatus(ExportStatus.error) + dogfoodTrackCsvExport(false, dashboardState, startedAt) } }