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 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 }) {
|
||||
)}
|
||||
</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">
|
||||
<div className="mb-4 flex items-end justify-between gap-4">
|
||||
<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ı */}
|
||||
<div className="mt-10">
|
||||
<CtaSiraForm
|
||||
baslik={`Sıralaman ${bolum.ad} tabanlarının neresinde?`}
|
||||
konum="alt"
|
||||
baslik={ctaBaslik}
|
||||
aciklama={KATALOG_CTA_ACIKLAMA}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -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 = (
|
||||
<div className="mt-6">
|
||||
<CtaSiraForm varyant="kompakt" konum="ust" baslik={ctaBaslik} />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -276,6 +287,8 @@ export function UniversiteIcerik({
|
||||
</em>
|
||||
</p>
|
||||
|
||||
{ustSiraSatiri}
|
||||
|
||||
<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} />
|
||||
<dl className="contents">
|
||||
@@ -319,6 +332,7 @@ export function UniversiteIcerik({
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">
|
||||
{uni.ad} taban puanları tablosunun {sayfa}. sayfası (
|
||||
{sayi(tumProgramlar.length)} programın{" "}
|
||||
@@ -335,6 +349,8 @@ export function UniversiteIcerik({
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
{ustSiraSatiri}
|
||||
</>
|
||||
)}
|
||||
|
||||
<PagePixelDivider seed={57} className="mt-8" />
|
||||
@@ -363,7 +379,8 @@ export function UniversiteIcerik({
|
||||
|
||||
<div className="mt-10">
|
||||
<CtaSiraForm
|
||||
baslik={`Sıralaman ${uni.ad} tabanlarının neresinde?`}
|
||||
konum="alt"
|
||||
baslik={ctaBaslik}
|
||||
aciklama={KATALOG_CTA_ACIKLAMA}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -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<HTMLElement>("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<HTMLElement>(
|
||||
"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 = (
|
||||
<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) {
|
||||
return (
|
||||
<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 />
|
||||
</Button>
|
||||
|
||||
<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"
|
||||
>
|
||||
{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>
|
||||
{cekmece}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user