diff --git a/src/app/listem/page.tsx b/src/app/listem/page.tsx
index 8ac28d0..e3a8453 100644
--- a/src/app/listem/page.tsx
+++ b/src/app/listem/page.tsx
@@ -20,12 +20,18 @@ export const metadata: Metadata = {
export default function ListemPage({
searchParams,
}: {
- searchParams: Promise<{ acildi?: string; uret?: string }>;
+ // kaynak: üretim denemesini hangi yüzeyin başlattığı (bkz.
+ // uretim-basladi-olayi.tsx). Doğrulaması okuyan tarafta, allowlist ile.
+ searchParams: Promise<{ acildi?: string; uret?: string; kaynak?: string }>;
}) {
return (
}>
- {searchParams.then(({ acildi, uret }) => (
-
+ {searchParams.then(({ acildi, uret, kaynak }) => (
+
))}
);
diff --git a/src/features/rapor/components/listem-icerik.tsx b/src/features/rapor/components/listem-icerik.tsx
index df114cb..388a9c6 100644
--- a/src/features/rapor/components/listem-icerik.tsx
+++ b/src/features/rapor/components/listem-icerik.tsx
@@ -20,6 +20,7 @@ import type { Mesaj } from "./sohbet-client";
import { ListemGovde } from "./listem-govde";
import { ListemBosCta } from "./listem-bos-cta";
import { ListeUretici } from "./liste-uretici";
+import { UretimBasladiOlayi } from "./uretim-basladi-olayi";
const PAKET_FIYATI = (URUNLER.paket.amountKurus / 100).toLocaleString("tr-TR", {
maximumFractionDigits: 0,
@@ -28,9 +29,13 @@ const PAKET_FIYATI = (URUNLER.paket.amountKurus / 100).toLocaleString("tr-TR", {
export async function ListemIcerik({
uret,
acildi,
+ kaynak,
}: {
uret: boolean;
acildi: boolean;
+ /** Üretim denemesini başlatan yüzey (`?kaynak=`); doğrulaması
+ * UretimBasladiOlayi'nın allowlist'inde. */
+ kaynak?: string;
}) {
// Tercihleri girili olan (profil çerezi duran) ziyaretçi girişten dönünce
// "Henüz listen yok" boşluğuyla karşılaşmasın: callback ?uret=1 taşır,
@@ -39,7 +44,14 @@ export async function ListemIcerik({
const cerezProfili = cerezdenTercihProfili(
(await cookies()).get(PROFIL_CEREZ_ADI)?.value,
);
- const geriYol = uret || cerezProfili ? "/listem?uret=1" : "/listem";
+ // `kaynak` giriş round-trip'inde korunur; yoksa girişten dönen herkes
+ // "bilinmiyor" okunurdu. `uret=1` HER ZAMAN ilk parametre kalmalı:
+ // giris-karti.tsx:31 funnel metnini `startsWith("/listem?uret=1")` ile
+ // seçiyor, sıra bozulursa "Seçimlerin hazır" metni kaybolur.
+ const geriYol =
+ uret || cerezProfili
+ ? `/listem?uret=1${kaynak ? `&kaynak=${encodeURIComponent(kaynak)}` : ""}`
+ : "/listem";
const session = await verifySession(geriYol);
const [user, satir, sohbetGecmisi] = await Promise.all([
getCurrentUser(),
@@ -71,11 +83,20 @@ export async function ListemIcerik({
{/* ?uret=1: sihirbazdan yönlendirildi — bekleyen seçimlerle üretim
burada koşar, animasyon ve hata/kredi durumları burada yaşar. */}
{uret ? (
-
+ <>
+ {/* Denemenin paydası: üretim gövdesiyle kardeş, aynı koşulda
+ mount olur (bkz. uretim-basladi-olayi.tsx). */}
+
+
+ >
) : (
bosDurum
)}
diff --git a/src/features/rapor/components/uretim-basladi-olayi.tsx b/src/features/rapor/components/uretim-basladi-olayi.tsx
new file mode 100644
index 0000000..d3bdff4
--- /dev/null
+++ b/src/features/rapor/components/uretim-basladi-olayi.tsx
@@ -0,0 +1,70 @@
+"use client";
+
+// Üretim denemesinin paydasını ölçen görünmez bileşen: /listem?uret=1 ile
+// üretim gövdesi kurulduğunda `liste_uretim_basladi` atar. Bugüne kadar
+// yalnız başarı (`liste_olusturuldu`) sayıldığı için "kaç kişi denedi, kaçı
+// listeye ulaştı" sorusunun paydası yoktu.
+//
+// Olay bilerek üretimi başlatan `liste-uretici.tsx`'te değil, onun kardeşi
+// olan bu dosyada duruyor (o dosya bu turda başka bir işin altında). İkisi de
+// `listem-icerik.tsx`'te aynı `uret` dalında mount edildiğinden koşulları
+// birebir aynı: biri mount oluyorsa diğeri de oluyor.
+
+import { useEffect, useRef } from "react";
+import { olay } from "@/lib/analitik";
+import type { TercihProfili } from "@/features/sihirbaz/sihirbaz-sabitler";
+import { tercihProfiliOku } from "@/features/sihirbaz/sihirbaz-profil";
+
+/**
+ * `?kaynak=` sözleşmesinin izinli değerleri. Değer URL'den, yani kullanıcının
+ * denetiminden geliyor: allowlist olmadan Rybbit'e keyfi dizgi yazılabilir
+ * (kardinalite patlaması, panele enjekte edilen metin). Listede olmayan her
+ * şey `"bilinmiyor"`a düşer. Yeni bir üretim yüzeyi eklerken değerini önce
+ * buraya ekle, sonra href'e koy.
+ */
+const IZINLI_KAYNAKLAR = new Set([
+ "sihirbaz",
+ "hazir_cta",
+ "giris_donusu",
+ "bos_durum",
+ "odeme_sonrasi",
+ "bilinmiyor",
+]);
+
+export function UretimBasladiOlayi({
+ kaynak,
+ paketli,
+ sunucuProfil,
+}: {
+ /** `?kaynak=` ham değeri; allowlist dışındaysa "bilinmiyor" raporlanır. */
+ kaynak: string;
+ paketli: boolean;
+ /** Profil çerezinden SSR'da çözülen yedek profil (bkz. listem-icerik). */
+ sunucuProfil: TercihProfili | null;
+}) {
+ // Mount başına tek atış. StrictMode geliştirmede effect'i iki kez koşuyor;
+ // `liste-uretici.tsx`'teki `basladi` ref'iyle aynı desen. `olayBirKez`
+ // BİLEREK kullanılmıyor: o oturum başına tekilleştirir, oysa aynı oturumda
+ // ikinci deneme de paydaya girmeli.
+ const atildi = useRef(false);
+
+ useEffect(() => {
+ if (atildi.current) return;
+ atildi.current = true;
+ const kaynakGuvenli = IZINLI_KAYNAKLAR.has(kaynak) ? kaynak : "bilinmiyor";
+ olay("liste_uretim_basladi", {
+ kaynak: kaynakGuvenli,
+ paketli: paketli ? "evet" : "hayir",
+ // Üretimin gerçekten başlayabilmesinin koşulu: bu ifade
+ // `liste-uretici.tsx`'teki `tercihProfiliOku() ?? sunucuProfil`
+ // ifadesinin ikizidir — ikisi ayrı dosyada olduğu için elle eşit
+ // tutulmalı. O dosya açıldığında (bkz. backlog: olayın gerçek çağrı
+ // yerine taşınması) tek yere alınacak. Ham sıralama/seçim GÖNDERİLMEZ
+ // (bkz. lib/analitik.ts kuralı) — yalnız profilin varlığı.
+ profil: (tercihProfiliOku() ?? sunucuProfil) ? "var" : "yok",
+ });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return null;
+}
diff --git a/src/lib/analitik.ts b/src/lib/analitik.ts
index 186ff70..147da7c 100644
--- a/src/lib/analitik.ts
+++ b/src/lib/analitik.ts
@@ -17,6 +17,12 @@ export type OlayAdi =
| "sihirbaz_adim"
| "sihirbaz_tamamlandi"
| "giris_denendi"
+ // Üretim denemesinin paydası: /listem?uret=1 ile üretim gövdesi mount
+ // olduğunda atılır (sihirbaz, hazır panel CTA'sı, giriş dönüşü, boş durum
+ // CTA'sı ve ödeme sonrası kartı — hangisi olduğu `kaynak` özelliğinde).
+ // Bilerek `olayBirKez` KULLANILMAZ: aynı oturumdaki ikinci deneme de
+ // sayılmalı, yoksa payda başarıdan (liste_olusturuldu) küçük kalır.
+ | "liste_uretim_basladi"
| "liste_olusturuldu"
| "liste_guncellendi"
| "liste_revize"