From bfa4c67ebc348145fe7d26b2dc9f8b71b85209e7 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Tue, 27 Jan 2026 20:04:24 +0000 Subject: [PATCH] fix: add attribution tracking and correct blog event names - Fix $pageview to include $referrer, $referring_domain, and UTM params - Track previous URL for SPA navigation referrer attribution - Rename events to blog_index_view and blog_post_view (per spec) - Add publish_time_pt property to blog_post_view - Add trackBlogSubstackClick function for use with MKT-72 --- .../components/providers/posthog-provider.tsx | 65 ++++++++++++++++++- apps/web-roo-code/src/lib/blog/analytics.ts | 25 +++++-- apps/web-roo-code/src/lib/blog/index.ts | 1 + 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/apps/web-roo-code/src/components/providers/posthog-provider.tsx b/apps/web-roo-code/src/components/providers/posthog-provider.tsx index 29e2fca933..52959d56e3 100644 --- a/apps/web-roo-code/src/components/providers/posthog-provider.tsx +++ b/apps/web-roo-code/src/components/providers/posthog-provider.tsx @@ -3,26 +3,85 @@ import { usePathname, useSearchParams } from "next/navigation" import posthog from "posthog-js" import { PostHogProvider as OriginalPostHogProvider } from "posthog-js/react" -import { useEffect, Suspense } from "react" +import { useEffect, useRef, Suspense } from "react" import { hasConsent } from "@/lib/analytics/consent-manager" +/** + * Extract referring domain from a URL string + */ +function getReferringDomain(referrer: string): string { + if (!referrer) return "" + try { + const url = new URL(referrer) + return url.hostname + } catch { + return "" + } +} + +/** + * Get UTM parameters from current URL search params + */ +function getUtmParams(searchParams: URLSearchParams | null): Record { + const utmKeys = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"] + const utmParams: Record = {} + + if (searchParams) { + for (const key of utmKeys) { + const value = searchParams.get(key) + if (value) { + utmParams[key] = value + } + } + } + + return utmParams +} + function PageViewTracker() { const pathname = usePathname() const searchParams = useSearchParams() + // Track previous URL for SPA navigation referrer + const previousUrl = useRef(null) + // Store original document.referrer for first page load + const initialReferrer = useRef(null) - // Track page views + // Capture initial referrer on mount + useEffect(() => { + if (typeof window !== "undefined" && initialReferrer.current === null) { + initialReferrer.current = document.referrer || "" + } + }, []) + + // Track page views with proper attribution useEffect(() => { if (pathname && process.env.NEXT_PUBLIC_POSTHOG_KEY) { let url = window.location.origin + pathname if (searchParams && searchParams.toString()) { url = url + `?${searchParams.toString()}` } + + // Determine the referrer: + // - For first pageview: use document.referrer (external referrer) + // - For SPA navigations: use previous URL within the site + const referrer = previousUrl.current || initialReferrer.current || "" + const referringDomain = getReferringDomain(referrer) + + // Collect UTM parameters + const utmParams = getUtmParams(searchParams) + posthog.capture("$pageview", { $current_url: url, + $referrer: referrer, + $referring_domain: referringDomain, + ...utmParams, }) + + // Update previous URL for next navigation + previousUrl.current = url } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pathname, searchParams.toString()]) + }, [pathname, searchParams?.toString()]) return null } diff --git a/apps/web-roo-code/src/lib/blog/analytics.ts b/apps/web-roo-code/src/lib/blog/analytics.ts index 15fd16a1d9..8e8cc86dde 100644 --- a/apps/web-roo-code/src/lib/blog/analytics.ts +++ b/apps/web-roo-code/src/lib/blog/analytics.ts @@ -15,9 +15,8 @@ import type { BlogPost } from "./types" export function trackBlogIndexView(postCount: number): void { if (typeof window === "undefined" || !posthog.__loaded) return - posthog.capture("blog_index_viewed", { + posthog.capture("blog_index_view", { post_count: postCount, - page_type: "blog_index", }) } @@ -28,12 +27,24 @@ export function trackBlogIndexView(postCount: number): void { export function trackBlogPostView(post: BlogPost): void { if (typeof window === "undefined" || !posthog.__loaded) return - posthog.capture("blog_post_viewed", { - post_slug: post.slug, - post_title: post.title, - post_tags: post.tags, + posthog.capture("blog_post_view", { + slug: post.slug, + title: post.title, + tags: post.tags, publish_date: post.publish_date, - page_type: "blog_post", + publish_time_pt: post.publish_time_pt, + }) +} + +/** + * Track Substack subscribe click + * Called when user clicks the Substack subscribe link + */ +export function trackBlogSubstackClick(source: "footer" | "blog_index" | "blog_post"): void { + if (typeof window === "undefined" || !posthog.__loaded) return + + posthog.capture("blog_substack_click", { + source, }) } diff --git a/apps/web-roo-code/src/lib/blog/index.ts b/apps/web-roo-code/src/lib/blog/index.ts index 8aecd2de45..bc2c9787d2 100644 --- a/apps/web-roo-code/src/lib/blog/index.ts +++ b/apps/web-roo-code/src/lib/blog/index.ts @@ -53,4 +53,5 @@ export { trackBlogPostShare, trackBlogPostCTAClick, trackBlogPostTimeSpent, + trackBlogSubstackClick, } from "./analytics"