From 8a919a9bbc82c4372013cbc6566050a19b9363a4 Mon Sep 17 00:00:00 2001 From: Karakatiza666 Date: Wed, 22 Jul 2026 14:56:38 +0000 Subject: [PATCH 1/2] [web-console] Add ProductFruits and ConceptualHQ hooks Signed-off-by: Karakatiza666 --- bun.lock | 3 + .../src/api/endpoints/config.rs | 12 ++- crates/pipeline-manager/src/auth.rs | 2 + crates/pipeline-manager/src/config.rs | 22 ++++- js-packages/web-console/package.json | 1 + .../src/lib/compositions/configCache.spec.ts | 2 +- .../src/lib/services/conceptualHq.spec.ts | 89 +++++++++++++++++++ .../src/lib/services/conceptualHq.ts | 84 +++++++++++++++++ .../src/lib/services/manager/types.gen.ts | 16 +++- .../src/lib/services/productFruits.spec.ts | 78 ++++++++++++++++ .../src/lib/services/productFruits.ts | 40 +++++++++ js-packages/web-console/src/routes/+layout.ts | 8 +- openapi.json | 20 +++-- 13 files changed, 359 insertions(+), 18 deletions(-) create mode 100644 js-packages/web-console/src/lib/services/conceptualHq.spec.ts create mode 100644 js-packages/web-console/src/lib/services/conceptualHq.ts create mode 100644 js-packages/web-console/src/lib/services/productFruits.spec.ts create mode 100644 js-packages/web-console/src/lib/services/productFruits.ts diff --git a/bun.lock b/bun.lock index ba64a39f6d5..aeb637f0b2c 100644 --- a/bun.lock +++ b/bun.lock @@ -240,6 +240,7 @@ "prettier": "3.8.1", "prettier-plugin-svelte": "3.5.0", "prettier-plugin-tailwindcss": "0.7.2", + "product-fruits": "^1.1.0", "profiler-layout": "workspace:*", "profiler-lib": "workspace:*", "runed": "0.37.1", @@ -1803,6 +1804,8 @@ "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], + "product-fruits": ["product-fruits@1.1.0", "", {}, "sha512-eDNdYrf4zJDbu2RSWJ10UxX1QdESqYFfrsOjwfrQG4oJvj88b540geS+lhHeKoEVLA9QEPZlWX9M+sKQois+bA=="], + "profiler-app": ["profiler-app@workspace:js-packages/profiler-app"], "profiler-layout": ["profiler-layout@workspace:js-packages/profiler-layout"], diff --git a/crates/pipeline-manager/src/api/endpoints/config.rs b/crates/pipeline-manager/src/api/endpoints/config.rs index 8d6b85c96fc..470aa27d44f 100644 --- a/crates/pipeline-manager/src/api/endpoints/config.rs +++ b/crates/pipeline-manager/src/api/endpoints/config.rs @@ -68,8 +68,12 @@ impl BuildInformation { #[derive(Serialize, ToSchema)] pub(crate) struct Configuration { - /// Telemetry key. - pub telemetry: String, + /// PostHog telemetry key. Empty when disabled. + pub posthog: String, + /// ConceptualHQ analytics key. Empty when disabled. + pub conceptualhq: String, + /// Product Fruits workspace code for in-app onboarding. Empty when disabled. + pub product_fruits: String, /// Feldera edition: "Open source" or "Enterprise" pub edition: String, /// The version corresponding to the type of `edition`. @@ -106,7 +110,9 @@ impl Configuration { let license_check = LicenseCheck::validate(state).await.unwrap_or_default(); Configuration { - telemetry: state.config.telemetry.clone(), + posthog: state.config.telemetry.clone(), + conceptualhq: state.config.conceptualhq.clone(), + product_fruits: state.config.product_fruits.clone(), edition: if cfg!(feature = "feldera-enterprise") { "Enterprise" } else { diff --git a/crates/pipeline-manager/src/auth.rs b/crates/pipeline-manager/src/auth.rs index 00be4085d8f..83b4c762414 100644 --- a/crates/pipeline-manager/src/auth.rs +++ b/crates/pipeline-manager/src/auth.rs @@ -1077,6 +1077,8 @@ mod test { allowed_origins: None, demos_dir: vec![], telemetry: "".to_owned(), + conceptualhq: "".to_owned(), + product_fruits: "".to_owned(), support_data_collection_frequency: 15, support_data_retention: 3, authorized_groups: vec![], diff --git a/crates/pipeline-manager/src/config.rs b/crates/pipeline-manager/src/config.rs index 7c6f7e923f4..048d63d59c3 100644 --- a/crates/pipeline-manager/src/config.rs +++ b/crates/pipeline-manager/src/config.rs @@ -863,13 +863,27 @@ pub struct ApiServerConfig { #[arg(long, default_values_t = default_demos_dir())] pub demos_dir: Vec, - /// Telemetry key. + /// PostHog telemetry key. /// - /// If a telemetry key is set, anonymous usage data will be collected - /// and sent to our telemetry service. + /// If a key is set, anonymous usage data will be collected + /// and sent to our PostHog telemetry service. #[arg(long, default_value = "", env = "FELDERA_TELEMETRY")] pub telemetry: String, + /// ConceptualHQ analytics key. + /// + /// If set, the WebConsole loads ConceptualHQ analytics to identify the + /// signed-in user and track sign-in events. Leave empty to disable. + #[arg(long, default_value = "", env = "FELDERA_CONCEPTUALHQ")] + pub conceptualhq: String, + + /// Product Fruits workspace code. + /// + /// If set, the WebConsole loads Product Fruits for in-app onboarding + /// (tours, hints, surveys). Leave empty to disable. + #[arg(long, default_value = "", env = "FELDERA_PRODUCT_FRUITS")] + pub product_fruits: String, + /// Support data collection frequency (in seconds). /// /// This parameter determines how often data should be collected and stored @@ -965,6 +979,8 @@ impl ApiServerConfig { dev_mode: false, allowed_origins: None, telemetry: "test".to_string(), + conceptualhq: "".to_string(), + product_fruits: "".to_string(), demos_dir: vec!["demos".to_string()], dump_openapi: false, issuer_tenant: false, diff --git a/js-packages/web-console/package.json b/js-packages/web-console/package.json index 19011fffbac..1fe612e02c8 100644 --- a/js-packages/web-console/package.json +++ b/js-packages/web-console/package.json @@ -72,6 +72,7 @@ "prettier": "3.8.1", "prettier-plugin-svelte": "3.5.0", "prettier-plugin-tailwindcss": "0.7.2", + "product-fruits": "^1.1.0", "profiler-layout": "workspace:*", "profiler-lib": "workspace:*", "runed": "0.37.1", diff --git a/js-packages/web-console/src/lib/compositions/configCache.spec.ts b/js-packages/web-console/src/lib/compositions/configCache.spec.ts index 2549f7c28ca..0ff55cd99de 100644 --- a/js-packages/web-console/src/lib/compositions/configCache.spec.ts +++ b/js-packages/web-console/src/lib/compositions/configCache.spec.ts @@ -54,7 +54,7 @@ const baseConfig = { edition: 'Open source', revision: 'abc123', runtime_revision: 'rt123', - telemetry: '', + posthog: '', version: '0.0.0' } as unknown as Configuration diff --git a/js-packages/web-console/src/lib/services/conceptualHq.spec.ts b/js-packages/web-console/src/lib/services/conceptualHq.spec.ts new file mode 100644 index 00000000000..19e5d9b146f --- /dev/null +++ b/js-packages/web-console/src/lib/services/conceptualHq.spec.ts @@ -0,0 +1,89 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' + +import type { Configuration } from '$lib/services/manager' +import type { UserProfile } from '$lib/types/auth' + +const profile: UserProfile = { id: 'user-1', email: 'a@b.com', name: 'Ann' } + +// Only `conceptualhq` is read; the rest is filler to satisfy the type. +const config = (conceptualhq: string) => ({ conceptualhq }) as Configuration + +// Fake DOM that records the injected {#snippet defaultIcon()} @@ -14,9 +18,11 @@ + captureEvent('calendly_opened', { url: calendlyUrl, trigger_location: triggerLocation })} > {@render icon()} {@render children?.()} diff --git a/js-packages/web-console/src/lib/components/other/DemoTile.svelte b/js-packages/web-console/src/lib/components/other/DemoTile.svelte index 8f78fffcb57..06b26d15f91 100644 --- a/js-packages/web-console/src/lib/components/other/DemoTile.svelte +++ b/js-packages/web-console/src/lib/components/other/DemoTile.svelte @@ -2,14 +2,14 @@ import { useTryPipeline } from '$lib/compositions/pipelines/useTryPipeline' import type { Demo } from '$lib/services/pipelineManager' - let { demo }: { demo: Demo | null } = $props() + let { demo, triggerLocation }: { demo: Demo | null; triggerLocation?: string } = $props() const tryPipeline = useTryPipeline() {#if demo}
{demo.type}
- diff --git a/js-packages/web-console/src/lib/components/pipelines/list/Actions.svelte b/js-packages/web-console/src/lib/components/pipelines/list/Actions.svelte index 3fb2b504bff..c55d9ae930a 100644 --- a/js-packages/web-console/src/lib/components/pipelines/list/Actions.svelte +++ b/js-packages/web-console/src/lib/components/pipelines/list/Actions.svelte @@ -50,6 +50,7 @@ groups related actions into multi-action dropdowns when multiple options are ava import JSONDialog from '$lib/components/dialogs/JSONDialog.svelte' import PipelineConfigurationsPopup from '$lib/components/layout/pipelines/PipelineConfigurationsPopup.svelte' import { duplicatePipeline, duplicatePipelineTooltip } from '$lib/compositions/duplicatePipeline' + import { captureEvent } from '$lib/services/analytics' import { useGlobalDialog } from '$lib/compositions/layout/useGlobalDialog.svelte' import { useIsMobile } from '$lib/compositions/layout/useIsMobile.svelte' import { usePipelineActionCallbacks } from '$lib/compositions/pipelines/usePipelineActionCallbacks.svelte' @@ -849,7 +850,11 @@ groups related actions into multi-action dropdowns when multiple options are ava class="block pt-2 underline" href="https://calendly.com/d/cqnj-p63-mbq/feldera-demo" target="_blank" - rel="noreferrer">Upgrade
+ captureEvent('calendly_opened', { + url: 'https://calendly.com/d/cqnj-p63-mbq/feldera-demo' + })}>Upgrade {/if} diff --git a/js-packages/web-console/src/lib/compositions/pipelines/useTryPipeline.ts b/js-packages/web-console/src/lib/compositions/pipelines/useTryPipeline.ts index d65f22e5059..5c8a72f6398 100644 --- a/js-packages/web-console/src/lib/compositions/pipelines/useTryPipeline.ts +++ b/js-packages/web-console/src/lib/compositions/pipelines/useTryPipeline.ts @@ -1,13 +1,24 @@ import { goto } from '$app/navigation' -import { useUpdatePipelineList } from '$lib/compositions/pipelines/usePipelineList.svelte' +import { + usePipelineList, + useUpdatePipelineList +} from '$lib/compositions/pipelines/usePipelineList.svelte' import { resolve } from '$lib/functions/svelte' +import { captureEvent } from '$lib/services/analytics' import type { Demo } from '$lib/services/manager' import { usePipelineManager } from '../usePipelineManager.svelte' export const useTryPipeline = () => { const { updatePipelines } = useUpdatePipelineList() + const pipelineList = usePipelineList() const api = usePipelineManager() - return async (pipeline: Omit) => { + return async (pipeline: Omit, trigger_location?: string) => { + const already_created = (pipelineList.pipelines ?? []).some((p) => p.name === pipeline.name) + captureEvent('demo_opened', { + demo: pipeline.name, + already_created, + trigger_location + }) try { const newPipeline = await api.postPipeline({ name: pipeline.name, diff --git a/js-packages/web-console/src/lib/services/analytics.spec.ts b/js-packages/web-console/src/lib/services/analytics.spec.ts new file mode 100644 index 00000000000..dfcc557f12d --- /dev/null +++ b/js-packages/web-console/src/lib/services/analytics.spec.ts @@ -0,0 +1,32 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' + +// vi.mock is hoisted above imports, so its factory may only touch vars created +// with vi.hoisted (also hoisted), not plain module-level consts. +const { capture, trackConceptualHq } = vi.hoisted(() => ({ + capture: vi.fn(), + trackConceptualHq: vi.fn() +})) +vi.mock('posthog-js', () => ({ default: { capture } })) +vi.mock('$lib/services/conceptualHq', () => ({ trackConceptualHq })) + +import { captureEvent } from './analytics' + +afterEach(() => { + capture.mockClear() + trackConceptualHq.mockClear() +}) + +describe('captureEvent', () => { + it('forwards the event and properties to both PostHog and ConceptualHQ', () => { + const props = { demo: 'fraud', already_created: true, source: 'home' } + captureEvent('demo_opened', props) + expect(capture).toHaveBeenCalledExactlyOnceWith('demo_opened', props) + expect(trackConceptualHq).toHaveBeenCalledExactlyOnceWith('demo_opened', props) + }) + + it('forwards events with no properties to both backends', () => { + captureEvent('signin') + expect(capture).toHaveBeenCalledExactlyOnceWith('signin', undefined) + expect(trackConceptualHq).toHaveBeenCalledExactlyOnceWith('signin', undefined) + }) +}) diff --git a/js-packages/web-console/src/lib/services/analytics.ts b/js-packages/web-console/src/lib/services/analytics.ts new file mode 100644 index 00000000000..c7e8d613fcf --- /dev/null +++ b/js-packages/web-console/src/lib/services/analytics.ts @@ -0,0 +1,14 @@ +import posthog from 'posthog-js' + +import { trackConceptualHq } from '$lib/services/conceptualHq' + +/** + * Report a product-analytics event to every configured backend (PostHog and + * ConceptualHQ). Each backend no-ops when its integration is disabled, so + * callers fire unconditionally. Use snake_case event names and property keys for consistency + * across both tools. + */ +export const captureEvent = (event: string, properties?: Record) => { + posthog.capture(event, properties) + trackConceptualHq(event, properties) +} diff --git a/js-packages/web-console/src/lib/services/conceptualHq.spec.ts b/js-packages/web-console/src/lib/services/conceptualHq.spec.ts index 19e5d9b146f..2ad785ebf9f 100644 --- a/js-packages/web-console/src/lib/services/conceptualHq.spec.ts +++ b/js-packages/web-console/src/lib/services/conceptualHq.spec.ts @@ -87,3 +87,36 @@ describe('initConceptualHq', () => { expect(() => initConceptualHq(config('my-key'), profile)).not.toThrow() }) }) + +describe('trackConceptualHq', () => { + it('enqueues a track call with properties once the loader is installed', async () => { + stubDom() + const { initConceptualHq, trackConceptualHq } = await freshModule(true) + initConceptualHq(config('my-key'), profile) + const before = queued().length + trackConceptualHq('demo_opened', { demo: 'x', already_created: true }) + expect(queued().slice(before)).toEqual([ + ['track', 'demo_opened', { demo: 'x', already_created: true }] + ]) + }) + + it('omits the properties argument when none are given', async () => { + stubDom() + const { initConceptualHq, trackConceptualHq } = await freshModule(true) + initConceptualHq(config('my-key'), profile) + const before = queued().length + trackConceptualHq('some_event') + expect(queued().slice(before)).toEqual([['track', 'some_event']]) + }) + + it('is a no-op when analytics is disabled (loader never installed)', async () => { + const { trackConceptualHq } = await freshModule(true) + expect(() => trackConceptualHq('demo_opened', { demo: 'x' })).not.toThrow() + expect(window.ca).toBeUndefined() + }) + + it('is a no-op outside the browser', async () => { + const { trackConceptualHq } = await freshModule(false) + expect(() => trackConceptualHq('demo_opened')).not.toThrow() + }) +}) diff --git a/js-packages/web-console/src/lib/services/conceptualHq.ts b/js-packages/web-console/src/lib/services/conceptualHq.ts index a1654eecb30..39bd9366aca 100644 --- a/js-packages/web-console/src/lib/services/conceptualHq.ts +++ b/js-packages/web-console/src/lib/services/conceptualHq.ts @@ -54,10 +54,7 @@ const loadConceptualAnalytics = (key: string): ConceptualAnalytics => { } /** - * Initialize ConceptualHQ analytics for the signed-in user. The analytics key - * is served by the pipeline-manager in `/config` (`config.conceptualhq`), the - * same path the PostHog key travels, so deployments configure it without - * rebuilding the console. + * Initialize ConceptualHQ analytics for the signed-in user. * * Identifies the user (keyed on email to match PostHog identity) and tracks a * `signin` event. @@ -82,3 +79,20 @@ export const initConceptualHq = (config: Configuration, profile: UserProfile) => } ca('track', 'signin') } + +/** + * Track an event in ConceptualHQ. No-op when the loader was never installed + * (analytics disabled) or outside the browser, so callers fire unconditionally. + * Prefer the shared `captureEvent` in `analytics.ts` over calling this directly, + * so PostHog and ConceptualHQ stay in sync. + */ +export const trackConceptualHq = (event: string, properties?: Record) => { + if (typeof window === 'undefined' || !window.ca) { + return + } + if (properties) { + window.ca('track', event, properties) + } else { + window.ca('track', event) + } +} diff --git a/js-packages/web-console/src/lib/services/posthog.spec.ts b/js-packages/web-console/src/lib/services/posthog.spec.ts new file mode 100644 index 00000000000..66d766a2c08 --- /dev/null +++ b/js-packages/web-console/src/lib/services/posthog.spec.ts @@ -0,0 +1,79 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' + +import type { Configuration } from '$lib/services/manager' +import type { UserProfile } from '$lib/types/auth' + +// vi.mock is hoisted above imports, so its factory may only touch vars created +// with vi.hoisted (also hoisted), not plain module-level consts. +const { init, identify, capture } = vi.hoisted(() => ({ + init: vi.fn(), + identify: vi.fn(), + capture: vi.fn() +})) +vi.mock('posthog-js', () => ({ default: { init, identify, capture } })) + +const profile: UserProfile = { id: 'user-1', email: 'a@b.com', name: 'Ann' } + +// Only `posthog` is read; the rest is filler to satisfy the type. +const config = (posthog: string) => ({ posthog }) as Configuration + +// Each case re-imports a fresh module so the one-shot `initialized` flag resets. +const freshModule = async (browser: boolean) => { + vi.resetModules() + init.mockClear() + identify.mockClear() + capture.mockClear() + vi.stubGlobal('window', browser ? {} : undefined) + return import('./posthog') +} + +afterEach(() => { + vi.unstubAllGlobals() +}) + +describe('initPosthog', () => { + it('starts the SDK, identifies the user, and captures signin', async () => { + const { initPosthog } = await freshModule(true) + initPosthog(config('ph-key'), profile) + expect(init).toHaveBeenCalledExactlyOnceWith('ph-key', { + api_host: 'https://us.i.posthog.com', + person_profiles: 'identified_only', + capture_pageview: false, + capture_pageleave: false + }) + expect(identify).toHaveBeenCalledExactlyOnceWith('a@b.com', { + email: 'a@b.com', + name: 'Ann', + auth_id: 'user-1' + }) + expect(capture).toHaveBeenCalledExactlyOnceWith('signin') + }) + + it('captures signin even when the profile has no email (skips identify)', async () => { + const { initPosthog } = await freshModule(true) + initPosthog(config('ph-key'), { id: 'user-1' }) + expect(identify).not.toHaveBeenCalled() + expect(capture).toHaveBeenCalledExactlyOnceWith('signin') + }) + + it('is idempotent: a second call reports signin once', async () => { + const { initPosthog } = await freshModule(true) + initPosthog(config('ph-key'), profile) + initPosthog(config('ph-key'), profile) + expect(init).toHaveBeenCalledOnce() + expect(capture).toHaveBeenCalledOnce() + }) + + it('does nothing when the key is empty', async () => { + const { initPosthog } = await freshModule(true) + initPosthog(config(''), profile) + expect(init).not.toHaveBeenCalled() + expect(capture).not.toHaveBeenCalled() + }) + + it('does nothing outside the browser', async () => { + const { initPosthog } = await freshModule(false) + initPosthog(config('ph-key'), profile) + expect(init).not.toHaveBeenCalled() + }) +}) diff --git a/js-packages/web-console/src/lib/services/posthog.ts b/js-packages/web-console/src/lib/services/posthog.ts new file mode 100644 index 00000000000..0efab10aa60 --- /dev/null +++ b/js-packages/web-console/src/lib/services/posthog.ts @@ -0,0 +1,40 @@ +import posthog from 'posthog-js' + +import type { Configuration } from '$lib/services/manager' +import type { UserProfile } from '$lib/types/auth' + +let initialized = false + +/** + * Initialize PostHog for the signed-in user: start the SDK, identify the user, + * and report a `signin` event. The telemetry key is served by the + * pipeline-manager in `/config` (`config.posthog`), so deployments configure it + * without rebuilding the console. + * + * Mirrors `initConceptualHq`: idempotent via its own `initialized` guard, so the + * warm-cache reconcile and `invalidateAll()` re-runs report `signin` once per + * session. No-op when the key is empty or outside the browser. + */ +export const initPosthog = (config: Configuration, profile: UserProfile) => { + if (initialized || !config.posthog || typeof window === 'undefined') { + return + } + initialized = true + + posthog.init(config.posthog, { + api_host: 'https://us.i.posthog.com', + person_profiles: 'identified_only', + capture_pageview: false, + capture_pageleave: false + }) + + if (profile.email) { + posthog.identify(profile.email, { + email: profile.email, + name: profile.name, + auth_id: profile.id + }) + } + + posthog.capture('signin') +} diff --git a/js-packages/web-console/src/routes/(system)/(authenticated)/(pipelines)/create/+page.ts b/js-packages/web-console/src/routes/(system)/(authenticated)/(pipelines)/create/+page.ts index 7bb7dae124f..4c46e32032d 100644 --- a/js-packages/web-console/src/routes/(system)/(authenticated)/(pipelines)/create/+page.ts +++ b/js-packages/web-console/src/routes/(system)/(authenticated)/(pipelines)/create/+page.ts @@ -29,5 +29,5 @@ export async function load({ url, parent }) { goto(resolve(`/`)) return } - await useTryPipeline()(pipeline) + await useTryPipeline()(pipeline, 'url') } diff --git a/js-packages/web-console/src/routes/(system)/(authenticated)/+layout.svelte b/js-packages/web-console/src/routes/(system)/(authenticated)/+layout.svelte index c9dff86d7c7..916e101db62 100644 --- a/js-packages/web-console/src/routes/(system)/(authenticated)/+layout.svelte +++ b/js-packages/web-console/src/routes/(system)/(authenticated)/+layout.svelte @@ -226,7 +226,9 @@ }} >
- Book a demo + Book a demo - Book a demo + Book a demo {/if} {/snippet} @@ -204,7 +205,7 @@ class="grid grid-cols-1 gap-x-6 gap-y-5 py-2 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-5" > {#each demos.current.slice(0, maxShownDemos) as demo} - + {/each}
diff --git a/js-packages/web-console/src/routes/(system)/(authenticated)/demos/+page.svelte b/js-packages/web-console/src/routes/(system)/(authenticated)/demos/+page.svelte index ad7db678ac0..c8120f2e5e3 100644 --- a/js-packages/web-console/src/routes/(system)/(authenticated)/demos/+page.svelte +++ b/js-packages/web-console/src/routes/(system)/(authenticated)/demos/+page.svelte @@ -56,7 +56,9 @@
- Book a demo + Book a demo {/if} {/snippet} @@ -70,7 +72,7 @@ {#each demos.current.filter((demo) => { return !demo || demosType === 'All' || demo.type === demosType }) as demo} - + {/each}
diff --git a/js-packages/web-console/src/routes/(system)/(authenticated)/health/+page.svelte b/js-packages/web-console/src/routes/(system)/(authenticated)/health/+page.svelte index d96a8a92d21..a49822313de 100644 --- a/js-packages/web-console/src/routes/(system)/(authenticated)/health/+page.svelte +++ b/js-packages/web-console/src/routes/(system)/(authenticated)/health/+page.svelte @@ -258,7 +258,9 @@ - Book a demo + Book a demo {/if} {/snippet} diff --git a/js-packages/web-console/src/routes/+layout.ts b/js-packages/web-console/src/routes/+layout.ts index 50ce77486ca..7e01509db32 100644 --- a/js-packages/web-console/src/routes/+layout.ts +++ b/js-packages/web-console/src/routes/+layout.ts @@ -29,6 +29,7 @@ import { import { initConceptualHq } from '$lib/services/conceptualHq' import type { Configuration, SessionInfo } from '$lib/services/manager' import { client } from '$lib/services/manager/client.gen' +import { initPosthog } from '$lib/services/posthog' import { initProductFruits } from '$lib/services/productFruits' import type { AuthDetails } from '$lib/types/auth' import type { LayoutLoad } from './$types' @@ -84,18 +85,6 @@ export const trailingSlash = 'always' * branches that call `invalidateAll()` unconditionally. */ -const initPosthog = async (config: Configuration) => { - if (!config.posthog) { - return - } - posthog.init(config.posthog, { - api_host: 'https://us.i.posthog.com', - person_profiles: 'identified_only', - capture_pageview: false, - capture_pageleave: false - }) -} - export type LayoutData = { auth: AuthDetails feldera: @@ -444,15 +433,7 @@ function buildLayoutData( */ function initializeConfigDependencies(auth: AuthDetails, config: Configuration) { if (typeof auth === 'object' && 'logout' in auth) { - initPosthog(config).then(() => { - if (auth.profile.email) { - posthog.identify(auth.profile.email, { - email: auth.profile.email, - name: auth.profile.name, - auth_id: auth.profile.id - }) - } - }) + initPosthog(config, auth.profile) initProductFruits(config, auth.profile) initConceptualHq(config, auth.profile) }