Update routing in next.config.ts to redirect "/tercih-robotu" to the hero form, add simple-icons dependency in package.json, and enhance database indexing for improved query performance. Remove unused SVG files and update metadata across various pages for better SEO and clarity.
All checks were successful
Deploy / deploy (push) Successful in 14m14s

This commit is contained in:
bilalgursen
2026-08-01 17:28:38 +03:00
parent 569b9e5da2
commit 8cf0c3267d
76 changed files with 5050 additions and 255 deletions

View File

@@ -0,0 +1,25 @@
import type { Metadata } from "next";
import { getAllUniversiteSlugs } from "@/lib/katalog";
import { UniversiteIcerik, uniSayfaMetadata } from "./universite-icerik";
export function generateStaticParams() {
return getAllUniversiteSlugs().map((slug) => ({ slug }));
}
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}): Promise<Metadata> {
const { slug } = await params;
return uniSayfaMetadata(slug, 1);
}
export default async function UniversitePage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
return <UniversiteIcerik slug={slug} sayfa={1} />;
}

View File

@@ -0,0 +1,47 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { getAllUniversiteler } from "@/lib/katalog";
import {
UniversiteIcerik,
uniSayfaMetadata,
uniToplamSayfa,
} from "../../universite-icerik";
// Yalnızca 1'den fazla sayfası olan üniversiteler için 2..N üretilir;
// 1. sayfa taban rota (/universite/[slug]) olduğundan burada üretilmez.
export function generateStaticParams() {
return getAllUniversiteler().flatMap((u) => {
const toplam = uniToplamSayfa(u.programSayisi);
return Array.from({ length: Math.max(0, toplam - 1) }, (_, i) => ({
slug: u.slug,
no: String(i + 2),
}));
});
}
function sayfaNo(no: string): number | null {
const n = Number(no);
return Number.isInteger(n) && n >= 2 ? n : null;
}
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string; no: string }>;
}): Promise<Metadata> {
const { slug, no } = await params;
const n = sayfaNo(no);
if (n == null) return {};
return uniSayfaMetadata(slug, n);
}
export default async function UniversiteSayfaPage({
params,
}: {
params: Promise<{ slug: string; no: string }>;
}) {
const { slug, no } = await params;
const n = sayfaNo(no);
if (n == null) notFound();
return <UniversiteIcerik slug={slug} sayfa={n} />;
}

View File

