"use client"; // Girişsiz yapay zekâ tadımlığı (PRD İP-B): /sonuc'ta, sıra kovası + (varsa) // sihirbaz alan tercihine uyan TEK örnek tercih satırı — gerçek rapor // satırının detay bloğuyla (RaporSatirDetayi) birebir aynı formatta. // İçerik batch üretilmiş havuzdan gelir; bu bileşen LLM'e istek attırmaz. // SSR "genel" satırı render eder (prop), mount sonrası sihirbaz kategorisi // varsa /api/tadimlik'ten kategoriye özel satır çekilip yerinde değiştirilir // (seçim sunucuda kalır). Havuzda içerik yoksa hiç render edilmez. import { useEffect, useRef, useState } from "react"; import Link from "next/link"; import { ArrowRight } from "lucide-react"; import { Button } from "@/components/ui/button"; import { UniLogo } from "@/components/uni-logo"; import { SectionEyebrow } from "@/components/pixel-decor"; import { RaporSatirDetayi } from "./rapor-listesi"; import { ProgramSiraGecmisi, ProgramTabanTrendi, programEtkinSira, raporSiraSerisi, } from "@/components/program-liste-verileri"; import { useGorunumOlayi } from "@/components/use-gorunum-olayi"; import { olay, siraKovasi } from "@/lib/analitik"; import { dilimdenRisk, riskHesapla } from "@/lib/risk"; import { turkishSlugify } from "@/lib/slug"; import { listemUretHref, PROFIL_DEGISTI_EVENT } from "@/features/sihirbaz/sihirbaz-sabitler"; import { tercihProfiliOku } from "@/features/sihirbaz/sihirbaz-profil"; import type { TadimlikSatir, TadimlikTip } from "@/lib/tadimlik-havuzu"; const GENEL = "genel"; /** Program tipi ile sihirbaz tercihi eşleşiyor mu (uniturFiltreSql semantiği). */ function tipeUyar(program: TadimlikSatir["program"], tip: TadimlikTip): boolean { if (tip === "genel") return true; if (tip === "devlet") return program.unitur === "DEVLET"; return program.unitur != null && program.unitur !== "DEVLET"; } export function TadimlikSatiri({ sira, tur, ilk, }: { sira: number; tur: string; /** SSR'da seçilmiş "genel" satır; havuz boşsa null. */ ilk: TadimlikSatir | null; }) { const [satir, setSatir] = useState(ilk); const yuklenenAnahtar = useRef(`${GENEL}|${GENEL}`); // Sihirbaz kategorisi ve devlet/vakıf tercihi yalnızca localStorage'da // yaşar; mount'ta ve profil değiştikçe okunur, seçime özel satır sunucudan // çekilir. Tip tercihi varsa yanlış tipte örnek bir an bile gösterilmez. useEffect(() => { const guncelle = () => { const profil = tercihProfiliOku(); const kategori = profil?.secimler.kategoriler[0]; const slug = kategori ? turkishSlugify(kategori) : GENEL; const tipTercihi = profil?.secimler.universiteTipi; const tip: TadimlikTip = tipTercihi === "devlet" || tipTercihi === "vakif" ? tipTercihi : GENEL; const anahtar = `${slug}|${tip}`; if (anahtar === yuklenenAnahtar.current) return; yuklenenAnahtar.current = anahtar; if (slug === GENEL && tip === GENEL) { setSatir(ilk); return; } // SSR "genel" satırı seçilen tipe uymuyorsa fetch bitene kadar gizle setSatir((mevcut) => mevcut && !tipeUyar(mevcut.program, tip) ? null : mevcut, ); const q = new URLSearchParams({ sira: String(sira), tur }); if (slug !== GENEL) q.set("kategori", slug); if (tip !== GENEL) q.set("tip", tip); fetch(`/api/tadimlik?${q}`) .then((yanit) => (yanit.ok ? yanit.json() : null)) .then((veri: { tadimlik: TadimlikSatir | null } | null) => { // Yarış koruması: bu arada başka seçim yüklendiyse dokunma if (yuklenenAnahtar.current !== anahtar) return; if (veri?.tadimlik) { setSatir(veri.tadimlik); } else if (tip !== GENEL) { // Uygun tipte satır yok: yanlış tip göstermektense gizle setSatir(null); } }) .catch(() => {}); }; guncelle(); window.addEventListener(PROFIL_DEGISTI_EVENT, guncelle); return () => window.removeEventListener(PROFIL_DEGISTI_EVENT, guncelle); }, [sira, tur, ilk]); // SERİ KIRILMASI (22 Eyl 2026): bu olay bu değişiklikten önce /sonuc'un // ~2.200 px altında, tablonun arkasında yaşıyordu; artık tadımlık ilk // ekranda. Adet bir gecede 0'dan yukarı fırlayacak — kırık ölçüm değil, // kazanılan payda. Öncesi ve sonrası KARŞILAŞTIRILAMAZ; anlamlı ölçüt // bundan sonra "tadımlık CTA tıklaması / tadımlık görüntülenmesi" oranıdır. // (Not analitik.ts'e değil buraya yazıldı: o dosya aynı gece başka bir // dalın altında — CTO şartnamesi, çakışma kararı #1.) const gorunumRef = useGorunumOlayi("tadimlik_goruntulendi", { kova: siraKovasi(sira), }); if (!satir) return null; const p = satir.program; const risk = riskHesapla(p.efektifSira, sira) ?? dilimdenRisk(satir.dilim); const seri = raporSiraSerisi(p.siraGecmisi, p.ilkYil); const taban = p.efektifSira ?? programEtkinSira(seri); const devlet = p.unitur === "DEVLET"; // Giriş dönüşü kullanıcıyı /sonuc'a geri bırakmaz: callback doğrudan // üretimin koştuğu /listem'e iner, liste ek tık olmadan orada başlar. const girisUrl = `/giris?callback=${encodeURIComponent(listemUretHref("tadimlik"))}`; return ( // first:mt-0 — /sonuc'ta bu
main'in ilk DOM çocuğudur (harita // artık tablonun altında, üstte hiçbir kardeş yok; JSX yorumları düğüm // üretmez): main'in py-12'si tek başına yeterli üst boşluğu verir. // Tadımlığın render edilmediği durumlarda (girişli kullanıcı, boş havuz) // bu bileşen hiç dönmediği için kural ilk gerçek bölüme geçer.
Yapay zekâdan örnek satır

Yapay zekâ listendeki her satırı böyle anlatır

{/* "ilk üçü" = rapor-maske.ts içindeki ACIK_SATIR (3). O dosya `server-only`, bu dosya "use client" — sabit buraya import edilemiyor, sayı elle yazıldı. ACIK_SATIR değişirse bu cümle de aynı commit'te değişmeli. Cümle kasten "her satır bu formatta kurulur"u sınırlıyor: maske girişsiz/paketsiz kullanıcıya 3 satır açıyor, kalan 21'in gerekçe/risk/trend metni maskeli geliyor. */}

Sıralamana uygun bir programın gerekçesi, yerleşme riski ve taban trendi — 24 tercihlik listenin her satırı bu formatta kurulur, ilk üçü hemen açılır.

{p.isim} {p.unitur ? ( {devlet ? "Devlet" : "Vakıf"} ) : null} {p.universite} {p.il ? ` · ${p.il.toLocaleLowerCase("tr-TR")}` : ""} {taban != null ? `~${taban.toLocaleString("tr-TR")}.` : "—"}

Bu, 24 satırlık Yapay Zeka listenden yalnızca bir örnek. Kredi kartı gerekmez · Google ya da e-posta ile 30 saniyede · Giriş sonrası liste hemen üretilir

); }