diff --git a/apps/code/src/renderer/features/auth/hooks/useAuthSession.ts b/apps/code/src/renderer/features/auth/hooks/useAuthSession.ts index a8ed01f4c..f3b946ce9 100644 --- a/apps/code/src/renderer/features/auth/hooks/useAuthSession.ts +++ b/apps/code/src/renderer/features/auth/hooks/useAuthSession.ts @@ -12,7 +12,7 @@ import { useSeatStore } from "@features/billing/stores/seatStore"; import { useFeatureFlag } from "@hooks/useFeatureFlag"; import { trpcClient } from "@renderer/trpc/client"; import { BILLING_FLAG } from "@shared/constants"; -import { identifyUser, resetUser } from "@utils/analytics"; +import { identifyUser, resetUser, setUserGroups } from "@utils/analytics"; import { logger } from "@utils/logger"; import { queryClient } from "@utils/queryClient"; import { useEffect } from "react"; @@ -72,6 +72,8 @@ function useAuthAnalyticsIdentity( region: authState.cloudRegion ?? "", }); + setUserGroups(currentUser); + void trpcClient.analytics.setUserId.mutate({ userId: distinctId, properties: { diff --git a/apps/code/src/renderer/features/auth/stores/authStore.test.ts b/apps/code/src/renderer/features/auth/stores/authStore.test.ts index a160efb19..f5d0ec951 100644 --- a/apps/code/src/renderer/features/auth/stores/authStore.test.ts +++ b/apps/code/src/renderer/features/auth/stores/authStore.test.ts @@ -55,6 +55,7 @@ vi.mock("@renderer/api/posthogClient", () => ({ vi.mock("@utils/analytics", () => ({ identifyUser: vi.fn(), resetUser: vi.fn(), + setUserGroups: vi.fn(), track: vi.fn(), })); @@ -83,7 +84,7 @@ vi.mock("@stores/navigationStore", () => ({ }, })); -import { resetUser } from "@utils/analytics"; +import { resetUser, setUserGroups } from "@utils/analytics"; import { queryClient } from "@utils/queryClient"; import { resetAuthStoreModuleStateForTest, useAuthStore } from "./authStore"; @@ -166,6 +167,7 @@ describe("authStore", () => { await useAuthStore.getState().checkCodeAccess(); expect(mockGetCurrentUser).toHaveBeenCalledTimes(1); + expect(setUserGroups).toHaveBeenCalledTimes(1); }); it("clears user identity and cached current user on implicit auth loss", async () => { diff --git a/apps/code/src/renderer/features/auth/stores/authStore.ts b/apps/code/src/renderer/features/auth/stores/authStore.ts index 2373c9778..8de660445 100644 --- a/apps/code/src/renderer/features/auth/stores/authStore.ts +++ b/apps/code/src/renderer/features/auth/stores/authStore.ts @@ -6,7 +6,12 @@ import { ANALYTICS_EVENTS } from "@shared/types/analytics"; import type { CloudRegion } from "@shared/types/regions"; import { getCloudUrlFromRegion } from "@shared/utils/urls"; import { useNavigationStore } from "@stores/navigationStore"; -import { identifyUser, resetUser, track } from "@utils/analytics"; +import { + identifyUser, + resetUser, + setUserGroups, + track, +} from "@utils/analytics"; import { logger } from "@utils/logger"; import { queryClient } from "@utils/queryClient"; import { create } from "zustand"; @@ -165,6 +170,8 @@ async function syncAuthState(): Promise { region: authState.cloudRegion ?? "", }); + setUserGroups(user); + trpcClient.analytics.setUserId.mutate({ userId: distinctId, properties: { diff --git a/apps/code/src/renderer/utils/analytics.ts b/apps/code/src/renderer/utils/analytics.ts index 23e9a971b..3e106d6a5 100644 --- a/apps/code/src/renderer/utils/analytics.ts +++ b/apps/code/src/renderer/utils/analytics.ts @@ -100,6 +100,33 @@ export function identifyUser( posthog.identify(userId, properties); } +type UserWithGroups = { + team?: { id: number; uuid: string; name: string } | null; + organization?: { id: string; name: string; slug: string } | null; +}; + +export function setUserGroups(user: UserWithGroups) { + if (!isInitialized) { + return; + } + + if (user.team) { + posthog.group("project", user.team.uuid, { + id: user.team.id, + uuid: user.team.uuid, + name: user.team.name, + }); + } + + if (user.organization) { + posthog.group("organization", user.organization.id, { + id: user.organization.id, + name: user.organization.name, + slug: user.organization.slug, + }); + } +} + export function resetUser() { if (!isInitialized) { return;