diff --git a/src/features/katalog/components/bolum-icerik.tsx b/src/features/katalog/components/bolum-icerik.tsx
index 5c4cbd1..a49608b 100644
--- a/src/features/katalog/components/bolum-icerik.tsx
+++ b/src/features/katalog/components/bolum-icerik.tsx
@@ -174,6 +174,9 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
const ilkSayfa = sayfa === 1;
const aralik = siraAraligiMetni(sayfaDilim);
const buYol = ilkSayfa ? `/bolum/${slug}` : `/bolum/${slug}/sayfa/${sayfa}`;
+ // Üst satır ve alt kart aynı soruyu sorar; sayfalama sayfalarında da bölüm
+ // adı geçer (ziyaretçi Google'dan doğrudan /sayfa/N'ye iniyor).
+ const ctaBaslik = `Sıralaman ${bolum.ad} tabanlarının neresinde?`;
return (
<>
@@ -258,6 +261,12 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
)}
+ {/* Tek sayfada çıkan ziyaretçi alttaki karta inmiyor: kompakt sıra girişi
+ başlık bloğunun hemen altında, listenin üstünde. Alttaki kart kalır. */}
+
+
+
+
diff --git a/src/features/katalog/components/universite-icerik.tsx b/src/features/katalog/components/universite-icerik.tsx
index 90da09d..3769ae6 100644
--- a/src/features/katalog/components/universite-icerik.tsx
+++ b/src/features/katalog/components/universite-icerik.tsx
@@ -169,6 +169,17 @@ export function UniversiteIcerik({
? `/universite/${slug}`
: `/universite/${slug}/sayfa/${sayfa}`;
const yurtdisi = uniUlkeKonum(uni.ad, uni.il);
+ // Üst satır ve alt kart aynı soruyu sorar; sayfalama sayfalarında da
+ // üniversite adı geçer (ziyaretçi Google'dan doğrudan /sayfa/N'ye iniyor).
+ const ctaBaslik = `Sıralaman ${uni.ad} tabanlarının neresinde?`;
+ // Tek sayfada çıkan ziyaretçi alttaki karta inmiyor: kompakt sıra girişi
+ // özet paragrafının hemen altında, tablonun (ve ilk sayfadaki istatistik
+ // ızgarasının) üstünde durur. Alttaki kart yerinde kalır.
+ const ustSiraSatiri = (
+
+
+
+ );
return (
<>
@@ -276,6 +287,8 @@ export function UniversiteIcerik({
+ {ustSiraSatiri}
+
@@ -319,22 +332,25 @@ export function UniversiteIcerik({
>
) : (
-
- {uni.ad} taban puanları tablosunun {sayfa}. sayfası (
- {sayi(tumProgramlar.length)} programın{" "}
- {sayi((sayfa - 1) * SAYFA_BOYU + 1)}–
- {sayi(Math.min(sayfa * SAYFA_BOYU, tumProgramlar.length))}.
- satırları, fakülte sırasıyla).
- {birimler ? ` Bu sayfadaki birimler: ${birimler}.` : ""} Üniversite
- özeti ve istatistikler{" "}
-
- ilk sayfada
-
- .
-
+ <>
+
+ {uni.ad} taban puanları tablosunun {sayfa}. sayfası (
+ {sayi(tumProgramlar.length)} programın{" "}
+ {sayi((sayfa - 1) * SAYFA_BOYU + 1)}–
+ {sayi(Math.min(sayfa * SAYFA_BOYU, tumProgramlar.length))}.
+ satırları, fakülte sırasıyla).
+ {birimler ? ` Bu sayfadaki birimler: ${birimler}.` : ""} Üniversite
+ özeti ve istatistikler{" "}
+
+ ilk sayfada
+
+ .
+
+ {ustSiraSatiri}
+ >
)}
@@ -363,7 +379,8 @@ export function UniversiteIcerik({
diff --git a/src/features/sihirbaz/components/cta-sira-form.tsx b/src/features/sihirbaz/components/cta-sira-form.tsx
index 738e5ce..8f69a7c 100644
--- a/src/features/sihirbaz/components/cta-sira-form.tsx
+++ b/src/features/sihirbaz/components/cta-sira-form.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useState } from "react";
+import { useId, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import {
@@ -28,6 +28,7 @@ import {
} from "./sihirbaz-adimlar-lazy";
import { authClient } from "@/lib/auth-client";
import { olay, siraKovasi } from "@/lib/analitik";
+import { cn } from "@/lib/utils";
import { onbellekliJsonGetir } from "@/lib/istek-cache";
import { useTercihProfili } from "@/features/liste/hooks/use-tercih-profili";
import type { SihirbazFacetleri } from "@/types/yokatlas";
@@ -65,16 +66,34 @@ const KANITLAR = [
* profilsiz karttaki paragrafı değiştirir — katalog sayfaları kartı üstündeki
* tabloya bağlayan köprü cümlesini buradan verir. Başlık ve açıklamalar
* yerleşme vaadi içermez: ürün sıralamaya yakın programları listeler.
+ *
+ * `varyant="kompakt"`: katalog sayfalarında başlığın hemen altına, tablonun
+ * ÜSTÜNE konan tek satırlık sürüm. Sıralama alanı çekmecede değil doğrudan
+ * sayfadadır (tek sayfada çıkan ziyaretçi alttaki karta hiç inmiyor); gönderince
+ * aynı çekmece sihirbaz adımıyla açılır. Aynı sayfada alttaki kartla birlikte
+ * durduğu için alan kimlikleri `useId` ile üretilir, analitik olayları da
+ * `konum` ("ust" | "alt") özelliğiyle ayrışır — olay adları değişmez.
*/
export function CtaSiraForm({
baslik,
aciklama,
+ varyant = "kart",
+ konum = "alt",
}: {
baslik?: string;
aciklama?: string;
+ varyant?: "kart" | "kompakt";
+ /** Yalnız ölçüm içindir: formun sayfadaki yeri (tablonun üstü / altı). */
+ konum?: "ust" | "alt";
}) {
const profil = useTercihProfili();
const router = useRouter();
+ const kompakt = varyant === "kompakt";
+ // Aynı sayfada iki form (üst satır + alt kart) olabildiği için sabit id yok.
+ const kimlik = useId();
+ const alanId = `${kimlik}-siralama`;
+ const hataId = `${kimlik}-siralama-hata`;
+ const baslikId = `${kimlik}-baslik`;
const [acik, setAcik] = useState(false);
const [adim, setAdim] = useState<"sira" | "sihirbaz">("sira");
@@ -87,8 +106,14 @@ export function CtaSiraForm({
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");
+ // özellik gerekmez — Rybbit olayın pathname'ini zaten yazar. `konum` hem
+ // özellik hem tekilleştirme ekidir: üstteki satır olayı atınca alttaki kartın
+ // gösterimi yutulmasın (oturum başına konum başına en fazla 1).
+ const formRef = useGorunumOlayi(
+ "cta_form_goruntulendi",
+ { konum },
+ konum,
+ );
// Yazarken binlik ayracıyla biçimle (85000 → 85.000)
function siralamaDegisti(ham: string) {
@@ -118,13 +143,17 @@ export function CtaSiraForm({
setSira(value);
setFacetler(veri.facetler);
setAdim("sihirbaz");
+ // Kompakt satırda sıralama sayfada girilir; çekmece ancak şimdi,
+ // doğrudan sihirbaz adımıyla açılır (kartta zaten açıktır).
+ setAcik(true);
olay("sira_girildi", {
kaynak: "cta",
+ konum,
tur,
sira_kovasi: siraKovasi(value),
});
} catch {
- olay("sira_form_hata", { kaynak: "cta" });
+ olay("sira_form_hata", { kaynak: "cta", konum });
setHata("Bir şeyler ters gitti, tekrar dener misin?");
} finally {
setYukleniyor(false);
@@ -146,13 +175,219 @@ export function CtaSiraForm({
function acikDegisti(v: boolean) {
setAcik(v);
if (!v) {
+ setHata(null);
+ // Kompakt satırda çekmecenin sıra adımı yoktur; her gönderim adımı ve
+ // facet'leri zaten baştan yazar (kapanış animasyonunda içerik boşalmasın).
+ if (kompakt) return;
// Yeniden açılışta sıfırdan başla; yazılan sıralama kaybolmasın.
setAdim("sira");
setFacetler(null);
- setHata(null);
}
}
+ // Sıra girişi: kartta çekmecenin ilk adımı, kompakt satırda sayfanın kendisi.
+ // Kompakt sürüm masaüstünde (lg) tek satırdır: çipler · alan · buton.
+ const siraFormu = (
+
+ );
+
+ // Çekmece iki varyantta da aynıdır; kompakt satırda sıra adımı sayfada
+ // olduğundan çekmece yalnız sihirbazı taşır.
+ const cekmece = (
+
+
+ {/* Görünür başlıklar adıma göre değişir; erişilebilir ad sabit kalır. */}
+
+ Sıralamana yakın programlar hangileri?
+
+
+ YKS başarı sıralamanı gir; sıralamana uygun programları bu sayfadan
+ ayrılmadan gör.
+
+
+
+ Kapat
+
+
+
+ {!kompakt && adim === "sira" ? (
+
+
+
Saniyede gör
+
+ Sıralamana yakın programlar hangileri?
+
+
+ Puan türünü seç, başarı sıralamanı yaz; hayal, dengeli ve
+ güvenli dilimlerindeki programları bu sayfadan ayrılmadan
+ gör. Ücretsiz, üyelik gerekmez.
+
+
+
+ {siraFormu}
+
+
+ {KANITLAR.map(({ ikon: Ikon, etiket }) => (
+
+
+ {etiket}
+
+ ))}
+
+
+ ) : facetler && sira != null ? (
+
+ ) : null}
+
+
+
+ );
+
+ if (kompakt) {
+ // CLS: kayıtlı profil hydration SONRASI bulunur. Satır tablonun üstünde
+ // olduğundan yüksekliği o anda değişmemeli — form yerinde (görünmez ve
+ // inert) kalır, profilli kısayol aynı ızgara hücresine bindirilir; hücrenin
+ // yüksekliğini her iki durumda da form belirler.
+ return (
+
+
+
+ {baslik ?? "Sıralaman bu tabanların neresinde?"}
+ {" "}
+ Ücretsiz, üyelik gerekmez.
+
+
+
+ {siraFormu}
+
+ {profil ? (
+
+
+ Sıralaman kayıtlı ({profil.sira.toLocaleString("tr-TR")}. sıra).
+
+
+ Sıralamana uygun programları gör
+
+
+
+ ) : null}
+
+ {cekmece}
+
+ );
+ }
+
if (profil) {
return (
@@ -198,138 +433,7 @@ export function CtaSiraForm({
-
-
- {/* Görünür başlıklar adıma göre değişir; erişilebilir ad sabit kalır. */}
-
- Sıralamana yakın programlar hangileri?
-
-
- YKS başarı sıralamanı gir; sıralamana uygun programları bu sayfadan
- ayrılmadan gör.
-
-
-
- Kapat
-
-
-
- {adim === "sira" ? (
-
-
-
Saniyede gör
-
- Sıralamana yakın programlar hangileri?
-
-
- Puan türünü seç, başarı sıralamanı yaz; hayal, dengeli ve
- güvenli dilimlerindeki programları bu sayfadan ayrılmadan
- gör. Ücretsiz, üyelik gerekmez.
-
-
-
-
-
-
- {KANITLAR.map(({ ikon: Ikon, etiket }) => (
-
-
- {etiket}
-
- ))}
-
-
- ) : facetler && sira != null ? (
-
- ) : null}
-
-
-
+ {cekmece}
);
}