fix(sonuc,liste): tadımlık listesi olanda tablonun altına iner, çekmecede risk etiketi metinle
- /sonuc: kayıtlı listesi olan ziyaretçide harita ile ekleme tablosu arasına tadımlık CTA'sı girmez, tablonun altına iner (karar sunucudaki liste sayısı çerezinden, hidrasyonda zıplamaz) - Liste çekmecesi: risk noktası tek başına değil RISK_ETIKET metniyle - Sıralama formu: kompakt başlık düzeni
This commit is contained in:
@@ -106,14 +106,19 @@ export default function SonucPage({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tadimlik = <TadimlikBolumu sira={sira} tur={turKey} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-slate-50 text-slate-900">
|
<div className="min-h-screen bg-slate-50 text-slate-900">
|
||||||
<main className="mx-auto max-w-4xl px-4 py-12">
|
<main className="mx-auto max-w-4xl px-4 py-12">
|
||||||
{/* Sayfa hiyerarşisi: manuel listenin haritası → tadımlık → ham
|
{/* Sayfa hiyerarşisi: manuel listenin haritası → ham tablo →
|
||||||
tablo. İki farklı ziyaretçi, iki farklı ilk ekran:
|
tadımlık (listesi yoksa tadımlık → tablo). İki farklı
|
||||||
|
ziyaretçi, iki farklı ilk ekran:
|
||||||
|
|
||||||
• Listesi OLAN ziyaretçi ekranda önce kendi kurduğu listeyi
|
• Listesi OLAN ziyaretçi ekranda önce kendi kurduğu listeyi
|
||||||
görür — o kişi "bu ürün ne?" aşamasını geçmiştir.
|
görür — o kişi "bu ürün ne?" aşamasını geçmiştir. Haritanın
|
||||||
|
hemen altında ekleme alanı (tablo) durur; tadımlık CTA'sı
|
||||||
|
ikisinin arasına girmez, tablonun altına iner.
|
||||||
• Listesi OLMAYAN ziyaretçide harita DOM'a hiç girmez
|
• Listesi OLMAYAN ziyaretçide harita DOM'a hiç girmez
|
||||||
(manuel-harita.tsx), sıra bugünküyle birebir aynı kalır:
|
(manuel-harita.tsx), sıra bugünküyle birebir aynı kalır:
|
||||||
tadımlık → tablo. Tadımlık, ödeme öncesi gösterebildiğimiz
|
tadımlık → tablo. Tadımlık, ödeme öncesi gösterebildiğimiz
|
||||||
@@ -131,8 +136,12 @@ export default function SonucPage({
|
|||||||
<ManuelHarita yerAyir={haritayaYerAyir} />
|
<ManuelHarita yerAyir={haritayaYerAyir} />
|
||||||
|
|
||||||
{/* Girişsiz tadımlık: gerçek rapor satırı formatında tek örnek.
|
{/* Girişsiz tadımlık: gerçek rapor satırı formatında tek örnek.
|
||||||
Havuzda içerik yoksa bileşen hiç render edilmez. */}
|
Havuzda içerik yoksa bileşen hiç render edilmez. Listesi
|
||||||
<TadimlikBolumu sira={sira} tur={turKey} />
|
OLAN ziyaretçide harita ile tablo (+ ile ekleme alanı)
|
||||||
|
arasına CTA girmesin diye tadımlık tablonun altına iner;
|
||||||
|
karar sunucudaki liste sayısı çerezinden verilir, böylece
|
||||||
|
hidrasyonda yer değiştirip zıplamaz. */}
|
||||||
|
{haritayaYerAyir ? null : tadimlik}
|
||||||
|
|
||||||
{/* Yapay Zeka'dan bağımsız ham tablo — sayfanın ana gövdesi;
|
{/* Yapay Zeka'dan bağımsız ham tablo — sayfanın ana gövdesi;
|
||||||
puan türü navbar'daki sıralama rozetinden değiştirilir.
|
puan türü navbar'daki sıralama rozetinden değiştirilir.
|
||||||
@@ -145,6 +154,8 @@ export default function SonucPage({
|
|||||||
uniturGrubu={uniturGrubu}
|
uniturGrubu={uniturGrubu}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{haritayaYerAyir ? tadimlik : null}
|
||||||
|
|
||||||
{/* Sihirbaz akışı (CTA + modal) — üretim ve liste /listem'de
|
{/* Sihirbaz akışı (CTA + modal) — üretim ve liste /listem'de
|
||||||
yaşar, burada yalnızca funnel kartları render edilir */}
|
yaşar, burada yalnızca funnel kartları render edilir */}
|
||||||
<SonucSihirbazi
|
<SonucSihirbazi
|
||||||
|
|||||||
@@ -422,10 +422,13 @@ function CekmeceSatiri({
|
|||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
{tercih.risk ? (
|
{tercih.risk ? (
|
||||||
|
<span className="inline-flex shrink-0 items-center gap-1.5 text-xs font-medium">
|
||||||
<span
|
<span
|
||||||
className={`size-2.5 shrink-0 rounded-full ${RISK_NOKTA[tercih.risk]}`}
|
className={`size-2.5 shrink-0 rounded-full ${RISK_NOKTA[tercih.risk]}`}
|
||||||
aria-label={RISK_ETIKET[tercih.risk]}
|
aria-hidden
|
||||||
/>
|
/>
|
||||||
|
{RISK_ETIKET[tercih.risk]}
|
||||||
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -120,11 +120,11 @@ export function TadimlikSatiri({
|
|||||||
const girisUrl = `/giris?callback=${encodeURIComponent(listemUretHref("tadimlik"))}`;
|
const girisUrl = `/giris?callback=${encodeURIComponent(listemUretHref("tadimlik"))}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// first:mt-0 — /sonuc'ta harita bu <section>'ın ÜSTÜNDE durur ama yalnızca
|
// first:mt-0 — kayıtlı listesi olan ziyaretçide /sonuc tadımlığı haritanın
|
||||||
// kayıtlı listesi olan ziyaretçide DOM'a girer (manuel-harita.tsx). Liste
|
// ve tablonun ALTINA koyar (sonuc/page.tsx), orada bölüm ritmini veren
|
||||||
// yoksa tadımlık main'in ilk DOM çocuğudur (JSX yorumları düğüm üretmez) ve
|
// mt-16'yı alır. Liste yoksa tadımlık main'in ilk DOM çocuğudur (JSX
|
||||||
// main'in py-12'si tek başına yeterli üst boşluğu verir; harita varsa
|
// yorumları düğüm üretmez) ve main'in py-12'si tek başına yeterli üst
|
||||||
// tadımlık ikinci çocuk olur ve bölüm ritmini veren mt-16'yı alır.
|
// boşluğu verir.
|
||||||
// Tadımlığın render edilmediği durumlarda (girişli kullanıcı, boş havuz)
|
// 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.
|
// bu bileşen hiç dönmediği için kural ilk gerçek bölüme geçer.
|
||||||
<section ref={gorunumRef} className="mt-16 min-w-0 first:mt-0">
|
<section ref={gorunumRef} className="mt-16 min-w-0 first:mt-0">
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
DrawerDescription,
|
DrawerDescription,
|
||||||
DrawerTitle,
|
DrawerTitle,
|
||||||
} from "@/components/ui/drawer";
|
} from "@/components/ui/drawer";
|
||||||
import { SectionEyebrow } from "@/components/pixel-decor";
|
import { PagePixelDivider, SectionEyebrow } from "@/components/pixel-decor";
|
||||||
import { useGorunumOlayi } from "@/components/use-gorunum-olayi";
|
import { useGorunumOlayi } from "@/components/use-gorunum-olayi";
|
||||||
import {
|
import {
|
||||||
SihirbazAdimlarLazy,
|
SihirbazAdimlarLazy,
|
||||||
@@ -51,6 +51,31 @@ const KANITLAR = [
|
|||||||
{ ikon: Database, etiket: "YÖK Atlas verisi" },
|
{ ikon: Database, etiket: "YÖK Atlas verisi" },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
// Kayıtlı profilli CTA'lar /listem boş durumunun dilini taşır (bkz.
|
||||||
|
// listem-bos-cta.tsx): büyük turuncu buton, piksel ayraç. Sınıf seti tek yerde.
|
||||||
|
const PROFILLI_CTA_SINIF =
|
||||||
|
"h-12 cursor-pointer bg-orange-500 px-6 text-white transition-colors duration-200 hover:bg-orange-600";
|
||||||
|
|
||||||
|
function turEtiketi(tur: string) {
|
||||||
|
return PUAN_TURLERI_SECENEK.find((t) => t.deger === tur)?.etiket ?? tur.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** "Sayısal · 100.000. sıra" — kayıtlı profilin kısa özeti. */
|
||||||
|
function KayitliSira({ sira, tur }: { sira: number; tur: string }) {
|
||||||
|
return (
|
||||||
|
<span className="inline-flex items-center gap-1.5 text-sm text-slate-600">
|
||||||
|
<Trophy className="size-4 shrink-0 text-primary" aria-hidden />
|
||||||
|
<span>
|
||||||
|
Sıralaman kayıtlı:{" "}
|
||||||
|
<strong className="font-semibold tabular-nums text-slate-900">
|
||||||
|
{sira.toLocaleString("tr-TR")}.
|
||||||
|
</strong>{" "}
|
||||||
|
sıra · {turEtiketi(tur)}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Programatik SEO sayfalarındaki funnel girişi. Statik/SSR çıktı her zaman
|
* Programatik SEO sayfalarındaki funnel girişi. Statik/SSR çıktı her zaman
|
||||||
* profilsiz hâldir (sayfalar prerender edilmeye devam eder); hydration
|
* profilsiz hâldir (sayfalar prerender edilmeye devam eder); hydration
|
||||||
@@ -344,42 +369,53 @@ export function CtaSiraForm({
|
|||||||
if (kompakt) {
|
if (kompakt) {
|
||||||
// CLS: kayıtlı profil hydration SONRASI bulunur. Satır tablonun üstünde
|
// 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
|
// 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
|
// inert) kalır, profilli kısayol (başlığıyla birlikte) aynı ızgara hücresine
|
||||||
// yüksekliğini her iki durumda da form belirler.
|
// bindirilir; hücrenin yüksekliğini her iki durumda da başlık + form belirler.
|
||||||
|
// Profilli içerik bu yüksekliği aşmayacak kadar kısa tutulmalı.
|
||||||
|
const kompaktBaslik = baslik ?? "Sıralaman bu tabanların neresinde?";
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
aria-labelledby={baslikId}
|
aria-labelledby={baslikId}
|
||||||
className="rounded-2xl border border-slate-200 bg-white p-4 sm:p-5"
|
className={cn(
|
||||||
|
"rounded-2xl border p-4 transition-colors duration-200 sm:p-5",
|
||||||
|
profil ? "border-primary/20 bg-primary/5" : "border-slate-200 bg-white",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<p className="text-sm leading-6 text-slate-600">
|
<div className="grid">
|
||||||
<strong
|
|
||||||
id={baslikId}
|
|
||||||
className="font-heading text-base font-bold text-slate-900"
|
|
||||||
>
|
|
||||||
{baslik ?? "Sıralaman bu tabanların neresinde?"}
|
|
||||||
</strong>{" "}
|
|
||||||
Ücretsiz, üyelik gerekmez.
|
|
||||||
</p>
|
|
||||||
<div className="mt-3 grid">
|
|
||||||
<div
|
<div
|
||||||
ref={profil ? undefined : formRef}
|
ref={profil ? undefined : formRef}
|
||||||
inert={profil ? true : undefined}
|
inert={profil ? true : undefined}
|
||||||
className={cn("col-start-1 row-start-1", profil && "invisible")}
|
className={cn("col-start-1 row-start-1", profil && "invisible")}
|
||||||
>
|
>
|
||||||
{siraFormu}
|
<p className="text-sm leading-6 text-slate-600">
|
||||||
|
<strong
|
||||||
|
id={profil ? undefined : baslikId}
|
||||||
|
className="font-heading text-base font-bold text-slate-900"
|
||||||
|
>
|
||||||
|
{kompaktBaslik}
|
||||||
|
</strong>{" "}
|
||||||
|
Ücretsiz, üyelik gerekmez.
|
||||||
|
</p>
|
||||||
|
<div className="mt-3">{siraFormu}</div>
|
||||||
</div>
|
</div>
|
||||||
{profil ? (
|
{profil ? (
|
||||||
<div className="col-start-1 row-start-1 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
<div className="col-start-1 row-start-1 flex flex-col items-center justify-center gap-3 text-center sm:flex-row sm:justify-between sm:gap-6 sm:text-left">
|
||||||
<p className="text-sm leading-6 text-slate-600">
|
<div className="flex min-w-0 flex-col items-center gap-1 sm:items-start">
|
||||||
Sıralaman kayıtlı ({profil.sira.toLocaleString("tr-TR")}. sıra).
|
<p
|
||||||
</p>
|
id={baslikId}
|
||||||
<Link
|
className="font-heading text-base font-bold leading-6 text-slate-900"
|
||||||
href={sonucHref()}
|
|
||||||
className="inline-flex h-11 items-center justify-center gap-2 rounded-full bg-orange-500 px-6 text-sm font-semibold text-white transition-colors duration-200 hover:bg-orange-600"
|
|
||||||
>
|
>
|
||||||
|
{kompaktBaslik}
|
||||||
|
</p>
|
||||||
|
<KayitliSira sira={profil.sira} tur={profil.tur} />
|
||||||
|
</div>
|
||||||
|
<PagePixelDivider seed={67} className="w-24 sm:hidden" />
|
||||||
|
<Button asChild size="lg" className={cn(PROFILLI_CTA_SINIF, "w-full sm:w-auto")}>
|
||||||
|
<Link href={sonucHref()}>
|
||||||
Sıralamana uygun programları gör
|
Sıralamana uygun programları gör
|
||||||
<ArrowRight className="size-4" aria-hidden />
|
<ArrowRight className="size-4" aria-hidden />
|
||||||
</Link>
|
</Link>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
@@ -390,22 +426,24 @@ export function CtaSiraForm({
|
|||||||
|
|
||||||
if (profil) {
|
if (profil) {
|
||||||
return (
|
return (
|
||||||
<section className="rounded-2xl border border-primary/20 bg-primary/5 p-6 sm:p-8">
|
<section className="flex flex-col items-center rounded-2xl border border-primary/20 bg-primary/5 px-6 py-8 text-center sm:px-10 sm:py-10">
|
||||||
<h2 className="font-heading text-xl font-bold text-slate-900">
|
<h2 className="font-heading text-xl font-bold text-balance text-slate-900 sm:text-2xl">
|
||||||
{baslik ?? "Sıralamana yakın programlar hangileri? Saniyede gör."}
|
{baslik ?? "Sıralamana yakın programlar hangileri? Saniyede gör."}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-2 text-sm leading-6 text-slate-600">
|
<div className="mt-3">
|
||||||
Sıralaman kayıtlı ({profil.sira.toLocaleString("tr-TR")}. sıra) —
|
<KayitliSira sira={profil.sira} tur={profil.tur} />
|
||||||
gerçek YÖK Atlas verisiyle hayal, dengeli ve güvenli dilimlerindeki
|
</div>
|
||||||
|
<p className="mt-2 max-w-xl text-sm leading-6 text-slate-600">
|
||||||
|
Gerçek YÖK Atlas verisiyle hayal, dengeli ve güvenli dilimlerindeki
|
||||||
programların hazır.
|
programların hazır.
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<PagePixelDivider seed={67} className="mt-6" />
|
||||||
href={sonucHref()}
|
<Button asChild size="lg" className={cn(PROFILLI_CTA_SINIF, "mt-6")}>
|
||||||
className="mt-4 inline-flex h-12 items-center justify-center gap-2 rounded-full bg-orange-500 px-7 text-sm font-semibold text-white transition-colors duration-200 hover:bg-orange-600"
|
<Link href={sonucHref()}>
|
||||||
>
|
|
||||||
Sıralamana uygun programları gör
|
Sıralamana uygun programları gör
|
||||||
<ArrowRight className="size-4" aria-hidden />
|
<ArrowRight className="size-4" aria-hidden />
|
||||||
</Link>
|
</Link>
|
||||||
|
</Button>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user