feat(katalog+tasarim): şehir adları her yerde link; piksel yüzeylere beyaz ton
- ilSayfaHref: üniversite sayfası il hapı, /universiteler listesi, bölüm / sonuç / listem / tadımlık satırlarındaki il metası şehir sayfasına gider. - Şehir kümeleri tüm (üniversite, il) çiftlerinden: DB'deki her il değerinin sayfası var (ODTÜ Ankara + KKTC); dev'de eski globalThis önbelleğine dayanıklı. - PikselZemin ton="acik", CtaBandi/CtaSiraForm ton="beyaz": /universiteler altı ve /meraklisina kapanışı beyaz, diğer bantlar mavi (dönüşümlü). - ilUlkeHaritasi: il değerinden ülke haritası + şehir konumu. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,8 @@ import { startTransition, useMemo, useRef, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { ChevronDown, ChevronUp } from "lucide-react";
|
||||
import type { Program } from "@/types/yokatlas";
|
||||
import { trBaslikDuzeni } from "@/lib/slug";
|
||||
import { ilSayfaHref, trBaslikDuzeni } from "@/lib/slug";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { UNITUR_ETIKET } from "@/lib/unitur";
|
||||
import { VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||
import { Sayfalama, SAYFA_BOYU } from "./sayfalama";
|
||||
@@ -23,6 +24,7 @@ import {
|
||||
SATIR_BUTON_KABI,
|
||||
SATIR_CIP,
|
||||
SATIR_GRUP,
|
||||
SATIR_IKINCIL_LINK,
|
||||
SATIR_META,
|
||||
SATIR_MOBIL_SAYI,
|
||||
} from "@/components/liste-satiri";
|
||||
@@ -247,9 +249,13 @@ export function BolumProgramListesi({
|
||||
</span>
|
||||
) : null}
|
||||
{p.il ? (
|
||||
<span className="shrink-0">
|
||||
<Link
|
||||
href={ilSayfaHref(p.il)}
|
||||
className={cn("shrink-0", SATIR_IKINCIL_LINK)}
|
||||
title={`${trBaslikDuzeni(p.il)} üniversiteleri`}
|
||||
>
|
||||
{trBaslikDuzeni(p.il)}
|
||||
</span>
|
||||
</Link>
|
||||
) : null}
|
||||
{p.sure ? (
|
||||
<span className="shrink-0">{p.sure} yıl</span>
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
getUniversiteBySlug,
|
||||
bolumSlugFromIsim,
|
||||
} from "../katalog-queries";
|
||||
import { trBaslikDuzeni } from "@/lib/slug";
|
||||
import { ilSayfaHref, trBaslikDuzeni } from "@/lib/slug";
|
||||
import { UNITUR_ETIKET, uniturDevletMi } from "@/lib/unitur";
|
||||
import {
|
||||
JsonLd,
|
||||
@@ -579,10 +579,16 @@ export function UniversiteIcerik({
|
||||
</span>
|
||||
) : null}
|
||||
{uni.il ? (
|
||||
<span className="inline-flex h-10 min-w-0 items-center gap-2 rounded-full border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-800">
|
||||
// İl hapı şehir sayfasına gider (Bilal, 24 Eyl 2026: "şehirlerin
|
||||
// geçtiği her yer tıklanabilir"); hover §2.1 tek turuncu.
|
||||
<Link
|
||||
href={ilSayfaHref(uni.il)}
|
||||
title={`${trBaslikDuzeni(uni.il)} üniversiteleri`}
|
||||
className="inline-flex h-10 min-w-0 items-center gap-2 rounded-full border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-800 transition-[background-color,border-color,color,transform] duration-150 hover:border-orange-500 hover:bg-orange-50/60 hover:text-orange-500 active:scale-[0.97]"
|
||||
>
|
||||
<MapPin className="size-4 shrink-0 text-primary" aria-hidden />
|
||||
<span className="truncate">{trBaslikDuzeni(uni.il)}</span>
|
||||
</span>
|
||||
</Link>
|
||||
) : null}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
getAllUniversiteler,
|
||||
type UniOzet,
|
||||
} from "../katalog-queries";
|
||||
import { trBaslikDuzeni } from "@/lib/slug";
|
||||
import { ilSayfaHref, trBaslikDuzeni } from "@/lib/slug";
|
||||
import { SehirCipleri, oneCikanIller } from "./sehir-cipleri";
|
||||
import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form";
|
||||
import { PagePixelDivider } from "@/components/pixel-decor";
|
||||
@@ -15,25 +15,41 @@ import { SON_YIL, TERCIH_YILI } from "@/lib/veri-yillari";
|
||||
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
||||
|
||||
export function UniLinkleri({ uniler }: { uniler: UniOzet[] }) {
|
||||
// Ad ve il ayrı linkler (iç içe <a> olmaz): logo + ad üniversiteye, alt
|
||||
// satırdaki şehir adı şehir sayfasına (Bilal, 24 Eyl 2026: "şehirlerin
|
||||
// geçtiği her yer tıklanabilir"). Hover §2.1 tek turuncu.
|
||||
return (
|
||||
<ul className="mt-4 grid gap-x-6 gap-y-2 sm:grid-cols-2">
|
||||
{uniler.map((u) => (
|
||||
<li key={u.slug}>
|
||||
<li key={u.slug} className="grid grid-cols-[auto_minmax(0,1fr)] items-center gap-x-3 text-sm">
|
||||
<Link
|
||||
href={`/universite/${u.slug}`}
|
||||
className="group flex items-center gap-3 text-sm text-slate-700 transition-colors hover:text-orange-500"
|
||||
tabIndex={-1}
|
||||
aria-hidden
|
||||
className="row-span-2"
|
||||
>
|
||||
<UniLogo slug={u.slug} ad={u.ad} boy="sm" />
|
||||
<span className="min-w-0">
|
||||
<span className="block truncate group-hover:underline">
|
||||
{u.ad}
|
||||
</span>
|
||||
<span className="block text-xs text-slate-400">
|
||||
{u.il ? `${trBaslikDuzeni(u.il)} · ` : ""}
|
||||
{sayi(u.programSayisi)} program
|
||||
</span>
|
||||
</span>
|
||||
</Link>
|
||||
<Link
|
||||
href={`/universite/${u.slug}`}
|
||||
className="block truncate text-slate-700 transition-colors hover:text-orange-500 hover:underline"
|
||||
>
|
||||
{u.ad}
|
||||
</Link>
|
||||
<span className="block text-xs text-slate-400">
|
||||
{u.il ? (
|
||||
<>
|
||||
<Link
|
||||
href={ilSayfaHref(u.il)}
|
||||
className="transition-colors hover:text-orange-500 hover:underline"
|
||||
>
|
||||
{trBaslikDuzeni(u.il)}
|
||||
</Link>
|
||||
{" · "}
|
||||
</>
|
||||
) : null}
|
||||
{sayi(u.programSayisi)} program
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
@@ -146,7 +162,7 @@ export function UniversiteKatalogu() {
|
||||
</section>
|
||||
|
||||
<div className="mt-12">
|
||||
<CtaSiraForm />
|
||||
<CtaSiraForm ton="beyaz" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -21,7 +21,8 @@ import type {
|
||||
RankResultsListe,
|
||||
} from "@/types/yokatlas";
|
||||
import { DILIM_ETIKET, riskHesapla } from "@/lib/risk";
|
||||
import { bolumSayfaSlug, trBaslikDuzeni, uniSayfaSlug } from "@/lib/slug";
|
||||
import { bolumSayfaSlug, ilSayfaHref, trBaslikDuzeni, uniSayfaSlug } from "@/lib/slug";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||
import {
|
||||
ProgramSiraGecmisi,
|
||||
@@ -374,7 +375,14 @@ function ProgramSatiri({
|
||||
<span className={SATIR_META}>
|
||||
<span className={SATIR_CIP}>{devlet ? "Devlet" : "Vakıf"}</span>
|
||||
{p.il ? (
|
||||
<span className="shrink-0">{trBaslikDuzeni(p.il)}</span>
|
||||
<Link
|
||||
href={ilSayfaHref(p.il)}
|
||||
className={cn("shrink-0", SATIR_IKINCIL_LINK)}
|
||||
title={`${trBaslikDuzeni(p.il)} üniversiteleri`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{trBaslikDuzeni(p.il)}
|
||||
</Link>
|
||||
) : null}
|
||||
{p.sure ? (
|
||||
<span className="shrink-0">{p.sure} yıl</span>
|
||||
|
||||
@@ -239,7 +239,7 @@ export function DemoKapanisCta() {
|
||||
// §10.8; Bilal, 24 Eyl 2026: "burda piksel dokuları yok"). Tek başlık, tek
|
||||
// turuncu buton; sayfadaki diğer bantlardan farklı seed.
|
||||
return (
|
||||
<CtaBandi etiket="div" seed={29}>
|
||||
<CtaBandi etiket="div" seed={29} ton="beyaz">
|
||||
<p className="font-heading text-2xl font-bold text-balance sm:text-3xl">
|
||||
{profil
|
||||
? "Kuralları öğrendin — sayılar zaten senin sıralamanla hesaplandı."
|
||||
|
||||
@@ -26,7 +26,8 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { bolumSayfaSlug, trBaslikDuzeni, uniSayfaSlug } from "@/lib/slug";
|
||||
import { bolumSayfaSlug, ilSayfaHref, trBaslikDuzeni, uniSayfaSlug } from "@/lib/slug";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { UNITUR_ETIKET } from "@/lib/unitur";
|
||||
import { RiskEtiketi } from "@/components/risk-etiketi";
|
||||
import {
|
||||
@@ -266,7 +267,14 @@ export function RaporListesi({
|
||||
<span className={SATIR_CIP}>{devlet ? "Devlet" : "Vakıf"}</span>
|
||||
) : null}
|
||||
{p?.il ? (
|
||||
<span className="shrink-0">{trBaslikDuzeni(p.il)}</span>
|
||||
<Link
|
||||
href={ilSayfaHref(p.il)}
|
||||
className={cn("shrink-0", SATIR_IKINCIL_LINK)}
|
||||
title={`${trBaslikDuzeni(p.il)} üniversiteleri`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{trBaslikDuzeni(p.il)}
|
||||
</Link>
|
||||
) : null}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -24,12 +24,14 @@ import {
|
||||
import { useGorunumOlayi } from "@/components/use-gorunum-olayi";
|
||||
import { olay, siraKovasi } from "@/lib/analitik";
|
||||
import { dilimdenRisk, riskHesapla } from "@/lib/risk";
|
||||
import { trBaslikDuzeni, turkishSlugify } from "@/lib/slug";
|
||||
import { ilSayfaHref, trBaslikDuzeni, turkishSlugify } from "@/lib/slug";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
SATIR_ALT_AD,
|
||||
SATIR_AD_DUZ,
|
||||
SATIR_CIP,
|
||||
SATIR_ALT_GRUP,
|
||||
SATIR_IKINCIL_LINK,
|
||||
SATIR_META,
|
||||
} from "@/components/liste-satiri";
|
||||
import { listemUretHref, PROFIL_DEGISTI_EVENT } from "@/features/sihirbaz/sihirbaz-sabitler";
|
||||
@@ -166,7 +168,13 @@ export function TadimlikSatiri({
|
||||
<span className={SATIR_CIP}>{devlet ? "Devlet" : "Vakıf"}</span>
|
||||
) : null}
|
||||
{p.il ? (
|
||||
<span className="shrink-0">{trBaslikDuzeni(p.il)}</span>
|
||||
<Link
|
||||
href={ilSayfaHref(p.il)}
|
||||
className={cn("shrink-0", SATIR_IKINCIL_LINK)}
|
||||
title={`${trBaslikDuzeni(p.il)} üniversiteleri`}
|
||||
>
|
||||
{trBaslikDuzeni(p.il)}
|
||||
</Link>
|
||||
) : null}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -101,6 +101,9 @@ function KayitliSira({
|
||||
* 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.
|
||||
*
|
||||
* `ton="beyaz"`: kart varyantının beyaz zeminli bandı (aynı sayfada iki mavi
|
||||
* bant üst üste gelmesin diye; DESIGN.md §10.8).
|
||||
*
|
||||
* `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
|
||||
@@ -113,10 +116,13 @@ export function CtaSiraForm({
|
||||
aciklama,
|
||||
varyant = "kart",
|
||||
konum = "alt",
|
||||
ton = "mavi",
|
||||
}: {
|
||||
baslik?: string;
|
||||
aciklama?: string;
|
||||
varyant?: "kart" | "kompakt";
|
||||
/** Yalnız kart varyantı: bandın zemini */
|
||||
ton?: "mavi" | "beyaz";
|
||||
/** Yalnız ölçüm içindir: formun sayfadaki yeri (tablonun üstü / altı). */
|
||||
konum?: "ust" | "alt";
|
||||
}) {
|
||||
@@ -427,16 +433,18 @@ export function CtaSiraForm({
|
||||
}
|
||||
|
||||
// Kart varyantı sayfa sonundaki CTA bandıdır (DESIGN.md §10.8, CtaBandi).
|
||||
const beyaz = ton === "beyaz";
|
||||
const paragrafSinifi = beyaz ? "text-slate-600" : "text-white/90";
|
||||
if (profil) {
|
||||
return (
|
||||
<CtaBandi seed={5}>
|
||||
<CtaBandi seed={5} ton={ton}>
|
||||
<h2 className="font-heading text-2xl font-bold text-balance sm:text-3xl">
|
||||
{baslik ?? "Sıralamana yakın programlar hangileri? Saniyede gör."}
|
||||
</h2>
|
||||
<div className="mt-3">
|
||||
<KayitliSira sira={profil.sira} tur={profil.tur} beyaz />
|
||||
<KayitliSira sira={profil.sira} tur={profil.tur} beyaz={!beyaz} />
|
||||
</div>
|
||||
<p className="mx-auto mt-2 max-w-xl text-base leading-relaxed text-white/90">
|
||||
<p className={cn("mx-auto mt-2 max-w-xl text-base leading-relaxed", paragrafSinifi)}>
|
||||
Gerçek YÖK Atlas verisiyle hayal, dengeli ve güvenli dilimlerindeki
|
||||
programların hazır.
|
||||
</p>
|
||||
@@ -456,11 +464,11 @@ export function CtaSiraForm({
|
||||
|
||||
return (
|
||||
<div ref={formRef}>
|
||||
<CtaBandi seed={9}>
|
||||
<CtaBandi seed={9} ton={ton}>
|
||||
<h2 className="font-heading text-2xl font-bold text-balance sm:text-3xl">
|
||||
{baslik ?? "Sıralamana yakın programlar hangileri? Saniyede gör."}
|
||||
</h2>
|
||||
<p className="mx-auto mt-3 max-w-xl text-base leading-relaxed text-white/90">
|
||||
<p className={cn("mx-auto mt-3 max-w-xl text-base leading-relaxed", paragrafSinifi)}>
|
||||
{aciklama ??
|
||||
"YKS başarı sıralamanı gir; gerçek YÖK Atlas verisiyle sıralamana uygun hayal, dengeli ve güvenli dilimlerindeki programları anında listele. Ücretsiz, üyelik gerekmez."}
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user