fix(analytics): OAuth Google Ads sign-up conversion #252

Merged
root merged 1 commits from dev into main 2026-08-01 09:16:42 +03:00
3 changed files with 68 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
import { useAuth } from "@/hooks/use-auth";
import { trackGoogleAdsConversion } from "@/lib/google-ads";
import { useEffect, useRef } from "react";
/**
* Universal post-auth Google Ads sign-up conversion.
*
* The register page fires the sign-up conversion only on the EMAIL path
* (register.tsx). Google OAuth signups — both the register-page Google button
* and the login-page Google button — complete server-side after a redirect and
* never fire it, so Google Ads saw ~0 of them (measured: ~79% of paid-driven
* signups were OAuth → invisible to Google Ads, starving Max Conversions of its
* signal). This mounts at the root and fires for any brand-new authenticated
* user, covering every signup path.
*
* Double-count safety: the conversion carries transaction_id `signup_<userId>`,
* which Google dedupes on — so an email signup that already fired on the
* register page counts once even though this fires again. The localStorage guard
* additionally stops repeat fires across the new user's first-session pageviews;
* register.tsx sets the same guard so email signups skip the redundant fire.
*
* Attribution: gtag's own `_gcl_aw` cookie (set from gclid on the ad landing)
* survives the same-domain OAuth round-trip, so firing here still attributes to
* the original click. Organic (non-ad) new users fire too, but Google only
* counts conversions it can tie to an ad click — the rest are ignored.
*/
const NEW_USER_WINDOW_MS = 30 * 60 * 1000;
function guardKey(userId: string): string {
return `sase-ga-signup-conv-${userId}`;
}
export function GoogleAdsSignupConversion() {
const { user, isLoading } = useAuth();
const firedRef = useRef(false);
useEffect(() => {
if (isLoading || !user || firedRef.current) return;
const createdAt = user.createdAt ? new Date(user.createdAt).getTime() : 0;
// Only genuinely fresh accounts — this is a signup conversion, not a login.
if (!createdAt || Date.now() - createdAt > NEW_USER_WINDOW_MS) return;
const key = guardKey(user.id);
try {
if (localStorage.getItem(key)) return;
localStorage.setItem(key, "1");
} catch {
// localStorage unavailable — firedRef still prevents in-session repeats.
}
firedRef.current = true;
trackGoogleAdsConversion(import.meta.env.VITE_GOOGLE_ADS_SIGNUP_LABEL, {
transactionId: `signup_${user.id}`,
email: user.email,
});
}, [user, isLoading]);
return null;
}

View File

@@ -1,4 +1,5 @@
import { SegmentGate } from "@/components/auth/segment-gate";
import { GoogleAdsSignupConversion } from "@/components/google-ads-signup-conversion";
import { InAppEscape } from "@/components/in-app-escape";
import { SurveyPopover } from "@/components/survey-popover";
import { useAuth } from "@/hooks/use-auth";
@@ -159,6 +160,7 @@ function RootComponent() {
<InAppEscape />
<Outlet />
<SegmentGate />
<GoogleAdsSignupConversion />
<SurveyPopover />
<Toaster position="top-center" />
</>

View File

@@ -189,6 +189,13 @@ function RegisterPage() {
transactionId: data?.user ? `signup_${data.user.id}` : undefined,
email,
});
// Guard so the universal post-auth conversion (which covers OAuth signups)
// skips this email signup — Google would dedupe on transaction_id anyway.
try {
if (data?.user) localStorage.setItem(`sase-ga-signup-conv-${data.user.id}`, "1");
} catch {
// localStorage unavailable — the transaction_id dedupe still protects us.
}
if (plan) localStorage.setItem(PENDING_PLAN_KEY, plan);
toast.success("Hesap oluşturuldu!");
window.location.href = redirectUrl;