All checks were successful
Deploy / deploy (push) Successful in 6m23s
- TemaSaglayici (next-themes) + alt bilgide TemaSecici - globals.css: .dark bloğunda palet token'ları çevrilir; bileşenlere dark: sınıfı yazılmaz - yüzeyler bg-white → bg-card, koyu haplar text-slate-50, SVG'lerde sabit renk yerine değişken - DESIGN.md §2.3 + tasarim-denetim kuralları (yuzey-beyaz, koyu-hap-beyaz-yazi, dark-sinif), AGENTS.md karanlık mod bölümü - docs: koyu tema denetimi raporu Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
229 lines
8.3 KiB
TypeScript
229 lines
8.3 KiB
TypeScript
import type { Metadata } from "next";
|
||
import Link from "next/link";
|
||
import { notFound } from "next/navigation";
|
||
import { Building2, ChevronRight, Landmark, MapPin } from "lucide-react";
|
||
import {
|
||
getAllIller,
|
||
getAllIlSlugs,
|
||
getIlBySlug,
|
||
type UniOzet,
|
||
} from "../katalog-queries";
|
||
import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form";
|
||
import { PagePixelDivider } from "@/components/pixel-decor";
|
||
import { Skeleton } from "@/components/ui/skeleton";
|
||
import {
|
||
JsonLd,
|
||
breadcrumbJsonLd,
|
||
sayfaMetadata,
|
||
ustSegmentOgGorseli,
|
||
} from "@/lib/seo";
|
||
import { BASLIK_YILI, SON_YIL, TERCIH_YILI } from "@/lib/veri-yillari";
|
||
import { NavBaslik } from "./nav-baslik";
|
||
import { SehirCipleri, oneCikanIller } from "./sehir-cipleri";
|
||
import { SehirPikselHaritasi } from "./sehir-piksel-haritasi";
|
||
import { SehirYuzeyi } from "./sehir-yuzeyi";
|
||
import { UniSatirlari } from "./uni-satirlari";
|
||
|
||
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
||
|
||
export function ilStaticSlugs(): string[] {
|
||
return getAllIlSlugs();
|
||
}
|
||
|
||
export function ilSayfaMetadata(slug: string): Metadata {
|
||
const il = getIlBySlug(slug);
|
||
if (!il) return {};
|
||
return sayfaMetadata({
|
||
title: `${il.ad} Üniversiteleri Taban Puanları ve Sıralamaları ${BASLIK_YILI}`,
|
||
description: `${il.ad}'daki ${il.uniler.length} üniversitenin ${SON_YIL} YKS yerleştirme verileri: ${sayi(il.programSayisi)} programın taban puanları, başarı sıralamaları ve kontenjanları. ${TERCIH_YILI} tercih rehberi.`,
|
||
path: `/universiteler/${slug}`,
|
||
openGraph: {
|
||
images: ustSegmentOgGorseli(
|
||
"/universiteler",
|
||
"Üniversitelerin taban puanları ve sıralamaları — KolayTercih",
|
||
),
|
||
},
|
||
});
|
||
}
|
||
|
||
function UniGrubu({
|
||
baslik,
|
||
uniler,
|
||
buIl,
|
||
}: {
|
||
baslik: string;
|
||
uniler: UniOzet[];
|
||
buIl: string;
|
||
}) {
|
||
if (uniler.length === 0) return null;
|
||
return (
|
||
<section className="mt-10 text-center lg:text-left">
|
||
<h2 className="font-heading text-2xl font-bold text-slate-900">
|
||
{baslik}
|
||
<span className="ml-2 text-sm font-normal text-slate-400">
|
||
{sayi(uniler.length)}
|
||
</span>
|
||
</h2>
|
||
<UniSatirlari uniler={uniler} gizliIl={buIl} />
|
||
</section>
|
||
);
|
||
}
|
||
|
||
/**
|
||
* /universiteler/[il]: bir şehirdeki üniversiteler, devlet / vakıf / diğer
|
||
* öbekleriyle. Düzen katalog detay sayfalarıyla aynı (DESIGN.md §5.2):
|
||
* yol satırı, başlık, kimlik hapları ve özet lg altında ortalı; listeler
|
||
* her zaman sola yaslı.
|
||
*/
|
||
export function IlIcerik({ slug }: { slug: string }) {
|
||
const il = getIlBySlug(slug);
|
||
if (!il) notFound();
|
||
|
||
const devlet: UniOzet[] = [];
|
||
const vakif: UniOzet[] = [];
|
||
const diger: UniOzet[] = [];
|
||
for (const u of il.uniler) {
|
||
if (u.unitur === "DEVLET") devlet.push(u);
|
||
else if (u.unitur === "VAKIF" || u.unitur === "VAKIF MYO") vakif.push(u);
|
||
else diger.push(u);
|
||
}
|
||
const ozetParcalar = [
|
||
devlet.length > 0 ? `${sayi(devlet.length)} devlet` : null,
|
||
vakif.length > 0 ? `${sayi(vakif.length)} vakıf` : null,
|
||
diger.length > 0 ? `${sayi(diger.length)} KKTC/yurt dışı` : null,
|
||
].filter(Boolean);
|
||
const ctaBaslik = `Sıralaman ${il.ad} üniversitelerinin neresinde?`;
|
||
const digerSehirler = oneCikanIller(getAllIller());
|
||
|
||
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: il.ad, path: `/universiteler/${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">{il.ad}</span>
|
||
</nav>
|
||
|
||
<NavBaslik baslikId="il-baslik" ad={`${il.ad} üniversiteleri`} />
|
||
{/* Başlık yüzeyi: beyaz kart, arka planı kaplayan dot-matrix şehir
|
||
haritası (DESIGN.md §4.17). lg altında metin ortalı, harita altta. */}
|
||
<SehirYuzeyi
|
||
harita={
|
||
<SehirPikselHaritasi
|
||
il={il.il}
|
||
ad={il.ad}
|
||
doldur
|
||
odakHiza={{ lg: { x: 0.74, y: 0.5 }, dar: { x: 0.5, y: 0.78 } }}
|
||
/>
|
||
}
|
||
metin={
|
||
<div className="text-center lg:text-left">
|
||
<h1
|
||
id="il-baslik"
|
||
className="font-heading text-3xl font-bold leading-tight text-slate-900 sm:text-5xl"
|
||
>
|
||
{il.ad} Üniversiteleri Taban Puanları {BASLIK_YILI}
|
||
</h1>
|
||
|
||
{/* Kimlik hapları (DESIGN.md §4.12): il + üniversite sayısı */}
|
||
<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-card px-4 text-sm font-semibold text-slate-800">
|
||
<MapPin className="size-4 text-primary" aria-hidden />
|
||
{il.ad}
|
||
</span>
|
||
<span className="inline-flex h-10 items-center gap-2 rounded-full border border-slate-200 bg-card px-4 text-sm font-semibold text-slate-800">
|
||
{devlet.length >= vakif.length ? (
|
||
<Landmark className="size-4 text-primary" aria-hidden />
|
||
) : (
|
||
<Building2 className="size-4 text-primary" aria-hidden />
|
||
)}
|
||
{sayi(il.uniler.length)} üniversite
|
||
</span>
|
||
</p>
|
||
|
||
<p className="mx-auto mt-6 max-w-2xl text-base leading-relaxed text-slate-600 sm:text-lg lg:mx-0">
|
||
{il.ad}'da {SON_YIL} YKS yerleştirmesinde öğrenci alan{" "}
|
||
<strong>{sayi(il.uniler.length)} üniversite</strong>
|
||
{ozetParcalar.length > 1 ? ` (${ozetParcalar.join(", ")})` : ""}{" "}
|
||
ve toplam <strong>{sayi(il.programSayisi)} program</strong> 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.
|
||
Veriler YÖK Atlas {SON_YIL} yerleştirme sonuçlarına dayanır;{" "}
|
||
{TERCIH_YILI} tercihleri için rehber niteliğindedir.
|
||
</p>
|
||
</div>
|
||
}
|
||
/>
|
||
|
||
<div className="mt-8">
|
||
<CtaSiraForm varyant="kompakt" konum="ust" baslik={ctaBaslik} />
|
||
</div>
|
||
|
||
<PagePixelDivider seed={71} className="mx-auto mt-8 lg:mx-0" />
|
||
|
||
<UniGrubu baslik="Devlet Üniversiteleri" uniler={devlet} buIl={il.il} />
|
||
<UniGrubu baslik="Vakıf Üniversiteleri" uniler={vakif} buIl={il.il} />
|
||
<UniGrubu baslik="KKTC ve Yurt Dışı" uniler={diger} buIl={il.il} />
|
||
|
||
<section className="mt-12 text-center lg:text-left" aria-labelledby="diger-sehirler">
|
||
<h2
|
||
id="diger-sehirler"
|
||
className="font-heading text-2xl font-bold text-slate-900"
|
||
>
|
||
Diğer şehirler
|
||
</h2>
|
||
<SehirCipleri
|
||
iller={digerSehirler}
|
||
aktifSlug={slug}
|
||
tumuHref="/universiteler#tum-sehirler"
|
||
className="mt-4"
|
||
/>
|
||
</section>
|
||
|
||
<div className="mt-12">
|
||
<CtaSiraForm
|
||
baslik={ctaBaslik}
|
||
aciklama={`YKS başarı sıralamanı gir; ${il.ad} dahil tüm şehirlerde sıralamana uygun hayal, dengeli ve güvenli dilimlerindeki programları anında listele. Ücretsiz, üyelik gerekmez.`}
|
||
/>
|
||
</div>
|
||
</main>
|
||
);
|
||
}
|
||
|
||
export function IlIcerikSkeleton() {
|
||
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-10 space-y-4">
|
||
<Skeleton className="h-8 w-72" />
|
||
{Array.from({ length: 6 }, (_, i) => (
|
||
<Skeleton key={i} className="h-9 w-full max-w-2xl" />
|
||
))}
|
||
</div>
|
||
</main>
|
||
);
|
||
}
|