All checks were successful
Deploy / deploy (push) Successful in 5m1s
- /universiteler/devlet ve /universiteler/vakif: şehir sayfası iskeleti, haritada bu türden üniversitesi olan her il turuncu (yakınlaşma yok), üç özet kartı, şehir çipleri (sayfa içi çapa), şehir şehir §4.15 satırları, öteki türe geçiş hapı; metadata + sitemap. - TurCipi tek kaynak: devlet/vakıf çipi tür sayfasına link (sonuç, listem, tadımlık, bölüm, şehir/tür satırları); üniversite sayfası tür hapı ve /universiteler başlıkları da link. UniSatirlari ortak dosyada: ad, tür ve il ayrı linkler. - Kompakt profilli satır (DESIGN.md §10.9): iri kupa kutusu + text-3xl/4xl sıra, text-xl/2xl başlık, 48 px buton; telefonda alt alta, lg'de tek satır. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
280 lines
10 KiB
TypeScript
280 lines
10 KiB
TypeScript
import type { Metadata } from "next";
|
||
import Link from "next/link";
|
||
import {
|
||
Building2,
|
||
ChevronRight,
|
||
Landmark,
|
||
LayoutList,
|
||
MapPin,
|
||
} from "lucide-react";
|
||
import { getAllUniversiteler, type UniOzet } from "../katalog-queries";
|
||
import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form";
|
||
import { PagePixelDivider } from "@/components/pixel-decor";
|
||
import { OzetKart } from "@/components/ozet-kart";
|
||
import { Skeleton } from "@/components/ui/skeleton";
|
||
import {
|
||
JsonLd,
|
||
breadcrumbJsonLd,
|
||
sayfaMetadata,
|
||
ustSegmentOgGorseli,
|
||
} from "@/lib/seo";
|
||
import { ilSayfaHref, trBaslikDuzeni, turkishSlugify } from "@/lib/slug";
|
||
import { TUR_SAYFALARI, type TurSlug } from "@/lib/unitur";
|
||
import { BASLIK_YILI, SON_YIL, TERCIH_YILI } from "@/lib/veri-yillari";
|
||
import { NavBaslik } from "./nav-baslik";
|
||
import { TurkiyePikselHaritasi } from "./sehir-piksel-haritasi";
|
||
import { SehirYuzeyi } from "./sehir-yuzeyi";
|
||
import { UniSatirlari } from "./uni-satirlari";
|
||
|
||
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
||
|
||
function turUnileri(tur: TurSlug): UniOzet[] {
|
||
const set = new Set<string>(TUR_SAYFALARI[tur].uniturler);
|
||
return getAllUniversiteler().filter((u) => u.unitur && set.has(u.unitur));
|
||
}
|
||
|
||
export function turSayfaMetadata(tur: TurSlug): Metadata {
|
||
const t = TUR_SAYFALARI[tur];
|
||
const uniler = turUnileri(tur);
|
||
const program = uniler.reduce((a, u) => a + u.programSayisi, 0);
|
||
return sayfaMetadata({
|
||
title: `${t.ad} Taban Puanları ve Sıralamaları ${BASLIK_YILI}`,
|
||
description: `Türkiye'deki ${uniler.length} ${t.tekil}nin ${SON_YIL} YKS yerleştirme verileri: ${sayi(program)} programın taban puanları, başarı sıralamaları ve kontenjanları, şehir şehir. ${TERCIH_YILI} tercih rehberi.`,
|
||
path: `/universiteler/${t.slug}`,
|
||
openGraph: {
|
||
images: ustSegmentOgGorseli(
|
||
"/universiteler",
|
||
"Üniversitelerin taban puanları ve sıralamaları — KolayTercih",
|
||
),
|
||
},
|
||
});
|
||
}
|
||
|
||
/**
|
||
* /universiteler/devlet ve /universiteler/vakif (DESIGN.md §4.18). Şehir
|
||
* sayfasıyla aynı iskelet; kreatif fark: başlık yüzeyindeki dot-matrix
|
||
* Türkiye'de bu türden üniversitesi olan **her il** turuncu yanar, yakınlaşma
|
||
* yok — "devlet üniversiteleri Türkiye'nin neresinde" tek bakışta. Altında
|
||
* üç özet kartı, şehir çipleri (bu türün il başına sayısı) ve şehir şehir
|
||
* §4.15 liste satırları.
|
||
*/
|
||
export function TurIcerik({ tur }: { tur: TurSlug }) {
|
||
const t = TUR_SAYFALARI[tur];
|
||
const uniler = turUnileri(tur);
|
||
const program = uniler.reduce((a, u) => a + u.programSayisi, 0);
|
||
// Şehir öbekleri: üniversitenin ana ili (UniOzet.il); ili boş olan sona.
|
||
const ilOzet = new Map<string, { il: string; ad: string; uniler: UniOzet[] }>();
|
||
const ilsiz: UniOzet[] = [];
|
||
for (const u of uniler) {
|
||
if (!u.il) {
|
||
ilsiz.push(u);
|
||
continue;
|
||
}
|
||
let k = ilOzet.get(u.il);
|
||
if (!k) {
|
||
k = { il: u.il, ad: trBaslikDuzeni(u.il), uniler: [] };
|
||
ilOzet.set(u.il, k);
|
||
}
|
||
k.uniler.push(u);
|
||
}
|
||
const sehirler = [...ilOzet.values()].sort((a, b) =>
|
||
a.ad.localeCompare(b.ad, "tr-TR"),
|
||
);
|
||
const cipSehirler = [...sehirler].sort(
|
||
(a, b) =>
|
||
b.uniler.length - a.uniler.length || a.ad.localeCompare(b.ad, "tr-TR"),
|
||
);
|
||
const Ikon = tur === "devlet" ? Landmark : Building2;
|
||
const DigerIkon = tur === "devlet" ? Building2 : Landmark;
|
||
const ctaBaslik = `Sıralaman ${t.tekil} tabanlarının neresinde?`;
|
||
const digerTur = tur === "devlet" ? TUR_SAYFALARI.vakif : TUR_SAYFALARI.devlet;
|
||
|
||
return (
|
||
<main className="mx-auto w-full max-w-5xl px-4 py-12 sm:py-16">
|
||
<JsonLd
|
||
data={breadcrumbJsonLd([
|
||
{ name: "Ana Sayfa", path: "/" },
|
||
{ name: "Üniversiteler", path: "/universiteler" },
|
||
{ name: t.ad, path: `/universiteler/${t.slug}` },
|
||
])}
|
||
/>
|
||
|
||
<nav
|
||
aria-label="İçerik yolu"
|
||
className="flex flex-wrap items-center justify-center gap-1.5 text-sm text-slate-500 lg:justify-start"
|
||
>
|
||
<Link
|
||
href="/"
|
||
className="transition-colors hover:text-orange-500 hover:underline"
|
||
>
|
||
Ana Sayfa
|
||
</Link>
|
||
<ChevronRight className="size-3.5 shrink-0" aria-hidden />
|
||
<Link
|
||
href="/universiteler"
|
||
className="transition-colors hover:text-orange-500 hover:underline"
|
||
>
|
||
Üniversiteler
|
||
</Link>
|
||
<ChevronRight className="size-3.5 shrink-0" aria-hidden />
|
||
<span className="text-slate-700">{t.ad}</span>
|
||
</nav>
|
||
|
||
<NavBaslik baslikId="tur-baslik" ad={t.ad} />
|
||
<SehirYuzeyi
|
||
harita={
|
||
<TurkiyePikselHaritasi
|
||
iller={sehirler.map((s) => s.il)}
|
||
etiket={`${t.ad} Türkiye haritasında: ${sehirler.length} il`}
|
||
doldur
|
||
/>
|
||
}
|
||
metin={
|
||
<div className="text-center lg:text-left">
|
||
<h1
|
||
id="tur-baslik"
|
||
className="font-heading text-3xl font-bold leading-tight text-slate-900 sm:text-5xl"
|
||
>
|
||
{t.ad} Taban Puanları {BASLIK_YILI}
|
||
</h1>
|
||
{/* Kimlik hapları (§4.12): tür + öteki türe geçiş */}
|
||
<p className="mt-5 flex flex-wrap items-center justify-center gap-2 lg:justify-start">
|
||
<span className="inline-flex h-10 items-center gap-2 rounded-full border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-800">
|
||
<Ikon className="size-4 text-primary" aria-hidden />
|
||
{tur === "devlet" ? "Devlet" : "Vakıf"}
|
||
</span>
|
||
<Link
|
||
href={`/universiteler/${digerTur.slug}`}
|
||
className="inline-flex h-10 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]"
|
||
>
|
||
<DigerIkon className="size-4 text-primary" aria-hidden />
|
||
{digerTur.ad}
|
||
</Link>
|
||
</p>
|
||
<p className="mx-auto mt-6 max-w-2xl text-base leading-relaxed text-slate-600 sm:text-lg lg:mx-0">
|
||
Türkiye'de {SON_YIL} YKS yerleştirmesinde öğrenci alan{" "}
|
||
<strong>
|
||
{sayi(uniler.length)} {t.tekil}
|
||
</strong>
|
||
, <strong>{sayi(sehirler.length)} şehirde</strong> toplam{" "}
|
||
<strong>{sayi(program)} program</strong> açtı. Haritada turuncu
|
||
yanan her ilde en az bir {t.tekil} var. Üniversiteye tıklayınca
|
||
programlarının taban puanlarını, başarı sıralamalarını ve
|
||
kontenjanlarını fakülte fakülte görürsün; {TERCIH_YILI} tercihleri
|
||
için rehber niteliğindedir.
|
||
</p>
|
||
</div>
|
||
}
|
||
/>
|
||
|
||
<div className="mt-8">
|
||
<CtaSiraForm varyant="kompakt" konum="ust" baslik={ctaBaslik} />
|
||
</div>
|
||
|
||
<dl className="mt-8 grid grid-cols-2 gap-4 lg:grid-cols-3">
|
||
<OzetKart
|
||
ikon={Ikon}
|
||
etiket="Üniversite"
|
||
deger={sayi(uniler.length)}
|
||
alt={`${SON_YIL}'de öğrenci alan`}
|
||
/>
|
||
<OzetKart
|
||
ikon={LayoutList}
|
||
etiket="Program"
|
||
deger={sayi(program)}
|
||
alt="lisans ve önlisans"
|
||
/>
|
||
<OzetKart
|
||
ikon={MapPin}
|
||
etiket="Şehir"
|
||
deger={sayi(sehirler.length)}
|
||
alt="en az bir kampüs"
|
||
className="col-span-2 lg:col-span-1"
|
||
/>
|
||
</dl>
|
||
|
||
<PagePixelDivider seed={73} className="mx-auto mt-8 lg:mx-0" />
|
||
|
||
<section
|
||
className="mt-8 text-center lg:text-left"
|
||
aria-labelledby="tur-sehirler"
|
||
>
|
||
<h2
|
||
id="tur-sehirler"
|
||
className="font-heading text-2xl font-bold text-slate-900"
|
||
>
|
||
Şehre göre
|
||
<span className="ml-2 text-sm font-normal text-slate-400">
|
||
{sayi(sehirler.length)} şehir
|
||
</span>
|
||
</h2>
|
||
<ul className="mt-4 flex flex-wrap items-center justify-center gap-2 lg:justify-start">
|
||
{cipSehirler.map((s) => (
|
||
<li key={s.il}>
|
||
<a
|
||
href={`#sehir-${turkishSlugify(s.il)}`}
|
||
className="inline-flex h-10 cursor-pointer items-center gap-1.5 rounded-full border border-slate-200 bg-white px-3.5 text-sm font-semibold text-slate-700 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]"
|
||
>
|
||
{s.ad}
|
||
<span className="font-normal tabular-nums text-slate-400">
|
||
{sayi(s.uniler.length)}
|
||
</span>
|
||
</a>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</section>
|
||
|
||
{sehirler.map((s) => (
|
||
<section
|
||
key={s.il}
|
||
id={`sehir-${turkishSlugify(s.il)}`}
|
||
className="mt-10 scroll-mt-24 text-center lg:text-left"
|
||
>
|
||
<h2 className="font-heading text-2xl font-bold text-slate-900">
|
||
<Link
|
||
href={ilSayfaHref(s.il)}
|
||
className="transition-colors hover:text-orange-500 hover:underline"
|
||
>
|
||
{s.ad}
|
||
</Link>
|
||
<span className="ml-2 text-sm font-normal text-slate-400">
|
||
{sayi(s.uniler.length)}
|
||
</span>
|
||
</h2>
|
||
<UniSatirlari uniler={s.uniler} gizliIl={s.il} />
|
||
</section>
|
||
))}
|
||
{ilsiz.length > 0 ? (
|
||
<section className="mt-10 text-center lg:text-left">
|
||
<h2 className="font-heading text-2xl font-bold text-slate-900">
|
||
Diğer
|
||
</h2>
|
||
<UniSatirlari uniler={ilsiz} />
|
||
</section>
|
||
) : null}
|
||
|
||
<div className="mt-12">
|
||
<CtaSiraForm
|
||
baslik={ctaBaslik}
|
||
aciklama={`YKS başarı sıralamanı gir; ${t.tekil} programları dahil sıralamana uygun hayal, dengeli ve güvenli dilimlerindeki programları anında listele. Ücretsiz, üyelik gerekmez.`}
|
||
/>
|
||
</div>
|
||
</main>
|
||
);
|
||
}
|
||
|
||
export function TurIcerikSkeleton() {
|
||
return (
|
||
<main className="mx-auto w-full max-w-5xl px-4 py-12 sm:py-16">
|
||
<Skeleton className="mx-auto h-4 w-64 lg:mx-0" />
|
||
<Skeleton className="mt-6 h-72 w-full rounded-3xl" />
|
||
<div className="mt-8 grid grid-cols-2 gap-4 lg:grid-cols-3">
|
||
<Skeleton className="h-28 rounded-2xl" />
|
||
<Skeleton className="h-28 rounded-2xl" />
|
||
<Skeleton className="col-span-2 h-28 rounded-2xl lg:col-span-1" />
|
||
</div>
|
||
</main>
|
||
);
|
||
}
|