From 8734063afdd51ed39976763c2e2dc288358f180b Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 6 Feb 2026 17:08:02 -0800 Subject: [PATCH 01/20] feat(marketing): add Outlit SDK with consent-gated tracking --- apps/marketing/package.json | 1 + apps/marketing/src/app/providers.tsx | 25 +++++++++++++------ .../CookieConsent/CookieConsent.tsx | 3 +++ apps/marketing/src/env.ts | 2 ++ apps/marketing/src/lib/outlit/index.ts | 21 ++++++++++++++++ bun.lock | 5 ++++ 6 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 apps/marketing/src/lib/outlit/index.ts diff --git a/apps/marketing/package.json b/apps/marketing/package.json index a5687af3c24..d688cd99ada 100644 --- a/apps/marketing/package.json +++ b/apps/marketing/package.json @@ -11,6 +11,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@outlit/browser": "^1.2.0", "@react-three/drei": "^10.7.6", "@react-three/fiber": "^9.4.0", "@sentry/nextjs": "^10.36.0", diff --git a/apps/marketing/src/app/providers.tsx b/apps/marketing/src/app/providers.tsx index 5160fa321bf..cee84191792 100644 --- a/apps/marketing/src/app/providers.tsx +++ b/apps/marketing/src/app/providers.tsx @@ -1,22 +1,31 @@ "use client"; +import { OutlitProvider } from "@outlit/browser/react"; import { THEME_STORAGE_KEY } from "@superset/shared/constants"; import { ThemeProvider } from "next-themes"; import posthog from "posthog-js"; import { PostHogProvider } from "posthog-js/react"; +import { env } from "@/env"; + export function Providers({ children }: { children: React.ReactNode }) { return ( - - {children} - + + {children} + + ); } diff --git a/apps/marketing/src/components/CookieConsent/CookieConsent.tsx b/apps/marketing/src/components/CookieConsent/CookieConsent.tsx index ca8100adced..e17e9fe00f7 100644 --- a/apps/marketing/src/components/CookieConsent/CookieConsent.tsx +++ b/apps/marketing/src/components/CookieConsent/CookieConsent.tsx @@ -1,5 +1,6 @@ "use client"; +import { useOutlit } from "@outlit/browser/react"; import { Button } from "@superset/ui/button"; import { AnimatePresence, motion } from "framer-motion"; import Link from "next/link"; @@ -9,6 +10,7 @@ import { useEffect, useState } from "react"; import { ANALYTICS_CONSENT_KEY } from "@/lib/constants"; export function CookieConsent() { + const { enableTracking } = useOutlit(); const [showBanner, setShowBanner] = useState(false); useEffect(() => { @@ -21,6 +23,7 @@ export function CookieConsent() { const handleAccept = () => { localStorage.setItem(ANALYTICS_CONSENT_KEY, "accepted"); setShowBanner(false); + enableTracking(); }; const handleOptOut = () => { diff --git a/apps/marketing/src/env.ts b/apps/marketing/src/env.ts index 3d7dc7948de..aea10c322f3 100644 --- a/apps/marketing/src/env.ts +++ b/apps/marketing/src/env.ts @@ -30,6 +30,7 @@ export const env = createEnv({ NEXT_PUBLIC_SENTRY_ENVIRONMENT: z .enum(["development", "preview", "production"]) .optional(), + NEXT_PUBLIC_OUTLIT_KEY: z.string().optional(), }, experimental__runtimeEnv: { NODE_ENV: process.env.NODE_ENV, @@ -40,6 +41,7 @@ export const env = createEnv({ NEXT_PUBLIC_SENTRY_DSN_MARKETING: process.env.NEXT_PUBLIC_SENTRY_DSN_MARKETING, NEXT_PUBLIC_SENTRY_ENVIRONMENT: process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT, + NEXT_PUBLIC_OUTLIT_KEY: process.env.NEXT_PUBLIC_OUTLIT_KEY, }, skipValidation: !!process.env.SKIP_ENV_VALIDATION, }); diff --git a/apps/marketing/src/lib/outlit/index.ts b/apps/marketing/src/lib/outlit/index.ts new file mode 100644 index 00000000000..80d98a29f8d --- /dev/null +++ b/apps/marketing/src/lib/outlit/index.ts @@ -0,0 +1,21 @@ +import { Outlit } from "@outlit/browser"; + +import { env } from "@/env"; + +let outlit: Outlit | null = null; + +export function getOutlit(): Outlit | null { + if (!env.NEXT_PUBLIC_OUTLIT_KEY) return null; + + if (!outlit) { + outlit = new Outlit({ + publicKey: env.NEXT_PUBLIC_OUTLIT_KEY, + trackPageviews: true, + trackForms: true, + autoTrack: false, + }); + } + return outlit; +} + +export { outlit }; diff --git a/bun.lock b/bun.lock index 381ec757235..85598b0ab1e 100644 --- a/bun.lock +++ b/bun.lock @@ -318,6 +318,7 @@ "name": "@superset/marketing", "version": "0.1.0", "dependencies": { + "@outlit/browser": "^1.2.0", "@react-three/drei": "^10.7.6", "@react-three/fiber": "^9.4.0", "@sentry/nextjs": "^10.36.0", @@ -1517,6 +1518,10 @@ "@orama/orama": ["@orama/orama@3.1.18", "", {}, "sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA=="], + "@outlit/browser": ["@outlit/browser@1.2.0", "", { "dependencies": { "@outlit/core": "1.1.0" }, "peerDependencies": { "react": ">=17.0.0", "vue": ">=3.0.0" }, "optionalPeers": ["react", "vue"] }, "sha512-h2c/9Qmzw7tVygPln3o8XVEVA/0E9XWOhbh2mNWPjmCmv0VbREP4IoU8dG4flWYNKcGo2aVPsdIXTiEVqSlkzg=="], + + "@outlit/core": ["@outlit/core@1.1.0", "", {}, "sha512-TNM9cIdunfcF0TLwCzSJ7sZWmDhUF8oYt4Q1KgW5Fm+4KGsfjDUbGDq9svrml1cmPrAXTZdiYwIYdRAG9327/g=="], + "@pierre/diffs": ["@pierre/diffs@1.0.10", "", { "dependencies": { "@shikijs/core": "^3.0.0", "@shikijs/engine-javascript": "^3.0.0", "@shikijs/transformers": "^3.0.0", "diff": "8.0.3", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "^3.0.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-ahkpfS30NfaB+PBxnf0/Mc20ySBRTQmM28a7Ojpd0UZixmTyhGhJfBFjvmhX8dSzR22lB3h3OIMMxpB4yYTIOQ=="], "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], From 3c7f013ae53686201463bd24aff18538810d903f Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 6 Feb 2026 17:12:23 -0800 Subject: [PATCH 02/20] refactor(marketing): centralize analytics track calls --- .../src/app/components/CTAButtons/HeaderCTA.tsx | 6 +++--- .../app/components/DownloadButton/DownloadButton.tsx | 10 +++++----- apps/marketing/src/lib/analytics/index.ts | 11 +++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 apps/marketing/src/lib/analytics/index.ts diff --git a/apps/marketing/src/app/components/CTAButtons/HeaderCTA.tsx b/apps/marketing/src/app/components/CTAButtons/HeaderCTA.tsx index c56ab7a3e1f..95876019dad 100644 --- a/apps/marketing/src/app/components/CTAButtons/HeaderCTA.tsx +++ b/apps/marketing/src/app/components/CTAButtons/HeaderCTA.tsx @@ -2,10 +2,10 @@ import { DOWNLOAD_URL_MAC_ARM64 } from "@superset/shared/constants"; import { Download } from "lucide-react"; -import posthog from "posthog-js"; import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { HiMiniClock } from "react-icons/hi2"; +import { track } from "@/lib/analytics"; import { usePlatform } from "../../hooks/useOS"; import { DownloadButton } from "../DownloadButton"; import { WaitlistModal } from "../WaitlistModal"; @@ -65,7 +65,7 @@ export function HeaderCTA({ isLoggedIn, dashboardUrl }: HeaderCTAProps) { posthog.capture("download_clicked")} + onClick={() => track("download_clicked")} > Download for macOS