feat: add PostHog event tracking for feature opt-in banner and dialog (#27579)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent
4367028492
commit
5804cc4fb0
@@ -3,7 +3,8 @@
|
||||
import type { OptInFeatureConfig } from "@calcom/features/feature-opt-in/config";
|
||||
import { getOptInFeatureConfig, shouldDisplayFeatureAt } from "@calcom/features/feature-opt-in/config";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import posthog from "posthog-js";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
getFeatureOptInTimestamp,
|
||||
isFeatureDismissed,
|
||||
@@ -28,6 +29,12 @@ type FeatureOptInMutations = {
|
||||
invalidateQueries: () => void;
|
||||
};
|
||||
|
||||
type FeatureOptInTrackingData = {
|
||||
enableFor: "user" | "organization" | "teams";
|
||||
teamCount?: number;
|
||||
autoOptIn: boolean;
|
||||
};
|
||||
|
||||
type UseFeatureOptInBannerResult = {
|
||||
shouldShow: boolean;
|
||||
isLoading: boolean;
|
||||
@@ -41,6 +48,7 @@ type UseFeatureOptInBannerResult = {
|
||||
dismiss: () => void;
|
||||
markOptedIn: () => void;
|
||||
mutations: FeatureOptInMutations;
|
||||
trackFeatureEnabled: (data: FeatureOptInTrackingData) => void;
|
||||
};
|
||||
|
||||
function isFeatureOptedIn(featureId: string): boolean {
|
||||
@@ -108,7 +116,12 @@ function useFeatureOptInBanner(featureId: string): UseFeatureOptInBannerResult {
|
||||
]
|
||||
);
|
||||
|
||||
const hasBannerShownBeenTracked = useRef(false);
|
||||
|
||||
const dismiss = useCallback(() => {
|
||||
posthog.capture("feature_opt_in_banner_dismissed", {
|
||||
feature_slug: featureId,
|
||||
});
|
||||
setFeatureDismissed(featureId);
|
||||
setIsDismissed(true);
|
||||
}, [featureId]);
|
||||
@@ -119,13 +132,28 @@ function useFeatureOptInBanner(featureId: string): UseFeatureOptInBannerResult {
|
||||
}, [featureId]);
|
||||
|
||||
const openDialog = useCallback(() => {
|
||||
posthog.capture("feature_opt_in_banner_try_it_clicked", {
|
||||
feature_slug: featureId,
|
||||
});
|
||||
setIsDialogOpen(true);
|
||||
}, []);
|
||||
}, [featureId]);
|
||||
|
||||
const closeDialog = useCallback(() => {
|
||||
setIsDialogOpen(false);
|
||||
}, []);
|
||||
|
||||
const trackFeatureEnabled = useCallback(
|
||||
(data: FeatureOptInTrackingData) => {
|
||||
posthog.capture("feature_opt_in_enabled", {
|
||||
feature_slug: featureId,
|
||||
enable_for: data.enableFor,
|
||||
team_count: data.teamCount,
|
||||
auto_opt_in: data.autoOptIn,
|
||||
});
|
||||
},
|
||||
[featureId]
|
||||
);
|
||||
|
||||
const shouldShow = useMemo(() => {
|
||||
if (isDismissed) return false;
|
||||
if (isOptedIn) return false;
|
||||
@@ -137,6 +165,15 @@ function useFeatureOptInBanner(featureId: string): UseFeatureOptInBannerResult {
|
||||
return eligibilityQuery.data.status === "can_opt_in";
|
||||
}, [isDismissed, isOptedIn, featureConfig, eligibilityQuery.isLoading, eligibilityQuery.data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldShow && !hasBannerShownBeenTracked.current) {
|
||||
hasBannerShownBeenTracked.current = true;
|
||||
posthog.capture("feature_opt_in_banner_shown", {
|
||||
feature_slug: featureId,
|
||||
});
|
||||
}
|
||||
}, [shouldShow, featureId]);
|
||||
|
||||
return {
|
||||
shouldShow,
|
||||
isLoading: eligibilityQuery.isLoading,
|
||||
@@ -150,6 +187,7 @@ function useFeatureOptInBanner(featureId: string): UseFeatureOptInBannerResult {
|
||||
dismiss,
|
||||
markOptedIn,
|
||||
mutations,
|
||||
trackFeatureEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,12 @@ type UserRoleContext = {
|
||||
adminTeamNames: { id: number; name: string }[];
|
||||
};
|
||||
|
||||
type FeatureOptInTrackingData = {
|
||||
enableFor: "user" | "organization" | "teams";
|
||||
teamCount?: number;
|
||||
autoOptIn: boolean;
|
||||
};
|
||||
|
||||
type FeatureOptInBannerState = {
|
||||
shouldShow: boolean;
|
||||
isLoading: boolean;
|
||||
@@ -28,6 +34,7 @@ type FeatureOptInBannerState = {
|
||||
dismiss: () => void;
|
||||
markOptedIn: () => void;
|
||||
mutations: FeatureOptInMutations;
|
||||
trackFeatureEnabled: (data: FeatureOptInTrackingData) => void;
|
||||
};
|
||||
|
||||
interface FeatureOptInBannerWrapperProps {
|
||||
@@ -60,6 +67,7 @@ function FeatureOptInBannerWrapper({ state }: FeatureOptInBannerWrapperProps): R
|
||||
featureConfig={state.featureConfig}
|
||||
userRoleContext={state.userRoleContext}
|
||||
mutations={state.mutations}
|
||||
onTrackFeatureEnabled={state.trackFeatureEnabled}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -41,6 +41,12 @@ export type FeatureOptInMutations = {
|
||||
invalidateQueries: () => void;
|
||||
};
|
||||
|
||||
type FeatureOptInTrackingData = {
|
||||
enableFor: "user" | "organization" | "teams";
|
||||
teamCount?: number;
|
||||
autoOptIn: boolean;
|
||||
};
|
||||
|
||||
interface FeatureOptInConfirmDialogProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
@@ -49,6 +55,7 @@ interface FeatureOptInConfirmDialogProps {
|
||||
featureConfig: OptInFeatureConfig;
|
||||
userRoleContext: UserRoleContext;
|
||||
mutations: FeatureOptInMutations;
|
||||
onTrackFeatureEnabled?: (data: FeatureOptInTrackingData) => void;
|
||||
}
|
||||
|
||||
export function FeatureOptInConfirmDialog({
|
||||
@@ -59,6 +66,7 @@ export function FeatureOptInConfirmDialog({
|
||||
featureConfig,
|
||||
userRoleContext,
|
||||
mutations,
|
||||
onTrackFeatureEnabled,
|
||||
}: FeatureOptInConfirmDialogProps): ReactElement {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
@@ -159,6 +167,13 @@ export function FeatureOptInConfirmDialog({
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
const enableFor = enableForOrg ? "organization" : hasTeamsSelected ? "teams" : "user";
|
||||
onTrackFeatureEnabled?.({
|
||||
enableFor,
|
||||
teamCount: hasTeamsSelected ? selectedTeamIds.length : undefined,
|
||||
autoOptIn,
|
||||
});
|
||||
|
||||
setIsSuccess(true);
|
||||
setShouldInvalidate(true);
|
||||
onOptInSuccess?.();
|
||||
|
||||
Reference in New Issue
Block a user