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
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:
59
apps/web/src/components/google-ads-signup-conversion.tsx
Normal file
59
apps/web/src/components/google-ads-signup-conversion.tsx
Normal 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;
|
||||
}
|
||||
@@ -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" />
|
||||
</>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user