fix(analytics): OAuth kayıtlarında Google Ads sign-up conversion'ı ateşle
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled

Conversion sadece register.tsx email path'inde ateşleniyordu; Google OAuth
signup'lar (register + login Google butonu) hiç ateşlemiyordu → ölçüm: paid-
kaynaklı kayıtların ~%79'u OAuth, Google Ads'e görünmüyordu, Max Conversions
sinyalsiz optimize ediyordu. Evrensel post-auth bileşeni (google-ads-signup-
conversion.tsx, __root'a mount) yeni kullanıcıda (createdAt < 30dk) conversion'ı
bir kez ateşler; transaction_id signup_<userId> Google tarafında dedup eder,
register.tsx guard'ı email path'in çift-ateşini önler. _gcl_aw cookie OAuth
roundtrip'inde korunduğu için atıf doğru. Playwright 6/6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-01 09:16:20 +03:00
parent 55b4cd38e9
commit 685c6af3ed
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 { SegmentGate } from "@/components/auth/segment-gate";
import { GoogleAdsSignupConversion } from "@/components/google-ads-signup-conversion";
import { InAppEscape } from "@/components/in-app-escape"; import { InAppEscape } from "@/components/in-app-escape";
import { SurveyPopover } from "@/components/survey-popover"; import { SurveyPopover } from "@/components/survey-popover";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
@@ -159,6 +160,7 @@ function RootComponent() {
<InAppEscape /> <InAppEscape />
<Outlet /> <Outlet />
<SegmentGate /> <SegmentGate />
<GoogleAdsSignupConversion />
<SurveyPopover /> <SurveyPopover />
<Toaster position="top-center" /> <Toaster position="top-center" />
</> </>

View File

@@ -189,6 +189,13 @@ function RegisterPage() {
transactionId: data?.user ? `signup_${data.user.id}` : undefined, transactionId: data?.user ? `signup_${data.user.id}` : undefined,
email, 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); if (plan) localStorage.setItem(PENDING_PLAN_KEY, plan);
toast.success("Hesap oluşturuldu!"); toast.success("Hesap oluşturuldu!");
window.location.href = redirectUrl; window.location.href = redirectUrl;