Skip to content

Commit cafae74

Browse files
feat(web-analytics): Add new Web Analytics onboarding flow (#42549)
1 parent fc871a1 commit cafae74

File tree

5 files changed

+116
-105
lines changed

5 files changed

+116
-105
lines changed

common/tailwind/tailwind.config.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,18 @@ const config = {
617617
animation: {
618618
// Default pulse animation but run for only 5 iterations
619619
'pulse-5': 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) 5',
620+
// Pulsing glow effect for highlighting UI elements
621+
'pulse-glow': 'pulse-glow 2s ease-in-out infinite',
622+
},
623+
keyframes: {
624+
'pulse-glow': {
625+
'0%, 100%': {
626+
boxShadow: '0 0 2px 1px var(--color-accent)',
627+
},
628+
'50%': {
629+
boxShadow: '0 0 6px 2px var(--color-accent)',
630+
},
631+
},
620632
},
621633
colors: {
622634
...deprecatedColors,

frontend/src/queries/nodes/OverviewGrid/OverviewGrid.tsx

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import clsx from 'clsx'
22
import { useValues } from 'kea'
33

4-
import { IconTrending } from '@posthog/icons'
5-
import { LemonBanner, LemonSkeleton } from '@posthog/lemon-ui'
4+
import { IconTrending, IconWarning } from '@posthog/icons'
5+
import { LemonBanner, LemonSkeleton, Link } from '@posthog/lemon-ui'
66

77
import { getColorVar } from 'lib/colors'
88
import { PreAggregatedBadge } from 'lib/components/PreAggregatedBadge'
@@ -31,6 +31,8 @@ export interface OverviewItem {
3131
changeFromPreviousPct?: number | undefined
3232
kind: WebAnalyticsItemKind
3333
isIncreaseBad?: boolean
34+
warning?: string
35+
warningLink?: string
3436
}
3537

3638
export interface SamplingRate {
@@ -185,10 +187,27 @@ const OverviewItemCell = ({
185187
)}
186188
>
187189
{usedPreAggregatedTables && <PreAggregatedBadge />}
188-
<div className="flex flex-row w-full justify-center">
189-
<div className={`uppercase py-0.5 ${compact ? 'text-[10px]' : 'text-xs font-bold'}`}>
190-
{label}&nbsp;
191-
</div>
190+
<div className="flex flex-row w-full justify-center items-center gap-1">
191+
<div className={`uppercase py-0.5 ${compact ? 'text-[10px]' : 'text-xs font-bold'}`}>{label}</div>
192+
{item.warning && (
193+
<Tooltip
194+
title={
195+
<div>
196+
{item.warning}
197+
{item.warningLink && (
198+
<>
199+
{' '}
200+
<Link to={item.warningLink} className="text-link">
201+
Learn more
202+
</Link>
203+
</>
204+
)}
205+
</div>
206+
}
207+
>
208+
<IconWarning className="text-warning h-3.5 w-3.5 cursor-pointer" />
209+
</Tooltip>
210+
)}
192211
</div>
193212
<div className="w-full flex-1 flex items-center justify-center">
194213
<div className={compact ? 'text-lg' : 'text-2xl'}>

frontend/src/queries/nodes/WebOverview/WebOverview.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { BuiltLogic, LogicWrapper, useValues } from 'kea'
22
import { useState } from 'react'
33

4+
import { reverseProxyCheckerLogic } from 'lib/components/ReverseProxyChecker/reverseProxyCheckerLogic'
5+
import { FEATURE_FLAGS } from 'lib/constants'
46
import { useFeatureFlag } from 'lib/hooks/useFeatureFlag'
7+
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
58
import { useAttachedLogic } from 'lib/logic/scenes/useAttachedLogic'
9+
import { capitalizeFirstLetter } from 'lib/utils'
610

711
import { OverviewGrid, OverviewItem } from '~/queries/nodes/OverviewGrid/OverviewGrid'
812
import { AnyResponseType, WebOverviewQuery, WebOverviewQueryResponse } from '~/queries/schema/schema-general'
@@ -20,6 +24,7 @@ export function WebOverview(props: {
2024
uniqueKey?: string | number
2125
}): JSX.Element | null {
2226
const { onData, loadPriority, dataNodeCollectionId } = props.context.insightProps ?? {}
27+
const { featureFlags } = useValues(featureFlagLogic)
2328
const [_key] = useState(() => `WebOverview.${uniqueNode++}`)
2429
const key = props.uniqueKey ? String(props.uniqueKey) : _key
2530
const logic = dataNodeLogic({
@@ -33,6 +38,8 @@ export function WebOverview(props: {
3338
const { response, responseLoading } = useValues(logic)
3439
useAttachedLogic(logic, props.attachTo)
3540

41+
const { hasReverseProxy } = useValues(reverseProxyCheckerLogic)
42+
3643
const webOverviewQueryResponse = response as WebOverviewQueryResponse | undefined
3744

3845
const samplingRate = webOverviewQueryResponse?.samplingRate
@@ -46,6 +53,8 @@ export function WebOverview(props: {
4653
'usedPreAggregatedTables' in response &&
4754
response.usedPreAggregatedTables
4855

56+
const showWarning = hasReverseProxy === false && !!featureFlags[FEATURE_FLAGS.WEB_ANALYTICS_EMPTY_ONBOARDING]
57+
4958
// Convert WebOverviewItem to OverviewItem
5059
const overviewItems: OverviewItem[] =
5160
webOverviewQueryResponse?.results?.map((item) => ({
@@ -55,6 +64,10 @@ export function WebOverview(props: {
5564
changeFromPreviousPct: item.changeFromPreviousPct,
5665
kind: item.kind,
5766
isIncreaseBad: item.isIncreaseBad,
67+
warning: showWarning
68+
? `${capitalizeFirstLetter(item.key)} counts may be underreported. Set up a reverse proxy so that events are less likely to be intercepted by tracking blockers.`
69+
: undefined,
70+
warningLink: showWarning ? 'https://posthog.com/docs/advanced/proxy' : undefined,
5871
})) || []
5972

6073
return (

frontend/src/scenes/web-analytics/WebAnalyticsDashboard.tsx

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ import { webAnalyticsModalLogic } from './webAnalyticsModalLogic'
5656

5757
export const Tiles = (props: { tiles?: WebAnalyticsTile[]; compact?: boolean }): JSX.Element => {
5858
const { tiles: tilesFromProps, compact = false } = props
59-
const { tiles: tilesFromLogic } = useValues(webAnalyticsLogic)
59+
const { tiles: tilesFromLogic, productTab } = useValues(webAnalyticsLogic)
6060
const { currentTeam, currentTeamLoading } = useValues(teamLogic)
6161
const tiles = tilesFromProps ?? tilesFromLogic
6262
const { featureFlags } = useValues(featureFlagLogic)
6363

64-
const emptyOnboardingContent = getEmptyOnboardingContent(featureFlags, currentTeamLoading, currentTeam)
64+
const emptyOnboardingContent = getEmptyOnboardingContent(featureFlags, currentTeamLoading, currentTeam, productTab)
6565

6666
return (
6767
<div
@@ -599,10 +599,42 @@ const WebAnalyticsTabs = (): JSX.Element => {
599599
)
600600
}
601601

602+
const WebVitalsEmptyState = (): JSX.Element => {
603+
const { currentTeam } = useValues(teamLogic)
604+
const { updateCurrentTeam } = useActions(teamLogic)
605+
606+
return (
607+
<div className="col-span-full w-full">
608+
<ProductIntroduction
609+
productName="Web Vitals"
610+
productKey={ProductKey.WEB_ANALYTICS}
611+
thingName="web vital"
612+
isEmpty={true}
613+
titleOverride="Enable web vitals to get started"
614+
description="Track Core Web Vitals like LCP, FID, and CLS to understand your site's performance.
615+
Enabling this will capture performance metrics from your visitors, which counts towards your event quota.
616+
You can always disable this feature in the settings."
617+
docsURL="https://posthog.com/docs/web-analytics/web-vitals"
618+
actionElementOverride={
619+
<LemonButton
620+
type="primary"
621+
onClick={() => updateCurrentTeam({ autocapture_web_vitals_opt_in: true })}
622+
data-attr="web-vitals-enable"
623+
disabledReason={currentTeam ? undefined : 'Loading...'}
624+
>
625+
Enable web vitals
626+
</LemonButton>
627+
}
628+
/>
629+
</div>
630+
)
631+
}
632+
602633
const getEmptyOnboardingContent = (
603634
featureFlags: FeatureFlagsSet,
604635
currentTeamLoading: boolean,
605-
currentTeam: TeamType | TeamPublicType | null
636+
currentTeam: TeamType | TeamPublicType | null,
637+
productTab: ProductTab
606638
): JSX.Element | null => {
607639
if (!featureFlags[FEATURE_FLAGS.WEB_ANALYTICS_EMPTY_ONBOARDING]) {
608640
return null
@@ -612,28 +644,39 @@ const getEmptyOnboardingContent = (
612644
return <LemonSkeleton className="col-span-full w-full" />
613645
}
614646

615-
if (!currentTeam?.ingested_event) {
647+
if (productTab === ProductTab.ANALYTICS && !currentTeam?.ingested_event) {
616648
return (
617-
<ProductIntroduction
618-
className="col-span-full w-full"
619-
productName="Web Analytics"
620-
productKey={ProductKey.WEB_ANALYTICS}
621-
thingName="event"
622-
isEmpty={true}
623-
titleOverride="Nothing to investigate yet!"
624-
description="Install PostHog on your site or app to start capturing events. Head to the installation guide to get set up in just a few minutes."
625-
docsURL="https://posthog.com/docs/web-analytics/installation"
626-
actionElementOverride={
627-
<LemonButton
628-
type="primary"
629-
to={urls.onboarding(ProductKey.WEB_ANALYTICS, OnboardingStepKey.INSTALL)}
630-
data-attr="web-analytics-onboarding"
631-
>
632-
Open installation guide
633-
</LemonButton>
634-
}
635-
/>
649+
<div className="col-span-full w-full">
650+
<ProductIntroduction
651+
productName="Web Analytics"
652+
productKey={ProductKey.WEB_ANALYTICS}
653+
thingName="event"
654+
isEmpty={true}
655+
titleOverride="Nothing to investigate yet!"
656+
description="Install PostHog on your site or app to start capturing events. Head to the installation guide to get set up in just a few minutes."
657+
actionElementOverride={
658+
<div className="flex items-center gap-2">
659+
<LemonButton
660+
type="primary"
661+
to={urls.onboarding(ProductKey.WEB_ANALYTICS, OnboardingStepKey.INSTALL)}
662+
data-attr="web-analytics-onboarding"
663+
>
664+
Open installation guide
665+
</LemonButton>
666+
<span className="text-muted-alt">or</span>
667+
<Link target="_blank" to="/web/web-vitals">
668+
Set up web vitals while you wait
669+
</Link>
670+
</div>
671+
}
672+
/>
673+
</div>
636674
)
637675
}
676+
677+
if (productTab === ProductTab.WEB_VITALS && !currentTeam?.autocapture_web_vitals_opt_in) {
678+
return <WebVitalsEmptyState />
679+
}
680+
638681
return null
639682
}

frontend/src/scenes/web-analytics/WebAnalyticsHealthCheck.tsx

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { useValues } from 'kea'
22

33
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
44
import { Link } from 'lib/lemon-ui/Link'
5-
import { urls } from 'scenes/urls'
65
import { webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic'
76

87
import { ConversionGoalWarning, ProductTab } from './common'
@@ -36,84 +35,9 @@ export const WebAnalyticsHealthCheck = (): JSX.Element | null => {
3635
}
3736
}
3837

39-
// No need to show loading or error states for this warning
4038
if (!statusCheck) {
4139
return null
4240
}
4341

44-
if (!statusCheck.hasAuthorizedUrls) {
45-
return (
46-
<LemonBanner type="warning" className="mt-2">
47-
<p>
48-
We couldn't find any authorized domains. Some of our Web analytics filters won't work correctly
49-
until you let us know what domains you are sending your events from.
50-
</p>
51-
<p>
52-
Please take some time to outline them for us in{' '}
53-
<Link to={urls.settings('environment', 'web-analytics-authorized-urls')}>the settings</Link>.
54-
</p>
55-
</LemonBanner>
56-
)
57-
}
58-
59-
if (productTab === ProductTab.WEB_VITALS) {
60-
if (!statusCheck.isSendingWebVitals) {
61-
return (
62-
<LemonBanner type="warning" className="mt-2">
63-
<p>
64-
No <code>$web_vitals</code> events have been received. Web Vitals won't work correctly (it'll be
65-
a little empty!)
66-
</p>
67-
<p>
68-
Please see{' '}
69-
<Link to="https://posthog.com/docs/web-analytics/web-vitals">
70-
documentation for how to set up web vitals
71-
</Link>
72-
.
73-
</p>
74-
</LemonBanner>
75-
)
76-
}
77-
} else {
78-
if (!statusCheck.isSendingPageViews) {
79-
return (
80-
<LemonBanner type="warning" className="mt-2">
81-
<p>
82-
No <code>$pageview</code>{' '}
83-
{!statusCheck.isSendingPageLeaves ? (
84-
<>
85-
or <code>$pageleave</code>{' '}
86-
</>
87-
) : null}
88-
events have been received. Web analytics won't work correctly (it'll be a little empty!)
89-
</p>
90-
<p>
91-
Please see{' '}
92-
<Link to="https://posthog.com/docs/libraries/js">
93-
documentation for how to set up posthog-js
94-
</Link>
95-
.
96-
</p>
97-
</LemonBanner>
98-
)
99-
} else if (!statusCheck.isSendingPageLeaves) {
100-
return (
101-
<LemonBanner type="warning" className="mt-2">
102-
<p>
103-
No <code>$pageleave</code> events have been received, this means that Bounce rate and Session
104-
duration might be inaccurate.
105-
</p>
106-
<p>
107-
Please see{' '}
108-
<Link to="https://posthog.com/docs/libraries/js">
109-
documentation for how to set up posthog-js
110-
</Link>
111-
.
112-
</p>
113-
</LemonBanner>
114-
)
115-
}
116-
}
117-
11842
return null
11943
}

0 commit comments

Comments
 (0)