@@ -0,0 +1,336 @@
import type { Metadata } from "next";
import Link from "next/link";
import { notFound } from "next/navigation";
import { ChevronRight } from "lucide-react";
import { getUniversiteBySlug, bolumSlugFromIsim } from "@/lib/katalog";
import { trBaslikDuzeni } from "@/lib/slug";
import { JsonLd, breadcrumbJsonLd, SITE_URL } from "@/lib/seo";
import { CtaSiraForm } from "@/components/cta-sira-form";
import { SiteFooter } from "@/components/site-footer";
import { Sayfalama, SAYFA_BOYU } from "@/components/sayfalama";
import { PagePixelDivider } from "@/components/pixel-decor";
import { UniversiteKonumHaritasi } from "@/components/universite-konum-haritasi";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import type { Program } from "@/lib/db";
const sayi = (n: number) => n.toLocaleString("tr-TR");
const TUR_ETIKET: Record<string, string> = {
SAYISAL: "SAY",
"EŞİT AĞIRLIK": "EA",
SÖZEL: "SÖZ",
DİL: "DİL",
TYT: "TYT",
};
export function uniToplamSayfa(programSayisi: number): number {
return Math.max(1, Math.ceil(programSayisi / SAYFA_BOYU));
}
export function uniSayfaMetadata(slug: string, sayfa: number): Metadata {
const uni = getUniversiteBySlug(slug);
if (!uni) return {};
if (sayfa > 1) {
return {
title: `${uni.ad} Taban Puanları 2026 (Sayfa ${sayfa})`,
description: `${uni.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${uni.programSayisi} programın 2025 yerleştirme verileri.`,
alternates: { canonical: `/universite/${slug}/sayfa/${sayfa}` },
};
}
return {
title: `${uni.ad} Taban Puanları ve Başarı Sıralamaları 2026`,
description: `${uni.ad} 2025 yerleştirme verileri: ${uni.programSayisi} programın taban puanları, başarı sıralamaları ve kontenjanları${uni.il ? ` (${trBaslikDuzeni(uni.il)})` : ""}. 2026 tercih rehberi.`,
alternates: { canonical: `/universite/${slug}` },
};
}
export function UniversiteIcerik({
slug,
sayfa,
}: {
slug: string;
sayfa: number;
}) {
const uni = getUniversiteBySlug(slug);
if (!uni) notFound();
// Sayfalama fakülte sıralı düz liste üzerinden; dilim içi yeniden gruplanır
const tumProgramlar = uni.fakulteler.flatMap((f) => f.programlar);
const toplamSayfa = uniToplamSayfa(tumProgramlar.length);
if (sayfa < 1 || sayfa > toplamSayfa) notFound();
const dilim = tumProgramlar.slice(
(sayfa - 1) * SAYFA_BOYU,
sayfa * SAYFA_BOYU,
);
const gruplar: { fakulte: string; programlar: Program[] }[] = [];
for (const p of dilim) {
const anahtar = p.fakulte ?? "Diğer";
const son = gruplar[gruplar.length - 1];
if (son && son.fakulte === anahtar) son.programlar.push(p);
else gruplar.push({ fakulte: anahtar, programlar: [p] });
}
const ilkSayfa = sayfa === 1;
const buYol = ilkSayfa
? `/universite/${slug}`
: `/universite/${slug}/sayfa/${sayfa}`;
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: uni.ad, path: `/universite/${slug}` },
...(ilkSayfa ? [] : [{ name: `Sayfa ${sayfa}`, path: buYol }]),
])}
/>
{ilkSayfa ? (
<JsonLd
data={{
"@context": "https://schema.org",
"@type": "CollegeOrUniversity",
name: uni.ad,
url: `${SITE_URL}/universite/${slug}`,
...(uni.il
? {
address: {
"@type": "PostalAddress",
addressLocality: trBaslikDuzeni(uni.il),
addressCountry: "TR",
},
}
: {}),
}}
/>
) : null}
<nav
aria-label="breadcrumb"
className="flex items-center gap-1 text-xs text-slate-500"
>
<Link href="/" className="hover:text-primary hover:underline">
Ana Sayfa
</Link>
<ChevronRight className="size-3" aria-hidden />
<Link
href="/universiteler"
className="hover:text-primary hover:underline"
>
Üniversiteler
</Link>
<ChevronRight className="size-3" aria-hidden />
{ilkSayfa ? (
<span className="text-slate-700">{uni.ad}</span>
) : (
<>
<Link
href={`/universite/${slug}`}
className="hover:text-primary hover:underline"
>
{uni.ad}
</Link>
<ChevronRight className="size-3" aria-hidden />
<span className="text-slate-700">Sayfa {sayfa}</span>
</>
)}
</nav>
<h1 className="mt-3 font-heading text-3xl font-bold text-slate-900 sm:text-4xl">
{uni.ad} Taban Puanları 2026
{ilkSayfa ? "" : ` — Sayfa ${sayfa}`}
</h1>
{ilkSayfa ? (
<>
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">
{uni.ad}
{uni.il ? ` (${trBaslikDuzeni(uni.il)})` : ""}, 2025 YKS
yerleştirme sonuçlarına göre{" "}
<strong>{sayi(uni.programSayisi)} programda</strong> öğrenci aldı
{uni.fakulteSayisi > 1
? `; programlar ${sayi(uni.fakulteSayisi)} fakülte/yüksekokula dağılıyor`
: ""}
.
{uni.stats.minSira != null ? (
<>
{" "}
En iyi taban başarı sıralaması{" "}
<strong>{sayi(uni.stats.minSira)}</strong>.
</>
) : null}
{uni.stats.toplamKontenjan > 0 ? (
<> Toplam kontenjan {sayi(uni.stats.toplamKontenjan)}.</>
) : null}{" "}
Aşağıdaki tablolar 2026 tercihleri için rehber niteliğindedir;{" "}
<em>
2026 taban puanları ve sıralamaları, yerleştirme sonuçları
ıklandığında netleşir.
</em>
</p>
<div className="mt-6 grid grid-cols-2 gap-3 lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2">
<dl className="contents">
<div className="rounded-xl border border-slate-200 bg-white p-4">
<dt className="text-xs font-medium uppercase tracking-wide text-slate-500">
Program
</dt>
<dd className="mt-1 font-heading text-2xl font-bold text-slate-900">
{sayi(uni.programSayisi)}
</dd>
</div>
<div className="rounded-xl border border-slate-200 bg-white p-4">
<dt className="text-xs font-medium uppercase tracking-wide text-slate-500">
Fakülte / Birim
</dt>
<dd className="mt-1 font-heading text-2xl font-bold text-slate-900">
{sayi(uni.fakulteSayisi)}
</dd>
</div>
<div className="rounded-xl border border-slate-200 bg-white p-4">
<dt className="text-xs font-medium uppercase tracking-wide text-slate-500">
Tür
</dt>
<dd className="mt-1 font-heading text-2xl font-bold text-slate-900">
{uni.unitur === "DEVLET"
? "Devlet"
: uni.unitur
? trBaslikDuzeni(uni.unitur)
: "—"}
</dd>
</div>
<div className="rounded-xl border border-slate-200 bg-white p-4">
<dt className="text-xs font-medium uppercase tracking-wide text-slate-500">
En iyi sıralama
</dt>
<dd className="mt-1 font-heading text-2xl font-bold text-slate-900">
{uni.stats.minSira != null ? sayi(uni.stats.minSira) : "—"}
</dd>
</div>
</dl>
<UniversiteKonumHaritasi universite={uni.ad} il={uni.il} />
</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{" "}
{sayi((sayfa - 1) * SAYFA_BOYU + 1)}
{sayi(Math.min(sayfa * SAYFA_BOYU, tumProgramlar.length))}.
satırları, fakülte sırasıyla). Üniversite özeti ve istatistikler{" "}
<Link
href={`/universite/${slug}`}
className="font-medium text-primary hover:underline"
>
ilk sayfada
</Link>
.
</p>
)}
<PagePixelDivider seed={57} className="mt-8" />
<div
id="programlar"
className="mt-8"
>
<h2 className="font-heading text-xl font-bold text-slate-900">
Üniversite programları
</h2>
<p className="mt-1 text-xs text-slate-500">
Fakülte ve birimlere göre
</p>
</div>
{gruplar.map(({ fakulte, programlar }, i) => (
<section key={`${fakulte}-${i}`} className="mt-6">
<h2 className="mb-3 font-heading text-xl font-bold text-slate-900">
{fakulte}
</h2>
<div className="overflow-x-auto rounded-xl border border-slate-200 bg-white">
<Table>
<TableHeader>
<TableRow>
<TableHead>Program</TableHead>
<TableHead>Puan Türü</TableHead>
<TableHead className="text-right">2025 Puan</TableHead>
<TableHead className="text-right">2025 Sıralama</TableHead>
<TableHead className="text-right">2024 Sıralama</TableHead>
<TableHead className="text-right">Kontenjan</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{programlar.map((p) => {
const bolumSlug = bolumSlugFromIsim(p.isim);
return (
<TableRow key={p.id}>
<TableCell className="max-w-72">
{bolumSlug ? (
<Link
href={`/bolum/${bolumSlug}`}
className="font-medium text-slate-900 hover:text-primary hover:underline"
>
{p.isim.trim()}
</Link>
) : (
<span className="font-medium">
{p.isim.trim()}
</span>
)}
</TableCell>
<TableCell>{TUR_ETIKET[p.tur] ?? p.tur}</TableCell>
<TableCell className="text-right tabular-nums">
{p.puan2025 != null
? p.puan2025.toFixed(2).replace(".", ",")
: "—"}
</TableCell>
<TableCell className="text-right tabular-nums">
{p.sira2025 != null ? sayi(p.sira2025) : "—"}
</TableCell>
<TableCell className="text-right tabular-nums">
{p.sira2024 != null ? sayi(p.sira2024) : "—"}
</TableCell>
<TableCell className="text-right tabular-nums">
{p.kontenjan2025 != null
? sayi(p.kontenjan2025)
: "—"}
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</section>
))}
<p className="mt-3 text-xs leading-5 text-slate-500">
&quot;&quot;: o yıl için yerleştirme verisi bulunmuyor (yeni ılan
veya o yıl yerleşen çıkmayan program). Kaynak: YÖK Atlas 2025
yerleştirme sonuçları.
</p>
<Sayfalama
tabanYol={`/universite/${slug}`}
sayfa={sayfa}
toplamSayfa={toplamSayfa}
/>
<div className="mt-10">
<CtaSiraForm
baslik={`Sıralamanla ${uni.ad} programlarına yerleşir misin?`}
/>
</div>
</main>
<SiteFooter />
</>
);
}