diff --git a/src/app/paket/layout.tsx b/src/app/paket/layout.tsx
new file mode 100644
index 0000000..7620f3e
--- /dev/null
+++ b/src/app/paket/layout.tsx
@@ -0,0 +1,16 @@
+import { PaketGorunumOlayi } from "@/features/odeme/components/paket-gorunum-olayi";
+
+// Yalnızca ölçüm için: sayfanın kendisine dokunmadan paket_goruntulendi
+// olayını bağlar (bkz. paket-gorunum-olayi.tsx).
+export default function PaketLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+ <>
+ {children}
+
+ >
+ );
+}
diff --git a/src/features/kullanici/components/giris-form.tsx b/src/features/kullanici/components/giris-form.tsx
index 2a14fe6..1510ece 100644
--- a/src/features/kullanici/components/giris-form.tsx
+++ b/src/features/kullanici/components/giris-form.tsx
@@ -59,6 +59,7 @@ export function GirisForm({ callbackURL }: { callbackURL: string }) {
callbackURL,
});
if (error) {
+ olay("giris_hata", { yontem: "google", status: error.status ?? 0 });
toast.error("Google ile giriş başarısız. Tekrar dener misin?");
setBusy(null);
}
@@ -81,6 +82,7 @@ export function GirisForm({ callbackURL }: { callbackURL: string }) {
});
setBusy(null);
if (error) {
+ olay("giris_hata", { yontem: "eposta", status: error.status ?? 0 });
toast.error("Bağlantı gönderilemedi. Tekrar dener misin?");
return;
}
diff --git a/src/features/kullanici/components/user-nav.tsx b/src/features/kullanici/components/user-nav.tsx
index 5b43f6a..9a45188 100644
--- a/src/features/kullanici/components/user-nav.tsx
+++ b/src/features/kullanici/components/user-nav.tsx
@@ -48,7 +48,7 @@ export async function UserNav() {
diff --git a/src/features/liste/components/tercih-profili-kapisi.tsx b/src/features/liste/components/tercih-profili-kapisi.tsx
index d9ca13a..7085430 100644
--- a/src/features/liste/components/tercih-profili-kapisi.tsx
+++ b/src/features/liste/components/tercih-profili-kapisi.tsx
@@ -163,6 +163,7 @@ function KapıIcerik({
sira_kovasi: siraKovasi(siraDeger),
});
} catch {
+ olay("sira_form_hata", { kaynak: duzenle ? "duzenle" : "kapi" });
setHata("Bir şeyler ters gitti, tekrar dener misin?");
} finally {
setYukleniyor(false);
diff --git a/src/features/odeme/components/paket-gorunum-olayi.tsx b/src/features/odeme/components/paket-gorunum-olayi.tsx
new file mode 100644
index 0000000..8d9a172
--- /dev/null
+++ b/src/features/odeme/components/paket-gorunum-olayi.tsx
@@ -0,0 +1,25 @@
+"use client";
+
+// /paket görüntülenmesini ve ziyaretçiyi oraya getiren yüzeyi ölçer.
+//
+// Neden ayrı bileşen + layout: /paket sayfası sunucu bileşeni ve statik
+// kalmalı. `useSearchParams` kullanılmaz — cacheComponents açıkken Suspense
+// sınırı/CSR bailout gerektirir; parametre yalnızca ölçüm için lazım, mount
+// sonrası window.location'dan okumak yeter.
+
+import { useEffect } from "react";
+import { olayBirKez } from "@/lib/analitik";
+
+// Serbest metin analitiğe sızmasın: bilinmeyen değerler "diger"e düşer.
+const KAYNAKLAR = ["kilit", "kredi_bitti", "nav"] as const;
+
+export function PaketGorunumOlayi() {
+ useEffect(() => {
+ const ham = new URLSearchParams(window.location.search).get("kaynak");
+ const kaynak = KAYNAKLAR.find((k) => k === ham) ?? "diger";
+ // Oturum başına kaynak başına 1 kez: yenileme sayıyı şişirmesin
+ olayBirKez("paket_goruntulendi", { kaynak }, kaynak);
+ }, []);
+
+ return null;
+}
diff --git a/src/features/rapor/components/liste-uretici.tsx b/src/features/rapor/components/liste-uretici.tsx
index b5f36dc..c355e0b 100644
--- a/src/features/rapor/components/liste-uretici.tsx
+++ b/src/features/rapor/components/liste-uretici.tsx
@@ -84,9 +84,11 @@ export function ListeUretici({
} else if (sonuc.code === "AUTH") {
router.push(`/giris?callback=${encodeURIComponent("/listem?uret=1")}`);
} else {
+ olay("liste_uretim_hata", { kod: sonuc.code });
setDurum({ ad: "hata", mesaj: sonuc.error });
}
} catch {
+ olay("liste_uretim_hata", { kod: "ISTISNA" });
setDurum({
ad: "hata",
mesaj: "Beklenmeyen bir hata oluştu, tekrar dener misin?",
@@ -131,7 +133,7 @@ export function ListeUretici({
size="lg"
className="mt-6 cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
>
-
+
{hasPaket ? "+30 kredi yükle — 129 TL" : "Paketi aktive et — 299 TL"}
diff --git a/src/features/rapor/components/listem-govde.tsx b/src/features/rapor/components/listem-govde.tsx
index 026a86c..28feb08 100644
--- a/src/features/rapor/components/listem-govde.tsx
+++ b/src/features/rapor/components/listem-govde.tsx
@@ -285,7 +285,7 @@ export function ListemGovde({
asChild
className="h-11 w-full shrink-0 cursor-pointer bg-orange-500 text-white transition-[background-color,transform] duration-200 active:scale-[0.97] hover:bg-orange-600 sm:w-auto"
>
-
+
Analizleri aç — {paketFiyati} TL
@@ -366,7 +366,7 @@ export function ListemGovde({
size="lg"
className="mt-5 w-full cursor-pointer bg-orange-500 text-white transition-[background-color,transform] duration-200 active:scale-[0.97] hover:bg-orange-600"
>
-
+
Kişisel analizi aç
diff --git a/src/features/sihirbaz/components/cta-sira-form.tsx b/src/features/sihirbaz/components/cta-sira-form.tsx
index d35a757..6003c8f 100644
--- a/src/features/sihirbaz/components/cta-sira-form.tsx
+++ b/src/features/sihirbaz/components/cta-sira-form.tsx
@@ -21,6 +21,7 @@ import {
DrawerTitle,
} from "@/components/ui/drawer";
import { SectionEyebrow } from "@/components/pixel-decor";
+import { useGorunumOlayi } from "@/components/use-gorunum-olayi";
import {
SihirbazAdimlarLazy,
preloadSihirbazAdimlar,
@@ -73,6 +74,10 @@ export function CtaSiraForm({ baslik }: { baslik?: string }) {
const [yukleniyor, setYukleniyor] = useState(false);
const [hata, setHata] = useState(null);
const [girisli, setGirisli] = useState(false);
+ // Huni girişi ölçümü: ref yalnızca profilsiz daldaki karta bağlanır; kayıtlı
+ // profilli ziyaretçi formu hiç görmediğinden sayılmaz. Sayfa ayrımı için
+ // özellik gerekmez — Rybbit olayın pathname'ini zaten yazar.
+ const formRef = useGorunumOlayi("cta_form_goruntulendi");
// Yazarken binlik ayracıyla biçimle (85000 → 85.000)
function siralamaDegisti(ham: string) {
@@ -108,6 +113,7 @@ export function CtaSiraForm({ baslik }: { baslik?: string }) {
sira_kovasi: siraKovasi(value),
});
} catch {
+ olay("sira_form_hata", { kaynak: "cta" });
setHata("Bir şeyler ters gitti, tekrar dener misin?");
} finally {
setYukleniyor(false);
@@ -159,7 +165,10 @@ export function CtaSiraForm({ baslik }: { baslik?: string }) {
}
return (
-
+
{baslik ?? "Sıralamanla nereye yerleşirsin? Saniyede gör."}
diff --git a/src/features/sihirbaz/components/hero-form.tsx b/src/features/sihirbaz/components/hero-form.tsx
index 484746b..ec4f94e 100644
--- a/src/features/sihirbaz/components/hero-form.tsx
+++ b/src/features/sihirbaz/components/hero-form.tsx
@@ -85,6 +85,7 @@ export function HeroForm() {
sira_kovasi: siraKovasi(value),
});
} catch {
+ olay("sira_form_hata", { kaynak: "hero" });
setHata("Bir şeyler ters gitti, tekrar dener misin?");
} finally {
setYukleniyor(false);
diff --git a/src/features/sihirbaz/components/sihirbaz-cagri-karti.tsx b/src/features/sihirbaz/components/sihirbaz-cagri-karti.tsx
index 2568f83..d5a3f90 100644
--- a/src/features/sihirbaz/components/sihirbaz-cagri-karti.tsx
+++ b/src/features/sihirbaz/components/sihirbaz-cagri-karti.tsx
@@ -9,7 +9,7 @@ import { useReducedMotion } from "motion/react";
import { Button } from "@/components/ui/button";
import { PixelField } from "@/components/pixel-decor";
import { useGorunumOlayi } from "@/components/use-gorunum-olayi";
-import { olay } from "@/lib/analitik";
+import { olay, olayBirKez, siraKovasi } from "@/lib/analitik";
import type { SihirbazFacetleri } from "@/types/yokatlas";
import { SIHIRBAZ_AC_EVENT, SIHIRBAZ_STORAGE_KEY, sihirbazDogrula, type SihirbazSecimleri } from "@/features/sihirbaz/sihirbaz-sabitler";
import { tercihProfiliYaz } from "@/features/sihirbaz/sihirbaz-profil";
@@ -82,6 +82,22 @@ export function SihirbazBolumu({
// görsün diye ref'te tutulur; olay dinleyicisi bir kez bağlanır.
const bekleyenRef = useRef(null);
+ // Huni adımı: sonuç sayfası görüntülendi. URL sıralamayı taşıdığı için
+ // pathname maskeli (bkz. app/layout.tsx); huni bu olayla kurulur. Oturum
+ // başına girişli/anonim için birer kez — girişten dönen ziyaretçi ikinci
+ // kez "girisli" olarak sayılır. Ham sıra gönderilmez, yalnızca kovası.
+ useEffect(() => {
+ olayBirKez(
+ "sonuc_goruntulendi",
+ {
+ girisli: girisliMi ? "evet" : "hayir",
+ sira_kovasi: siraKovasi(sira),
+ },
+ girisliMi ? "girisli" : "anonim",
+ );
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
// Mount sonrası storage'dan bekleyen seçimleri oku
useEffect(() => {
try {
diff --git a/src/lib/analitik.ts b/src/lib/analitik.ts
index c60c552..b7dedb5 100644
--- a/src/lib/analitik.ts
+++ b/src/lib/analitik.ts
@@ -30,7 +30,13 @@ export type OlayAdi =
| "kilit_goruntulendi"
| "kredi_bitti"
| "tadimlik_goruntulendi"
- | "kaydet_banner_goruntulendi";
+ | "kaydet_banner_goruntulendi"
+ | "cta_form_goruntulendi"
+ | "sira_form_hata"
+ | "sonuc_goruntulendi"
+ | "paket_goruntulendi"
+ | "liste_uretim_hata"
+ | "giris_hata";
export type Ozellikler = Record;