feat(katalog): başlığın altında kompakt sıra giriş satırı (alttaki kart yerinde)
Katalog 464 oturum → 12 sıra girişi; kart tablonun altında kalıyordu. CtaSiraForm'a varyant="kompakt" + konum prop'u; olay adları aynı, konum: ust|alt özelliği eklendi; alan kimlikleri useId ile (aynı sayfada iki form). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -174,6 +174,9 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
|
|||||||
const ilkSayfa = sayfa === 1;
|
const ilkSayfa = sayfa === 1;
|
||||||
const aralik = siraAraligiMetni(sayfaDilim);
|
const aralik = siraAraligiMetni(sayfaDilim);
|
||||||
const buYol = ilkSayfa ? `/bolum/${slug}` : `/bolum/${slug}/sayfa/${sayfa}`;
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -258,6 +261,12 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
|
|||||||
)}
|
)}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
{/* 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. */}
|
||||||
|
<div className="mt-6">
|
||||||
|
<CtaSiraForm varyant="kompakt" konum="ust" baslik={ctaBaslik} />
|
||||||
|
</div>
|
||||||
|
|
||||||
<section aria-labelledby="universite-programlari" className="mt-8">
|
<section aria-labelledby="universite-programlari" className="mt-8">
|
||||||
<div className="mb-4 flex items-end justify-between gap-4">
|
<div className="mb-4 flex items-end justify-between gap-4">
|
||||||
<h2
|
<h2
|
||||||
@@ -286,7 +295,8 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
|
|||||||
{/* Üniversite sayfasıyla aynı konum ve aynı kart: liste + sayfalama altı */}
|
{/* Üniversite sayfasıyla aynı konum ve aynı kart: liste + sayfalama altı */}
|
||||||
<div className="mt-10">
|
<div className="mt-10">
|
||||||
<CtaSiraForm
|
<CtaSiraForm
|
||||||
baslik={`Sıralaman ${bolum.ad} tabanlarının neresinde?`}
|
konum="alt"
|
||||||
|
baslik={ctaBaslik}
|
||||||
aciklama={KATALOG_CTA_ACIKLAMA}
|
aciklama={KATALOG_CTA_ACIKLAMA}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -169,6 +169,17 @@ export function UniversiteIcerik({
|
|||||||
? `/universite/${slug}`
|
? `/universite/${slug}`
|
||||||
: `/universite/${slug}/sayfa/${sayfa}`;
|
: `/universite/${slug}/sayfa/${sayfa}`;
|
||||||
const yurtdisi = uniUlkeKonum(uni.ad, uni.il);
|
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 = (
|
||||||
|
<div className="mt-6">
|
||||||
|
<CtaSiraForm varyant="kompakt" konum="ust" baslik={ctaBaslik} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -276,6 +287,8 @@ export function UniversiteIcerik({
|
|||||||
</em>
|
</em>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{ustSiraSatiri}
|
||||||
|
|
||||||
<div className="mt-6 grid grid-cols-2 gap-3 lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2">
|
<div className="mt-6 grid grid-cols-2 gap-3 lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2">
|
||||||
<UniversiteKonumHaritasi universite={uni.ad} il={uni.il} />
|
<UniversiteKonumHaritasi universite={uni.ad} il={uni.il} />
|
||||||
<dl className="contents">
|
<dl className="contents">
|
||||||
@@ -319,6 +332,7 @@ export function UniversiteIcerik({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
<>
|
||||||
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">
|
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">
|
||||||
{uni.ad} taban puanları tablosunun {sayfa}. sayfası (
|
{uni.ad} taban puanları tablosunun {sayfa}. sayfası (
|
||||||
{sayi(tumProgramlar.length)} programın{" "}
|
{sayi(tumProgramlar.length)} programın{" "}
|
||||||
@@ -335,6 +349,8 @@ export function UniversiteIcerik({
|
|||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
|
{ustSiraSatiri}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<PagePixelDivider seed={57} className="mt-8" />
|
<PagePixelDivider seed={57} className="mt-8" />
|
||||||
@@ -363,7 +379,8 @@ export function UniversiteIcerik({
|
|||||||
|
|
||||||
<div className="mt-10">
|
<div className="mt-10">
|
||||||
<CtaSiraForm
|
<CtaSiraForm
|
||||||
baslik={`Sıralaman ${uni.ad} tabanlarının neresinde?`}
|
konum="alt"
|
||||||
|
baslik={ctaBaslik}
|
||||||
aciklama={KATALOG_CTA_ACIKLAMA}
|
aciklama={KATALOG_CTA_ACIKLAMA}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useId, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
} from "./sihirbaz-adimlar-lazy";
|
} from "./sihirbaz-adimlar-lazy";
|
||||||
import { authClient } from "@/lib/auth-client";
|
import { authClient } from "@/lib/auth-client";
|
||||||
import { olay, siraKovasi } from "@/lib/analitik";
|
import { olay, siraKovasi } from "@/lib/analitik";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import { onbellekliJsonGetir } from "@/lib/istek-cache";
|
import { onbellekliJsonGetir } from "@/lib/istek-cache";
|
||||||
import { useTercihProfili } from "@/features/liste/hooks/use-tercih-profili";
|
import { useTercihProfili } from "@/features/liste/hooks/use-tercih-profili";
|
||||||
import type { SihirbazFacetleri } from "@/types/yokatlas";
|
import type { SihirbazFacetleri } from "@/types/yokatlas";
|
||||||
@@ -65,16 +66,34 @@ const KANITLAR = [
|
|||||||
* profilsiz karttaki paragrafı değiştirir — katalog sayfaları kartı üstündeki
|
* 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
|
* 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.
|
* 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({
|
export function CtaSiraForm({
|
||||||
baslik,
|
baslik,
|
||||||
aciklama,
|
aciklama,
|
||||||
|
varyant = "kart",
|
||||||
|
konum = "alt",
|
||||||
}: {
|
}: {
|
||||||
baslik?: string;
|
baslik?: string;
|
||||||
aciklama?: 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 profil = useTercihProfili();
|
||||||
const router = useRouter();
|
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 [acik, setAcik] = useState(false);
|
||||||
const [adim, setAdim] = useState<"sira" | "sihirbaz">("sira");
|
const [adim, setAdim] = useState<"sira" | "sihirbaz">("sira");
|
||||||
@@ -87,8 +106,14 @@ export function CtaSiraForm({
|
|||||||
const [girisli, setGirisli] = useState(false);
|
const [girisli, setGirisli] = useState(false);
|
||||||
// Huni girişi ölçümü: ref yalnızca profilsiz daldaki karta bağlanır; kayıtlı
|
// 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
|
// profilli ziyaretçi formu hiç görmediğinden sayılmaz. Sayfa ayrımı için
|
||||||
// özellik gerekmez — Rybbit olayın pathname'ini zaten yazar.
|
// özellik gerekmez — Rybbit olayın pathname'ini zaten yazar. `konum` hem
|
||||||
const formRef = useGorunumOlayi<HTMLElement>("cta_form_goruntulendi");
|
// ö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<HTMLElement>(
|
||||||
|
"cta_form_goruntulendi",
|
||||||
|
{ konum },
|
||||||
|
konum,
|
||||||
|
);
|
||||||
|
|
||||||
// Yazarken binlik ayracıyla biçimle (85000 → 85.000)
|
// Yazarken binlik ayracıyla biçimle (85000 → 85.000)
|
||||||
function siralamaDegisti(ham: string) {
|
function siralamaDegisti(ham: string) {
|
||||||
@@ -118,13 +143,17 @@ export function CtaSiraForm({
|
|||||||
setSira(value);
|
setSira(value);
|
||||||
setFacetler(veri.facetler);
|
setFacetler(veri.facetler);
|
||||||
setAdim("sihirbaz");
|
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", {
|
olay("sira_girildi", {
|
||||||
kaynak: "cta",
|
kaynak: "cta",
|
||||||
|
konum,
|
||||||
tur,
|
tur,
|
||||||
sira_kovasi: siraKovasi(value),
|
sira_kovasi: siraKovasi(value),
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
olay("sira_form_hata", { kaynak: "cta" });
|
olay("sira_form_hata", { kaynak: "cta", konum });
|
||||||
setHata("Bir şeyler ters gitti, tekrar dener misin?");
|
setHata("Bir şeyler ters gitti, tekrar dener misin?");
|
||||||
} finally {
|
} finally {
|
||||||
setYukleniyor(false);
|
setYukleniyor(false);
|
||||||
@@ -146,13 +175,219 @@ export function CtaSiraForm({
|
|||||||
function acikDegisti(v: boolean) {
|
function acikDegisti(v: boolean) {
|
||||||
setAcik(v);
|
setAcik(v);
|
||||||
if (!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.
|
// Yeniden açılışta sıfırdan başla; yazılan sıralama kaybolmasın.
|
||||||
setAdim("sira");
|
setAdim("sira");
|
||||||
setFacetler(null);
|
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 = (
|
||||||
|
<form
|
||||||
|
onSubmit={siraGonder}
|
||||||
|
className={
|
||||||
|
kompakt
|
||||||
|
? "flex flex-col gap-3 lg:flex-row lg:flex-wrap lg:items-center"
|
||||||
|
: "flex flex-col gap-3"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
role="radiogroup"
|
||||||
|
aria-label="Puan türü"
|
||||||
|
className={cn("flex flex-wrap gap-1.5", kompakt && "lg:shrink-0")}
|
||||||
|
>
|
||||||
|
{PUAN_TURLERI_SECENEK.map((t) => (
|
||||||
|
<button
|
||||||
|
key={t.deger}
|
||||||
|
type="button"
|
||||||
|
role="radio"
|
||||||
|
aria-checked={tur === t.deger}
|
||||||
|
onClick={() => setTur(t.deger)}
|
||||||
|
className={
|
||||||
|
tur === t.deger
|
||||||
|
? "cursor-pointer rounded-full border border-primary bg-primary px-3 py-1.5 text-xs font-medium text-white transition-colors duration-200"
|
||||||
|
: "cursor-pointer rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs text-slate-600 transition-colors duration-200 hover:border-slate-300 hover:bg-slate-50"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t.etiket}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label htmlFor={alanId} className="sr-only">
|
||||||
|
YKS başarı sıralaması
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col gap-3 sm:flex-row",
|
||||||
|
kompakt && "lg:min-w-0 lg:flex-1",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="relative sm:flex-1">
|
||||||
|
<Trophy
|
||||||
|
className="pointer-events-none absolute left-4 top-1/2 size-4 -translate-y-1/2 text-slate-400"
|
||||||
|
aria-hidden
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
id={alanId}
|
||||||
|
inputMode="numeric"
|
||||||
|
autoComplete="off"
|
||||||
|
placeholder="YKS başarı sıralaman (ör. 85.000)"
|
||||||
|
value={siralama}
|
||||||
|
onChange={(e) => siralamaDegisti(e.target.value)}
|
||||||
|
onFocus={preloadSihirbazAdimlar}
|
||||||
|
aria-invalid={hata ? true : undefined}
|
||||||
|
aria-describedby={hata ? hataId : undefined}
|
||||||
|
className="h-12 bg-background pl-11 text-base"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
disabled={yukleniyor}
|
||||||
|
className="h-12 cursor-pointer bg-orange-500 px-6 text-white transition-colors duration-200 hover:bg-orange-600"
|
||||||
|
>
|
||||||
|
{yukleniyor ? "Hazırlanıyor…" : "Programları gör"}
|
||||||
|
<ArrowRight className="size-4" aria-hidden />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{hata ? (
|
||||||
|
<p
|
||||||
|
id={hataId}
|
||||||
|
role="alert"
|
||||||
|
className={cn(
|
||||||
|
"text-sm font-medium text-red-600",
|
||||||
|
kompakt && "lg:basis-full",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{hata}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ç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 = (
|
||||||
|
<Drawer open={acik} onOpenChange={acikDegisti} handleOnly>
|
||||||
|
<DrawerContent
|
||||||
|
className="max-h-[92dvh] bg-white sm:mx-auto sm:max-w-2xl sm:border-x sm:border-slate-200 sm:shadow-2xl"
|
||||||
|
>
|
||||||
|
{/* Görünür başlıklar adıma göre değişir; erişilebilir ad sabit kalır. */}
|
||||||
|
<DrawerTitle className="sr-only">
|
||||||
|
Sıralamana yakın programlar hangileri?
|
||||||
|
</DrawerTitle>
|
||||||
|
<DrawerDescription className="sr-only">
|
||||||
|
YKS başarı sıralamanı gir; sıralamana uygun programları bu sayfadan
|
||||||
|
ayrılmadan gör.
|
||||||
|
</DrawerDescription>
|
||||||
|
<DrawerClose className="absolute top-3 right-3 z-10 flex size-9 cursor-pointer items-center justify-center rounded-full border border-slate-200 bg-popover text-slate-500 transition-[background-color,transform] duration-150 hover:bg-slate-100 hover:text-slate-800 active:scale-[0.96]">
|
||||||
|
<X className="size-4" aria-hidden />
|
||||||
|
<span className="sr-only">Kapat</span>
|
||||||
|
</DrawerClose>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="min-h-0 flex-1 overflow-y-auto px-4 pt-5 pb-[max(1.25rem,env(safe-area-inset-bottom))] sm:px-6"
|
||||||
|
>
|
||||||
|
{!kompakt && adim === "sira" ? (
|
||||||
|
<div className="flex flex-col gap-5">
|
||||||
|
<div>
|
||||||
|
<SectionEyebrow>Saniyede gör</SectionEyebrow>
|
||||||
|
<h2 className="mt-3 font-heading text-2xl font-bold text-slate-900">
|
||||||
|
Sıralamana yakın programlar hangileri?
|
||||||
|
</h2>
|
||||||
|
<p className="mt-2 max-w-xl text-sm leading-6 text-slate-600">
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{siraFormu}
|
||||||
|
|
||||||
|
<ul
|
||||||
|
aria-label="Neler alacaksın"
|
||||||
|
className="flex flex-wrap gap-x-4 gap-y-2 border-t border-slate-100 pt-4"
|
||||||
|
>
|
||||||
|
{KANITLAR.map(({ ikon: Ikon, etiket }) => (
|
||||||
|
<li
|
||||||
|
key={etiket}
|
||||||
|
className="inline-flex items-center gap-1.5 text-xs font-medium text-slate-600"
|
||||||
|
>
|
||||||
|
<Ikon className="size-3.5 text-primary" aria-hidden />
|
||||||
|
{etiket}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
) : facetler && sira != null ? (
|
||||||
|
<SihirbazAdimlarLazy
|
||||||
|
key={`${sira}-${tur}`}
|
||||||
|
sira={sira}
|
||||||
|
tur={tur}
|
||||||
|
facetler={facetler}
|
||||||
|
sonButonEtiketi={
|
||||||
|
girisli ? "Yapay Zeka listemi kur (3 kredi)" : "Ücretsiz tablonu gör"
|
||||||
|
}
|
||||||
|
onTamamla={secimleriTamamla}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<section
|
||||||
|
aria-labelledby={baslikId}
|
||||||
|
className="rounded-2xl border border-slate-200 bg-white p-4 sm:p-5"
|
||||||
|
>
|
||||||
|
<p className="text-sm leading-6 text-slate-600">
|
||||||
|
<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
|
||||||
|
ref={profil ? undefined : formRef}
|
||||||
|
inert={profil ? true : undefined}
|
||||||
|
className={cn("col-start-1 row-start-1", profil && "invisible")}
|
||||||
|
>
|
||||||
|
{siraFormu}
|
||||||
|
</div>
|
||||||
|
{profil ? (
|
||||||
|
<div className="col-start-1 row-start-1 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
<p className="text-sm leading-6 text-slate-600">
|
||||||
|
Sıralaman kayıtlı ({profil.sira.toLocaleString("tr-TR")}. sıra).
|
||||||
|
</p>
|
||||||
|
<Link
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
Sıralamana uygun programları gör
|
||||||
|
<ArrowRight className="size-4" aria-hidden />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
{cekmece}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (profil) {
|
if (profil) {
|
||||||
return (
|
return (
|
||||||
<section className="rounded-2xl border border-primary/20 bg-primary/5 p-6 sm:p-8">
|
<section className="rounded-2xl border border-primary/20 bg-primary/5 p-6 sm:p-8">
|
||||||
@@ -198,138 +433,7 @@ export function CtaSiraForm({
|
|||||||
<ArrowRight className="size-4" aria-hidden />
|
<ArrowRight className="size-4" aria-hidden />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Drawer open={acik} onOpenChange={acikDegisti} handleOnly>
|
{cekmece}
|
||||||
<DrawerContent
|
|
||||||
className="max-h-[92dvh] bg-white sm:mx-auto sm:max-w-2xl sm:border-x sm:border-slate-200 sm:shadow-2xl"
|
|
||||||
>
|
|
||||||
{/* Görünür başlıklar adıma göre değişir; erişilebilir ad sabit kalır. */}
|
|
||||||
<DrawerTitle className="sr-only">
|
|
||||||
Sıralamana yakın programlar hangileri?
|
|
||||||
</DrawerTitle>
|
|
||||||
<DrawerDescription className="sr-only">
|
|
||||||
YKS başarı sıralamanı gir; sıralamana uygun programları bu sayfadan
|
|
||||||
ayrılmadan gör.
|
|
||||||
</DrawerDescription>
|
|
||||||
<DrawerClose className="absolute top-3 right-3 z-10 flex size-9 cursor-pointer items-center justify-center rounded-full border border-slate-200 bg-popover text-slate-500 transition-[background-color,transform] duration-150 hover:bg-slate-100 hover:text-slate-800 active:scale-[0.96]">
|
|
||||||
<X className="size-4" aria-hidden />
|
|
||||||
<span className="sr-only">Kapat</span>
|
|
||||||
</DrawerClose>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className="min-h-0 flex-1 overflow-y-auto px-4 pt-5 pb-[max(1.25rem,env(safe-area-inset-bottom))] sm:px-6"
|
|
||||||
>
|
|
||||||
{adim === "sira" ? (
|
|
||||||
<div className="flex flex-col gap-5">
|
|
||||||
<div>
|
|
||||||
<SectionEyebrow>Saniyede gör</SectionEyebrow>
|
|
||||||
<h2 className="mt-3 font-heading text-2xl font-bold text-slate-900">
|
|
||||||
Sıralamana yakın programlar hangileri?
|
|
||||||
</h2>
|
|
||||||
<p className="mt-2 max-w-xl text-sm leading-6 text-slate-600">
|
|
||||||
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.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form onSubmit={siraGonder} className="flex flex-col gap-3">
|
|
||||||
<div
|
|
||||||
role="radiogroup"
|
|
||||||
aria-label="Puan türü"
|
|
||||||
className="flex flex-wrap gap-1.5"
|
|
||||||
>
|
|
||||||
{PUAN_TURLERI_SECENEK.map((t) => (
|
|
||||||
<button
|
|
||||||
key={t.deger}
|
|
||||||
type="button"
|
|
||||||
role="radio"
|
|
||||||
aria-checked={tur === t.deger}
|
|
||||||
onClick={() => setTur(t.deger)}
|
|
||||||
className={
|
|
||||||
tur === t.deger
|
|
||||||
? "cursor-pointer rounded-full border border-primary bg-primary px-3 py-1.5 text-xs font-medium text-white transition-colors duration-200"
|
|
||||||
: "cursor-pointer rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs text-slate-600 transition-colors duration-200 hover:border-slate-300 hover:bg-slate-50"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{t.etiket}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label htmlFor="cta-siralama" className="sr-only">
|
|
||||||
YKS başarı sıralaması
|
|
||||||
</label>
|
|
||||||
<div className="flex flex-col gap-3 sm:flex-row">
|
|
||||||
<div className="relative sm:flex-1">
|
|
||||||
<Trophy
|
|
||||||
className="pointer-events-none absolute left-4 top-1/2 size-4 -translate-y-1/2 text-slate-400"
|
|
||||||
aria-hidden
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
id="cta-siralama"
|
|
||||||
inputMode="numeric"
|
|
||||||
autoComplete="off"
|
|
||||||
placeholder="YKS başarı sıralaman (ör. 85.000)"
|
|
||||||
value={siralama}
|
|
||||||
onChange={(e) => 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"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
disabled={yukleniyor}
|
|
||||||
className="h-12 cursor-pointer bg-orange-500 px-6 text-white transition-colors duration-200 hover:bg-orange-600"
|
|
||||||
>
|
|
||||||
{yukleniyor ? "Hazırlanıyor…" : "Programları gör"}
|
|
||||||
<ArrowRight className="size-4" aria-hidden />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
{hata ? (
|
|
||||||
<p
|
|
||||||
id="cta-siralama-hata"
|
|
||||||
role="alert"
|
|
||||||
className="text-sm font-medium text-red-600"
|
|
||||||
>
|
|
||||||
{hata}
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<ul
|
|
||||||
aria-label="Neler alacaksın"
|
|
||||||
className="flex flex-wrap gap-x-4 gap-y-2 border-t border-slate-100 pt-4"
|
|
||||||
>
|
|
||||||
{KANITLAR.map(({ ikon: Ikon, etiket }) => (
|
|
||||||
<li
|
|
||||||
key={etiket}
|
|
||||||
className="inline-flex items-center gap-1.5 text-xs font-medium text-slate-600"
|
|
||||||
>
|
|
||||||
<Ikon className="size-3.5 text-primary" aria-hidden />
|
|
||||||
{etiket}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
) : facetler && sira != null ? (
|
|
||||||
<SihirbazAdimlarLazy
|
|
||||||
key={`${sira}-${tur}`}
|
|
||||||
sira={sira}
|
|
||||||
tur={tur}
|
|
||||||
facetler={facetler}
|
|
||||||
sonButonEtiketi={
|
|
||||||
girisli ? "Yapay Zeka listemi kur (3 kredi)" : "Ücretsiz tablonu gör"
|
|
||||||
}
|
|
||||||
onTamamla={secimleriTamamla}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</DrawerContent>
|
|
||||||
</Drawer>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user