Some checks failed
Deploy / deploy (push) Failing after 38m47s
- Introduced a mechanism to save partially generated responses in case of errors, ensuring users do not lose their input. - Updated the RevizyonKutusu component to utilize a persistent request ID for retrying failed requests without double charging credits. - Improved the Sayfalama component to support client-side pagination without changing the URL, enhancing user navigation. - Added a timeout for AI generation requests to ensure credits are refunded in case of prolonged processing. - Refactored various components for better clarity and responsiveness, including updates to the BolumProgramListesi and ListePaneli components.
126 lines
4.5 KiB
TypeScript
126 lines
4.5 KiB
TypeScript
import type { Metadata } from "next";
|
||
import Link from "next/link";
|
||
import { notFound } from "next/navigation";
|
||
import { ChevronRight } from "lucide-react";
|
||
import { getBolumBySlug, uniSlugFromAd } from "@/lib/katalog";
|
||
import { JsonLd, breadcrumbJsonLd } from "@/lib/seo";
|
||
import { SAYFA_BOYU } from "@/components/sayfalama";
|
||
import { BolumProgramListesi } from "@/components/bolum-program-listesi";
|
||
import { SiteFooter } from "@/components/site-footer";
|
||
|
||
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
||
|
||
export function bolumToplamSayfa(programSayisi: number): number {
|
||
return Math.max(1, Math.ceil(programSayisi / SAYFA_BOYU));
|
||
}
|
||
|
||
export function bolumSayfaMetadata(slug: string, sayfa: number): Metadata {
|
||
const bolum = getBolumBySlug(slug);
|
||
if (!bolum) return {};
|
||
const { stats } = bolum;
|
||
if (sayfa > 1) {
|
||
return {
|
||
title: `${bolum.ad} Taban Puanları 2026 (Sayfa ${sayfa})`,
|
||
description: `${bolum.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${stats.toplam} programın 2025 yerleştirme verileri.`,
|
||
alternates: { canonical: `/bolum/${slug}/sayfa/${sayfa}` },
|
||
};
|
||
}
|
||
const enIyi =
|
||
stats.minSira != null
|
||
? ` En iyi taban sıralaması ${sayi(stats.minSira)}.`
|
||
: "";
|
||
return {
|
||
title: `${bolum.ad} Taban Puanları ve Başarı Sıralamaları 2026`,
|
||
description: `${bolum.ad} 2025 yerleştirme verileri: ${stats.toplam} programın taban puanı ve sıralaması, ${stats.devlet} devlet / ${stats.vakif} vakıf.${enIyi} 2026 tercih rehberi.`,
|
||
alternates: { canonical: `/bolum/${slug}` },
|
||
};
|
||
}
|
||
|
||
export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
|
||
const bolum = getBolumBySlug(slug);
|
||
if (!bolum) notFound();
|
||
|
||
const tumProgramlar = [
|
||
...bolum.lisansProgramlari,
|
||
...bolum.onlisansProgramlari,
|
||
];
|
||
const toplamSayfa = bolumToplamSayfa(tumProgramlar.length);
|
||
if (sayfa < 1 || sayfa > toplamSayfa) notFound();
|
||
const ilkSayfa = sayfa === 1;
|
||
const buYol = ilkSayfa ? `/bolum/${slug}` : `/bolum/${slug}/sayfa/${sayfa}`;
|
||
|
||
return (
|
||
<>
|
||
<main className="mx-auto w-full max-w-5xl px-4 py-10 sm:py-14">
|
||
<JsonLd
|
||
data={breadcrumbJsonLd([
|
||
{ name: "Ana Sayfa", path: "/" },
|
||
{ name: "Bölümler", path: "/bolumler" },
|
||
{ name: bolum.ad, path: `/bolum/${slug}` },
|
||
...(ilkSayfa ? [] : [{ name: `Sayfa ${sayfa}`, path: buYol }]),
|
||
])}
|
||
/>
|
||
|
||
<nav
|
||
aria-label="İçerik yolu"
|
||
className="mb-5 flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground"
|
||
>
|
||
<Link
|
||
href="/"
|
||
className="transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||
>
|
||
Ana sayfa
|
||
</Link>
|
||
<ChevronRight className="size-3.5" aria-hidden />
|
||
<Link
|
||
href="/bolumler"
|
||
className="transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||
>
|
||
Bölümler
|
||
</Link>
|
||
<ChevronRight className="size-3.5" aria-hidden />
|
||
<span className="text-foreground">
|
||
{ilkSayfa ? bolum.ad : `Sayfa ${sayfa}`}
|
||
</span>
|
||
</nav>
|
||
|
||
<header className="border-b pb-7 sm:pb-9">
|
||
<h1 className="max-w-3xl font-heading text-3xl font-bold tracking-tight text-foreground sm:text-5xl">
|
||
{bolum.ad}
|
||
</h1>
|
||
<p className="mt-3 max-w-2xl text-sm leading-6 text-muted-foreground sm:text-base">
|
||
{sayi(tumProgramlar.length)} üniversite programının 2025 taban
|
||
puanlarını, başarı sıralamalarını ve kontenjanlarını incele
|
||
{!ilkSayfa ? ` — sayfa ${sayfa}` : ""}.
|
||
</p>
|
||
</header>
|
||
|
||
<section aria-labelledby="universite-programlari" className="mt-8">
|
||
<div className="mb-4 flex items-end justify-between gap-4">
|
||
<h2
|
||
id="universite-programlari"
|
||
className="font-heading text-xl font-bold text-foreground sm:text-2xl"
|
||
>
|
||
Üniversite programları
|
||
</h2>
|
||
<span className="shrink-0 text-sm text-muted-foreground">
|
||
{sayi(tumProgramlar.length)} program
|
||
</span>
|
||
</div>
|
||
|
||
<BolumProgramListesi
|
||
programlar={tumProgramlar.map((program) => ({
|
||
...program,
|
||
universiteSlug: uniSlugFromAd(program.universite),
|
||
}))}
|
||
bazAd={bolum.ad}
|
||
tabanYol={`/bolum/${slug}`}
|
||
sayfa={sayfa}
|
||
/>
|
||
</section>
|
||
</main>
|
||
<SiteFooter />
|
||
</>
|
||
);
|
||
}
|