Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion src/analytics/posthog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ export const initPostHog = () => {
capture_pageview: false,
capture_pageleave: true,
capture_exceptions: true,
session_recording: {
maskInputFn: (text, element) => {
const el = element as HTMLInputElement | undefined
if (el?.type === 'password') {
return '*'.repeat(text.length)
}
if (
el?.hasAttribute?.('data-posthog-unmask-search') ||
el?.closest?.('[data-posthog-unmask-search]')
) {
return text
}
return '*'.repeat(text.length)
},
},
})

// Tag every event with the environment so staging visits are
Expand All @@ -28,12 +43,48 @@ export const initPostHog = () => {
initialized = true
}

export const capturePostHogPageview = (path: string) => {
/**
* Optional properties for well detail pages so `well_id` is on `$pageview`
* (and shows up in PostHog when breaking down or filtering).
*/
export const wellDetailPageviewProps = (
pathname: string
):
| {
well_id: string
page_template: 'well_detail'
well_detail_area: 'ocotillo' | 'amp'
}
| undefined => {
const ocotillo = pathname.match(/^\/ocotillo\/well\/show\/([^/]+)\/?$/)
if (ocotillo) {
return {
well_id: ocotillo[1],
page_template: 'well_detail',
well_detail_area: 'ocotillo',
}
}
const amp = pathname.match(/^\/amp\/wells\/show\/([^/]+)\/?$/)
if (amp) {
return {
well_id: amp[1],
page_template: 'well_detail',
well_detail_area: 'amp',
}
}
return undefined
}

export const capturePostHogPageview = (
path: string,
extras?: Record<string, unknown>
) => {
if (!isEnabled || !initialized) return

posthog.capture('$pageview', {
$current_url: window.location.href,
path,
...(extras ?? {}),
})
}

Expand Down
5 changes: 4 additions & 1 deletion src/components/SearchModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ export const SearchModal = ({ open, onClose }: SearchModalProps) => {
placeholder="Search"
fullWidth
sx={{ fontSize: 15 }}
inputProps={{ 'aria-label': 'Search' }}
inputProps={{
'aria-label': 'Search',
'data-posthog-unmask-search': true,
}}
endAdornment={
state.query ? (
<InputAdornment position="end">
Expand Down
9 changes: 7 additions & 2 deletions src/components/analytics/PostHogPageview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useEffect, useRef } from 'react'
import { useLocation } from 'react-router'
import { capturePostHogPageview, initPostHog } from '@/analytics/posthog'
import {
capturePostHogPageview,
initPostHog,
wellDetailPageviewProps,
} from '@/analytics/posthog'

export const PostHogPageview = () => {
const location = useLocation()
Expand All @@ -15,7 +19,8 @@ export const PostHogPageview = () => {

if (lastPathRef.current === path) return

capturePostHogPageview(path)
const wellDetail = wellDetailPageviewProps(location.pathname)
capturePostHogPageview(path, wellDetail)
lastPathRef.current = path
}, [location.hash, location.pathname, location.search])

Expand Down
63 changes: 63 additions & 0 deletions src/hooks/useSearchModalState.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useMemo, useRef, useState } from 'react'
import { useGo } from '@refinedev/core'
import { captureEvent } from '@/analytics/posthog'
import { GroupType } from '@/constants'
import { useAbortableList } from './useAbortableList'
import { useDebounce } from './useDebounce'
Expand Down Expand Up @@ -109,6 +110,68 @@ export const useSearchModalState = ({
return searchDocs(parsed.term)
}, [parsed])

/** Avoid duplicate PostHog emissions when the debounced query or outcome repeats. */
const defaultSearchEmittedKey = useRef<string | null>(null)
useEffect(() => {
defaultSearchEmittedKey.current = null
}, [debounced])

/**
* PostHog: command palette API search (wells, contacts, assets).
* One event per completed search for a given debounced query string.
*/
useEffect(() => {
if (!open || parsed.mode !== 'default') return
const q = debounced.trim()
if (!q) return
if (searchQuery.isFetching) return

const key = `${q}|${searchQuery.isError ? 1 : 0}|${results.length}`
if (defaultSearchEmittedKey.current === key) return
defaultSearchEmittedKey.current = key

captureEvent('global_search', {
search_mode: 'default',
query: q,
result_count: results.length,
has_results: results.length > 0,
had_error: searchQuery.isError,
})
}, [
debounced,
open,
parsed.mode,
results.length,
searchQuery.isError,
searchQuery.isFetching,
])

const docsSearchEmittedKey = useRef<string | null>(null)
useEffect(() => {
docsSearchEmittedKey.current = null
}, [parsed.term])

/**
* PostHog: local docs search (!docs …).
*/
useEffect(() => {
if (!open || parsed.mode !== 'docs') return
const term = parsed.term.trim()
if (!term) return

const key = `${term}|${docsResults.length}`
if (docsSearchEmittedKey.current === key) return
docsSearchEmittedKey.current = key

captureEvent('global_search', {
search_mode: 'docs',
query: term,
result_count: docsResults.length,
has_results: docsResults.length > 0,
had_error: false,
})
}, [docsResults.length, open, parsed.mode, parsed.term])

const navigateToResult = (option: SearchResult) => {
switch (option.group) {
case GroupType.Wells:
Expand Down
6 changes: 5 additions & 1 deletion src/pages/ocotillo/thing/well-show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ export const WellShow = () => {

useEffect(() => {
if (id)
captureEvent('feature_used', { feature: 'well_detail', well_id: id })
captureEvent('feature_used', {
feature: 'well_detail',
well_id: id,
well_detail_area: 'ocotillo',
})
}, [id])

const detailsQuery = useQuery({
Expand Down
Loading