"use client"; import { useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { ArrowRight, ChartNoAxesCombined, Database, ListChecks, Trophy, X, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerTitle, } from "@/components/ui/drawer"; import { SectionEyebrow } from "@/components/pixel-decor"; import { SihirbazAdimlarLazy, preloadSihirbazAdimlar, } from "@/components/sihirbaz-adimlar-lazy"; import { authClient } from "@/lib/auth-client"; import { olay, siraKovasi } from "@/lib/analitik"; import { useTercihProfili } from "@/components/manuel-liste/profil-kapisi-store"; import type { SihirbazFacetleri } from "@/types/yokatlas"; import { sonucHref, tercihProfiliYaz, type SihirbazSecimleri, } from "@/lib/sihirbaz"; // lib/db sunucu tarafı; etiketler client bundle'a sızmasın diye burada. const PUAN_TURLERI_SECENEK = [ { deger: "say", etiket: "Sayısal" }, { deger: "ea", etiket: "Eşit Ağırlık" }, { deger: "soz", etiket: "Sözel" }, { deger: "dil", etiket: "Dil" }, { deger: "tyt", etiket: "TYT" }, ] as const; // Kanıt üçlüsü arama funnel kartıyla aynı dili konuşur (bkz. arama-funnel-karti) const KANITLAR = [ { ikon: ListChecks, etiket: "24 tercihlik plan" }, { ikon: ChartNoAxesCombined, etiket: "Risk analizi" }, { ikon: Database, etiket: "YÖK Atlas verisi" }, ] as const; /** * Programatik SEO sayfalarındaki funnel girişi. Statik/SSR çıktı her zaman * profilsiz hâldir (sayfalar prerender edilmeye devam eder); hydration * sonrası kayıtlı tercih profili bulunursa "sıralamanı gir" yerine doğrudan * sonuç sayfasına götüren CTA gösterilir — sıralamasını girmiş adaya bir * daha girmesini söylemeyiz. * * Profilsiz CTA adayı ana sayfaya göndermez: buton alttan bir çekmece açar, * sıralama girişi ve sihirbaz adayın bulunduğu sayfanın bağlamında akar. * Tasarım gerekçesi için bkz. docs/tasarim/cta-cekmece-tasarimi.md. */ export function CtaSiraForm({ baslik }: { baslik?: string }) { const profil = useTercihProfili(); const router = useRouter(); const [acik, setAcik] = useState(false); const [adim, setAdim] = useState<"sira" | "sihirbaz">("sira"); const [siralama, setSiralama] = useState(""); const [tur, setTur] = useState("say"); const [sira, setSira] = useState(null); const [facetler, setFacetler] = useState(null); const [yukleniyor, setYukleniyor] = useState(false); const [hata, setHata] = useState(null); const [girisli, setGirisli] = useState(false); // Yazarken binlik ayracıyla biçimle (85000 → 85.000) function siralamaDegisti(ham: string) { setHata(null); const rakamlar = ham.replace(/\D/g, "").slice(0, 7); setSiralama(rakamlar ? Number(rakamlar).toLocaleString("tr-TR") : ""); } async function siraGonder(e: React.FormEvent) { e.preventDefault(); const value = Number(siralama.replace(/\./g, "")); if (!value || value < 1 || value > 4_000_000) { setHata("Geçerli bir başarı sıralaması gir (ör. 85.000)."); return; } setYukleniyor(true); try { // Oturum bilgisi facet isteğiyle PARALEL çözülür; sayfa statik kalır // (hero formundaki desenle aynı — bkz. hero-form.tsx). const [res, oturum] = await Promise.all([ fetch(`/api/facetler?sira=${value}&tur=${tur}`), authClient.getSession().catch(() => null), ]); if (!res.ok) throw new Error(); const veri = (await res.json()) as { facetler: SihirbazFacetleri }; setGirisli(Boolean(oturum?.data?.user)); setSira(value); setFacetler(veri.facetler); setAdim("sihirbaz"); olay("sira_girildi", { kaynak: "cta", tur, sira_kovasi: siraKovasi(value), }); } catch { setHata("Bir şeyler ters gitti, tekrar dener misin?"); } finally { setYukleniyor(false); } } // Seçimler tamam: sakla, çekmeceyi kapat ve /sonuc'a git. Girişli // kullanıcıda orada otomatik üretim başlar; girişsiz kullanıcı önce // ücretsiz tabloyu görür (değer önce — giriş duvarı sihirbazın hemen // arkasında değil). function secimleriTamamla(secimler: SihirbazSecimleri) { if (sira == null) return; const yeniProfil = { sira, tur, secimler }; tercihProfiliYaz(yeniProfil); setAcik(false); router.push( sonucHref(yeniProfil, girisli ? { sihirbaz: "1" } : { hazir: "1" }), ); } function acikDegisti(v: boolean) { setAcik(v); if (!v) { // Yeniden açılışta sıfırdan başla; yazılan sıralama kaybolmasın. setAdim("sira"); setFacetler(null); setHata(null); } } if (profil) { return (

{baslik ?? "Sıralamanla nereye yerleşirsin? Saniyede gör."}

Sıralaman kayıtlı ({profil.sira.toLocaleString("tr-TR")}. sıra) — gerçek YÖK Atlas verisiyle hayal, dengeli ve garanti dilimlerindeki programların hazır.

Sıralamana uygun programları gör
); } return (

{baslik ?? "Sıralamanla nereye yerleşirsin? Saniyede gör."}

YKS başarı sıralamanı gir; gerçek YÖK Atlas verisiyle sıralamana uygun hayal, dengeli ve garanti dilimlerindeki programları anında listele. Ücretsiz, üyelik gerekmez.

{/* Görünür başlıklar adıma göre değişir; erişilebilir ad sabit kalır. */} Sıralamanla nereye yerleşirsin? YKS başarı sıralamanı gir; sıralamana uygun programları bu sayfadan ayrılmadan gör. Kapat
{adim === "sira" ? (
Saniyede gör

Sıralamanla nereye yerleşirsin?

Puan türünü seç, başarı sıralamanı yaz; hayal, dengeli ve garanti dilimlerindeki programları bu sayfadan ayrılmadan gör. Ücretsiz, üyelik gerekmez.

{PUAN_TURLERI_SECENEK.map((t) => ( ))}
siralamaDegisti(e.target.value)} onFocus={preloadSihirbazAdimlar} aria-invalid={hata ? true : undefined} aria-describedby={ hata ? "cta-siralama-hata" : undefined } className="h-12 bg-background pl-11 text-base" />
{hata ? ( ) : null}
    {KANITLAR.map(({ ikon: Ikon, etiket }) => (
  • {etiket}
  • ))}
) : facetler && sira != null ? ( ) : null}
); }