From cb22faf62853f8b020fc354723b26be970fe4003 Mon Sep 17 00:00:00 2001
From: bilalgursen
Date: Tue, 22 Sep 2026 01:21:16 +0300
Subject: [PATCH 1/5] =?UTF-8?q?feat(funnel):=20navbar'da=20kal=C4=B1c?=
=?UTF-8?q?=C4=B1=20Yapay=20Zeka=20listesi=20giri=C5=9Fi?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Girişli kullanıcının /listem'e navbar'dan ulaşacak yolu yoktu: çekmece
butonu (Kendi Listem) manuel liste boşken tamamen gizleniyor, kredi pill'i
de /paket'e gidiyor. Girişli 14 oturumun 7'si /listem'i hiç görmedi.
Kredi pill'iyle birebir aynı sınıf setinde bir /listem girişi eklendi;
ikon ClipboardList (çekmecenin ListChecks'i ve kredinin Coins'i ile
karışmasın). Rapor var mı diye ek DB sorgusu yok — navbar'ın TTFB'si
bozulmasın, boş sayfada ListemBosCta karşılıyor.
Co-Authored-By: Claude Opus 5
---
.../kullanici/components/user-nav.tsx | 20 ++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/src/features/kullanici/components/user-nav.tsx b/src/features/kullanici/components/user-nav.tsx
index 9a45188..f762460 100644
--- a/src/features/kullanici/components/user-nav.tsx
+++ b/src/features/kullanici/components/user-nav.tsx
@@ -1,5 +1,5 @@
import Link from "next/link";
-import { Coins } from "lucide-react";
+import { ClipboardList, Coins } from "lucide-react";
import { getCurrentUser } from "@/lib/session";
import { Button } from "@/components/ui/button";
import { ListemButonu } from "@/features/liste/components/listem-butonu";
@@ -47,6 +47,24 @@ export async function UserNav() {
+ {/* Yapay Zeka raporunun kalıcı kapısı: girişli kullanıcı /listem'i
+ navbar'dan bulamıyordu (çekmece butonu manuel liste boşken hiç
+ görünmüyor). Rapor var mı diye DB'ye sorulmuyor — navbar'ın TTFB'si
+ bozulmasın; liste boşsa sayfadaki ListemBosCta karşılıyor.
+ Sınıf seti kredi pill'iyle birebir aynı (tek kaynak). */}
+
+
+ {/* ListemButonu:39 ile aynı görünürlük deseni: 375 px'te ve lg–xl
+ arasında metin gizli, ikon+aria-label anlamı taşıyor. */}
+
+ Yapay Zeka Listem
+
+
Date: Tue, 22 Sep 2026 01:21:32 +0300
Subject: [PATCH 2/5] =?UTF-8?q?fix(funnel):=20navbar=20giri=C5=9F=20butonu?=
=?UTF-8?q?=20kullan=C4=B1c=C4=B1y=C4=B1=20bulundu=C4=9Fu=20sayfaya=20d?=
=?UTF-8?q?=C3=B6nd=C3=BCr=C3=BCr?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Navbar'daki "Giriş yap" callback vermiyordu; giris-karti.tsx callback'siz
isteği "/"'a düşürdüğü için rehber ya da katalog okuyan ziyaretçi girişten
sonra ana sayfada uyanıyordu.
Yeni GirisLinki bileşeni usePathname ile bulunulan yolu callback yapıyor.
Callback bilerek sabit bir hedef (ör. /listem) değil: buton her sayfada
duruyor, sabit hedef bağlam kaybı olur; /listem?uret=1 yazılsaydı girişten
dönüşte üretim kendiliğinden başlar, kullanıcının ayrı onayı olmadan 3
kredi yanardı. Sorgu dizesi taşınmıyor — sorgu okuyan kanca tüm navbar'ı
Suspense zorunluluğuna sokardı.
Co-Authored-By: Claude Opus 5
---
.../kullanici/components/giris-linki.tsx | 40 +++++++++++++++++++
.../kullanici/components/user-nav.tsx | 9 +----
2 files changed, 42 insertions(+), 7 deletions(-)
create mode 100644 src/features/kullanici/components/giris-linki.tsx
diff --git a/src/features/kullanici/components/giris-linki.tsx b/src/features/kullanici/components/giris-linki.tsx
new file mode 100644
index 0000000..c9b9358
--- /dev/null
+++ b/src/features/kullanici/components/giris-linki.tsx
@@ -0,0 +1,40 @@
+"use client";
+
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import { Button } from "@/components/ui/button";
+
+/**
+ * Navbar'ın "Giriş yap" butonu: giriş bitince kullanıcıyı **bulunduğu
+ * sayfaya** geri döndürür (`/giris?callback=`). Callback verilmezse
+ * `giris-karti.tsx` "/"'a düşürüyordu — rehber okuyan ziyaretçi girişten
+ * sonra ana sayfada uyanıyordu.
+ *
+ * Callback neden sabit bir hedef (ör. `/listem`) değil: buton her sayfada
+ * duruyor, sabit hedef bağlam kaybı olurdu; `/listem?uret=1` yazılsaydı
+ * girişten dönüşte üretim kendiliğinden başlar ve kullanıcının ayrı onayı
+ * olmadan 3 kredi yakardı.
+ *
+ * Callback yalnız pathname taşır: sorgu dizesini okuyan kanca tüm navbar'ı
+ * Suspense zorunluluğuna sokardı. `usePathname` bu ağaçta zaten var
+ * (`header-gate`), `UserNav` da site-header'da Suspense içinde render
+ * ediliyor — cacheComponents açıkken dinamik parametreli rotalarda gereken
+ * sınır hazır (next/dist/docs/.../use-pathname.md).
+ */
+export function GirisLinki() {
+ const pathname = usePathname();
+ // /giris kendine callback veremez (döngü); pathname yoksa sade hedef.
+ const href =
+ pathname && !pathname.startsWith("/giris")
+ ? `/giris?callback=${encodeURIComponent(pathname)}`
+ : "/giris";
+
+ return (
+
+ );
+}
diff --git a/src/features/kullanici/components/user-nav.tsx b/src/features/kullanici/components/user-nav.tsx
index f762460..b58f107 100644
--- a/src/features/kullanici/components/user-nav.tsx
+++ b/src/features/kullanici/components/user-nav.tsx
@@ -1,10 +1,10 @@
import Link from "next/link";
import { ClipboardList, Coins } from "lucide-react";
import { getCurrentUser } from "@/lib/session";
-import { Button } from "@/components/ui/button";
import { ListemButonu } from "@/features/liste/components/listem-butonu";
import { ListeSenkron } from "@/features/liste/components/liste-senkron";
import { RybbitIdentify } from "@/components/rybbit-identify";
+import { GirisLinki } from "./giris-linki";
import { SignOutButton } from "./sign-out-button";
/**
@@ -32,12 +32,7 @@ export async function UserNav() {
-
+
);
}
From 419b617cab369e7eb1609b7a6f6746a19aebbe7d Mon Sep 17 00:00:00 2001
From: bilalgursen
Date: Tue, 22 Sep 2026 01:21:46 +0300
Subject: [PATCH 3/5] =?UTF-8?q?fix(listem):=20bo=C5=9F=20durum=20CTA's?=
=?UTF-8?q?=C4=B1=20ana=20sayfaya=20atmaz,=20tercih=20kap=C4=B1s=C4=B1n?=
=?UTF-8?q?=C4=B1=20a=C3=A7ar?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Profil yokken CTA kullanıcıyı "/"'a gönderiyordu: /listem'e kadar gelmiş
adayı ana sayfaya geri atmak huninin ortasında bağlam kaybı. Artık kapı
aynı sayfada açılıyor (hedef "sihirbaz", tanıtım adımı atlanıyor);
tamamlanınca mevcut yol işliyor — /sonuc?sihirbaz=1 → /listem?uret=1.
Profilli dalda hedefe kaynak etiketi eklendi (uret=1 ilk parametre kalır).
Co-Authored-By: Claude Opus 5
---
.../rapor/components/listem-bos-cta.tsx | 38 +++++++++++++------
1 file changed, 27 insertions(+), 11 deletions(-)
diff --git a/src/features/rapor/components/listem-bos-cta.tsx b/src/features/rapor/components/listem-bos-cta.tsx
index b4ff8c0..b97e70d 100644
--- a/src/features/rapor/components/listem-bos-cta.tsx
+++ b/src/features/rapor/components/listem-bos-cta.tsx
@@ -4,12 +4,20 @@ import Link from "next/link";
import { ArrowRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import { PagePixelDivider } from "@/components/pixel-decor";
-import { useTercihProfili } from "@/features/liste/hooks/use-tercih-profili";
+import {
+ profilToplamaAc,
+ useTercihProfili,
+} from "@/features/liste/hooks/use-tercih-profili";
+
+// Her iki dalın butonu da aynı CTA: sınıf seti tek yerde dursun.
+const CTA_SINIF = "mt-6 cursor-pointer bg-orange-500 text-white hover:bg-orange-600";
/**
* /listem boş hâlinin metni + CTA'sı: seçimleri zaten hazır olan adayı
* /sonuc üzerinden dolaştırmadan doğrudan üretime (?uret=1) alır; profil
- * yoksa ana sayfadaki sıralama formuna yönlendirir.
+ * yoksa kullanıcıyı ana sayfaya atmadan tercih kapısını **burada** açar.
+ * Kapı bitince `profilTamamlandi` "sihirbaz" hedefini /sonuc?sihirbaz=1'e
+ * çevirir, oradan /listem?uret=1'e dönülür — ayrı bir dal yok.
*/
export function ListemBosCta() {
const profil = useTercihProfili();
@@ -22,16 +30,24 @@ export function ListemBosCta() {
: "Sıralamanı girip birkaç adımda 24 tercihlik risk analizli listeni kur; sonra burada görüntüleyip Yapay Zeka danışmanla üzerinde konuşabilirsin."}
-
+ )}
>
);
}
From 1364f8cb43e16db0d5140ec758b56bc54881b605 Mon Sep 17 00:00:00 2001
From: bilalgursen
Date: Tue, 22 Sep 2026 01:33:01 +0300
Subject: [PATCH 4/5] =?UTF-8?q?feat(olcum):=20/listem=20bo=C5=9F=20ekran?=
=?UTF-8?q?=C4=B1ndan=20a=C3=A7=C4=B1lan=20kap=C4=B1=20ayr=C4=B1=20tetikle?=
=?UTF-8?q?=20say=C4=B1l=C4=B1r?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bu kapı huninin en sonunda açılıyor (giriş yaptı, listesi yok, profili de
yok). "diger" ile sayılınca arama kartı gibi genel yüzeylerle aynı kovaya
düşüyordu; ayrı değer bu dilimi görünür kılar.
Co-Authored-By: Claude Opus 5
---
src/features/liste/hooks/use-tercih-profili.ts | 4 ++++
src/features/rapor/components/listem-bos-cta.tsx | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/features/liste/hooks/use-tercih-profili.ts b/src/features/liste/hooks/use-tercih-profili.ts
index 3771ac7..1e77c03 100644
--- a/src/features/liste/hooks/use-tercih-profili.ts
+++ b/src/features/liste/hooks/use-tercih-profili.ts
@@ -52,6 +52,10 @@ export type KapiTetik =
| "arti_toast"
| "sonuc"
| "sihirbaz"
+ // Girişli ama listesiz kullanıcının /listem boş ekranındaki CTA'sı. Ayrı
+ // değer, çünkü bu kapı huninin en sonunda açılıyor: "giriş yaptı ama profili
+ // yok" dilimini arama kartı gibi genel yüzeylerden ayırt edebilmek gerek.
+ | "listem_bos"
| "duzenle"
| "diger";
diff --git a/src/features/rapor/components/listem-bos-cta.tsx b/src/features/rapor/components/listem-bos-cta.tsx
index b97e70d..06a1966 100644
--- a/src/features/rapor/components/listem-bos-cta.tsx
+++ b/src/features/rapor/components/listem-bos-cta.tsx
@@ -42,7 +42,7 @@ export function ListemBosCta() {
type="button"
size="lg"
className={CTA_SINIF}
- onClick={() => profilToplamaAc("sihirbaz", true, "diger")}
+ onClick={() => profilToplamaAc("sihirbaz", true, "listem_bos")}
>
Sıralamanı gir, Yapay Zeka listeni kur
From 248a01042f6f201c05019dd3224b210edbd54d5c Mon Sep 17 00:00:00 2001
From: bilalgursen
Date: Tue, 22 Sep 2026 01:36:59 +0300
Subject: [PATCH 5/5] =?UTF-8?q?fix(giris):=20callback=20yaln=C4=B1z=20bett?=
=?UTF-8?q?er-auth'un=20kabul=20etti=C4=9Fi=20yollarda=20tak=C4=B1l=C4=B1r?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
better-auth 1.6.23 callback'i kendi göreli-yol kalkanından geçiriyor ve \w
ASCII olduğu için /rehber/ösym ya da /bolum/t%C4%B1p gibi yolları 403
INVALID_CALLBACK_URL ile reddediyordu: kullanıcı o sayfalardan hiç giriş
yapamıyordu. Callback artık yalnız auth'un da kabul edeceği yollarda
takılıyor, gerisinde sade /giris'e düşülüyor.
Co-Authored-By: Claude Opus 5
---
src/features/kullanici/components/giris-linki.tsx | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/features/kullanici/components/giris-linki.tsx b/src/features/kullanici/components/giris-linki.tsx
index c9b9358..ccdbc75 100644
--- a/src/features/kullanici/components/giris-linki.tsx
+++ b/src/features/kullanici/components/giris-linki.tsx
@@ -21,11 +21,23 @@ import { Button } from "@/components/ui/button";
* ediliyor — cacheComponents açıkken dinamik parametreli rotalarda gereken
* sınır hazır (next/dist/docs/.../use-pathname.md).
*/
+/**
+ * better-auth (1.6.23) callback'i kendi göreli-yol kalkanından geçiriyor:
+ * `^\/(?!\/|\\|%2f|%5c)[\w\-.+/@]*(?:\?…)?$`. `\w` ASCII olduğu için
+ * `/rehber/ösym` ya da yüzde-kodlanmış `/bolum/t%C4%B1p` gibi yollar
+ * **403 INVALID_CALLBACK_URL** ile reddediliyor ve kullanıcı o sayfadan hiç
+ * giriş yapamıyor (main'de callback hep "/" olduğu için bu yol yoktu).
+ * Bu yüzden callback'i ancak auth katmanının da kabul edeceği yollarda
+ * takıyoruz; gerisinde sade /giris'e düşüyoruz — bağlam kaybı, kırık giriş
+ * akışına yeğdir. Kalkan daraltılırsa (tek yer) buradaki de gevşetilebilir.
+ */
+const AUTH_GUVENLI_YOL = /^\/(?!\/|\\)[\w\-./+@]*$/;
+
export function GirisLinki() {
const pathname = usePathname();
// /giris kendine callback veremez (döngü); pathname yoksa sade hedef.
const href =
- pathname && !pathname.startsWith("/giris")
+ pathname && !pathname.startsWith("/giris") && AUTH_GUVENLI_YOL.test(pathname)
? `/giris?callback=${encodeURIComponent(pathname)}`
: "/giris";