Enhance layout and functionality by integrating TercihProfiliKapisiLazy component into layout.tsx, improving user experience with a new section in the RootLayout. Update BolumlerPage and ListemPage for better structure and readability, including adjustments to headings and content organization. Refactor UniversiteIcerik to utilize UniversiteProgramTablosu for displaying program data, and streamline UniversiteKonumHaritasi for improved rendering. Update ManuelListe components for clearer user instructions and enhanced interaction. Overall, these changes aim to improve visual clarity and user engagement across the application.
All checks were successful
Deploy / deploy (push) Successful in 7m25s

This commit is contained in:
bilalgursen
2026-08-01 17:58:38 +03:00
parent 8cf0c3267d
commit 55eb32b22a
21 changed files with 2137 additions and 709 deletions

View File

@@ -78,46 +78,46 @@ export default function BolumlerPage() {
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: "Bölümler", path: "/bolumler" },
])}
/>
<main className="mx-auto w-full max-w-5xl px-4 py-12 sm:py-16">
<JsonLd
data={breadcrumbJsonLd([
{ name: "Ana Sayfa", path: "/" },
{ name: "Bölümler", path: "/bolumler" },
])}
/>
<h1 className="font-heading text-3xl font-bold text-slate-900 sm:text-4xl">
Tüm Bölümlerin Taban Puanları ve Sıralamaları 2026
</h1>
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">
{sayi(hepsi.length)} bölümün 2025 YKS yerleştirme verileri: taban
puanları, başarı sıralamaları, kontenjanlar ve devlet/vakıf dağılımı.
Bölüme tıklayınca o bölümü sunan tüm üniversitelerin tablosunu
görürsün. Veriler YÖK Atlas 2025 yerleştirme sonuçlarına dayanır; 2026
tercihleri için rehber niteliğindedir.
</p>
<h1 className="font-heading text-3xl font-bold text-slate-900 sm:text-4xl">
Tüm Bölümlerin Taban Puanları ve Sıralamaları 2026
</h1>
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">
{sayi(hepsi.length)} bölümün 2025 YKS yerleştirme verileri: taban
puanları, başarı sıralamaları, kontenjanlar ve devlet/vakıf dağılımı.
Bölüme tıklayınca o bölümü sunan tüm üniversitelerin tablosunu
görürsün. Veriler YÖK Atlas 2025 yerleştirme sonuçlarına dayanır; 2026
tercihleri için rehber niteliğindedir.
</p>
<PagePixelDivider seed={29} className="mt-8" />
<PagePixelDivider seed={29} className="mt-8" />
<section className="mt-8">
<h2 className="font-heading text-2xl font-bold text-slate-900">
Lisans Bölümleri (4 Yıllık)
</h2>
<KategoriGruplari bolumler={lisans} seviye="lisans" />
</section>
<section className="mt-8">
<h2 className="font-heading text-2xl font-bold text-slate-900">
Lisans Bölümleri (4 Yıllık)
</h2>
<KategoriGruplari bolumler={lisans} seviye="lisans" />
</section>
<div className="mt-12">
<CtaSiraForm />
</div>
<div className="mt-12">
<CtaSiraForm />
</div>
<section className="mt-12">
<h2 className="font-heading text-2xl font-bold text-slate-900">
Önlisans Bölümleri (2 Yıllık TYT)
</h2>
<KategoriGruplari bolumler={onlisans} seviye="onlisans" />
</section>
</main>
<SiteFooter />
<section className="mt-12">
<h2 className="font-heading text-2xl font-bold text-slate-900">
Önlisans Bölümleri (2 Yıllık TYT)
</h2>
<KategoriGruplari bolumler={onlisans} seviye="onlisans" />
</section>
</main>
<SiteFooter />
</>
);
}

View File

@@ -5,6 +5,7 @@ import { Toaster } from "@/components/ui/sonner";
import { SiteHeader } from "@/components/site-header";
import { SiteTopBanner } from "@/components/site-top-banner";
import { ListeCekmecesiLazy } from "@/components/manuel-liste/liste-cekmecesi-lazy";
import { TercihProfiliKapisiLazy } from "@/components/manuel-liste/tercih-profili-kapisi-lazy";
import { HeaderGate } from "@/components/header-gate";
import { ProgressiveBlur } from "@/components/ui/skiper-ui/skiper41";
import { DevPanel } from "@/components/dev/dev-panel";
@@ -109,6 +110,8 @@ export default function RootLayout({
</ScrollFlow>
{/* Manuel 24'lük liste çekmecesi + "+" uçuş katmanı (navbar'dan açılır) */}
<ListeCekmecesiLazy />
{/* İlk "+" için sıra + sihirbaz kapısı */}
<TercihProfiliKapisiLazy />
<Toaster position="top-center" />
{/* Dev süperadmin paneli — prod build'de hiç render edilmez */}
{process.env.NODE_ENV !== "production" ? <DevPanel /> : null}

View File

@@ -0,0 +1,36 @@
"use client";
import { ListChecks, Sparkles } from "lucide-react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { SecimlerimPaneli } from "@/components/manuel-liste/secimlerim-paneli";
export type ListemSekme = "secimlerim" | "ai";
export function ListemSekmeleri({
varsayilan,
aiIcerik,
}: {
varsayilan: ListemSekme;
aiIcerik: React.ReactNode;
}) {
return (
<Tabs defaultValue={varsayilan} className="mt-2 gap-6">
<TabsList variant="line" className="h-auto w-full justify-start gap-1">
<TabsTrigger value="secimlerim" className="cursor-pointer px-3 py-2.5">
<ListChecks className="size-4" aria-hidden />
Seçtiklerim
</TabsTrigger>
<TabsTrigger value="ai" className="cursor-pointer px-3 py-2.5">
<Sparkles className="size-4" aria-hidden />
AI Listem
</TabsTrigger>
</TabsList>
<TabsContent value="secimlerim" className="mt-0">
<SecimlerimPaneli />
</TabsContent>
<TabsContent value="ai" className="mt-0">
{aiIcerik}
</TabsContent>
</Tabs>
);
}

View File

@@ -29,6 +29,7 @@ import { PagePixelDivider } from "@/components/pixel-decor";
import { raporMaskele, ACIK_SATIR } from "@/lib/rapor-maske";
import { SohbetClient, type Mesaj } from "./sohbet-client";
import { RevizyonKutusu } from "./revizyon-kutusu";
import { ListemSekmeleri, type ListemSekme } from "./listem-sekmeleri";
export const metadata: Metadata = {
title: "Listem",
@@ -42,18 +43,15 @@ const PAKET_FIYATI = (URUNLER.paket.amountKurus / 100).toLocaleString("tr-TR", {
export default async function ListemPage({
searchParams,
}: {
searchParams: Promise<{ acildi?: string }>;
searchParams: Promise<{ acildi?: string; sekme?: string }>;
}) {
const session = await verifySession("/listem");
// Kullanıcı satırı, ?acildi parametresi ve rapor birbirinden bağımsız —
// seri await zinciri yerine tek turda çöz.
const [user, { acildi }, satir, sohbetGecmisi] = await Promise.all([
const [user, params, satir, sohbetGecmisi] = await Promise.all([
getCurrentUser(),
searchParams,
appDb.query.reports.findFirst({
where: eq(schema.reports.userId, session.user.id),
}),
// Sohbet geçmişi burada gelirse client mount sonrası /api/soru turu atmaz
appDb
.select({
id: schema.chatMessages.id,
@@ -67,41 +65,64 @@ export default async function ListemPage({
]);
if (!user) redirect("/giris?callback=/listem");
// ?acildi=1: ödeme dönüşü — kilidin kalktığı tek seferlik açılış deneyimi
const { acildi, sekme } = params;
const kilitAcilisi = Boolean(user.hasPaket && acildi === "1");
const raporVar = Boolean(satir?.result);
// Liste yoksa önce sihirbazdan oluşturulmalı
if (!satir?.result) {
const varsayilanSekme: ListemSekme =
sekme === "secimlerim"
? "secimlerim"
: sekme === "ai" || acildi === "1" || raporVar
? "ai"
: "secimlerim";
const aiBos = (
<section className="mx-auto flex max-w-2xl flex-col items-center px-4 py-16 text-center">
<Sparkles className="size-10 text-primary" aria-hidden />
<h2 className="mt-4 font-heading text-2xl font-bold">
Henüz AI listen yok
</h2>
<p className="mt-3 text-slate-600">
Sıralamanı girip birkaç adımda 24 tercihlik risk analizli listeni kur;
sonra burada görüntüleyip AI danışmanla üzerinde konuşabilirsin.
Seçtiklerim sekmesinde ise tablolardan eklediğin programlar durur.
</p>
<PagePixelDivider seed={67} className="mx-auto mt-6" />
<Button
asChild
size="lg"
className="mt-6 h-12 cursor-pointer bg-orange-500 px-8 text-white hover:bg-orange-600"
>
<Link href="/">Sıralamanı gir, AI listeni kur</Link>
</Button>
</section>
);
if (!raporVar) {
return (
<main className="mx-auto flex max-w-2xl flex-col items-center px-4 py-24 text-center">
<Sparkles className="size-10 text-primary" aria-hidden />
<h1 className="mt-4 font-heading text-2xl font-bold">
Henüz bir listen yok
<main className="mx-auto w-full max-w-7xl px-4 py-8 sm:py-10">
<h1 className="font-heading text-2xl font-bold tracking-tight sm:text-3xl">
Tercih karar merkezin
</h1>
<p className="mt-3 text-slate-600">
Sıralamanı girip birkaç adımda 24 tercihlik risk analizli listeni kur;
sonra burada görüntüleyip AI danışmanla üzerinde konuşabilirsin.
<p className="mt-2 max-w-2xl text-sm leading-6 text-slate-600">
Seçtiklerin ve AI listen burada yan yana ikisi birbirinden bağımsız.
</p>
<PagePixelDivider seed={67} className="mx-auto mt-6" />
<Button
asChild
size="lg"
className="mt-6 h-12 cursor-pointer bg-orange-500 px-8 text-white hover:bg-orange-600"
>
<Link href="/">Sıralamanı gir, listeni kur</Link>
</Button>
<PagePixelDivider seed={71} className="mt-6" />
<ListemSekmeleri
varsayilan={varsayilanSekme}
aiIcerik={aiBos}
/>
</main>
);
}
// Paketsize giden rapor burada da maskelenir (bkz. rapor-maske)
const rapor = user.hasPaket
? (satir.result as RaporSonuc)
: raporMaskele(satir.result as RaporSonuc);
const params = satir.params as RaporParams;
? (satir!.result as RaporSonuc)
: raporMaskele(satir!.result as RaporSonuc);
const raporParams = satir!.params as RaporParams;
return (
<main className="mx-auto w-full max-w-7xl px-4 py-8 sm:py-10">
const aiIcerik = (
<>
{kilitAcilisi ? (
<div
role="status"
@@ -109,7 +130,7 @@ export default async function ListemPage({
>
<Sparkles className="size-4 shrink-0" aria-hidden />
Paketin aktif 24 tercihinin tamamı, gerekçeler, riskler ve PDF
artık ık. 🎉
artık ık.
</div>
) : null}
<div className="flex flex-wrap items-end justify-between gap-4">
@@ -125,14 +146,16 @@ export default async function ListemPage({
YÖK Atlas verileri ücretsiz
</Badge>
)}
<h1 className="max-w-2xl font-heading text-2xl font-bold tracking-tight sm:text-3xl">
<h2 className="max-w-2xl font-heading text-xl font-bold tracking-tight sm:text-2xl">
{user.hasPaket
? "Tercih karar merkezin"
? "AI tercih listen"
: "Veriyi ücretsiz incele. Kararı AI ile netleştir."}
</h1>
</h2>
<p className="mt-2 max-w-2xl text-sm leading-6 text-slate-600">
{params.sira.toLocaleString("tr-TR")}. sıra ·{" "}
{PUAN_TURLERI[params.tur]} ·{" "}
{raporParams.sira.toLocaleString("tr-TR")}. sıra ·{" "}
{PUAN_TURLERI[raporParams.tur as keyof typeof PUAN_TURLERI] ??
raporParams.tur}{" "}
·{" "}
{user.hasPaket
? "24 tercihinin tamamını gerekçe, risk analizi ve trendleriyle aşağıda inceleyebilirsin."
: "24 programın 20242025 taban sıralamalarını ve ham trendini aşağıda ücretsiz inceleyebilirsin."}
@@ -165,16 +188,13 @@ export default async function ListemPage({
</div>
) : null}
</div>
<PagePixelDivider seed={71} className="mt-6" />
{/* AI'ın genel değerlendirmesi ve uyarıları: paketliye tam,
paketsize sunucuda kırpılmış önizleme + kilit notu */}
{rapor.genelDegerlendirme ? (
<section className="mt-6 rounded-2xl border border-slate-200 bg-white p-5">
<h2 className="flex items-center gap-2 font-heading text-base font-bold">
<h3 className="flex items-center gap-2 font-heading text-base font-bold">
<Bot className="size-4 text-primary" aria-hidden />
Danışman özeti
</h2>
</h3>
<p className="mt-2 text-sm leading-6 text-slate-700">
{rapor.genelDegerlendirme}
</p>
@@ -205,51 +225,51 @@ export default async function ListemPage({
<div className="mt-8 grid items-start gap-8 lg:grid-cols-[minmax(0,1fr)_340px]">
<div className="flex min-w-0 flex-col gap-6">
<section
className={`min-w-0 rounded-2xl border border-slate-200 bg-slate-50/60 p-3 sm:p-5${
kilitAcilisi ? " kt-unblur" : ""
}`}
>
<RaporListesi
rapor={rapor}
adaySira={params.sira}
kilitliBaslangic={user.hasPaket ? undefined : ACIK_SATIR}
kilitOverlay={
<div className="flex flex-col items-start gap-3 sm:flex-row sm:items-center sm:justify-between">
<div className="flex gap-3">
<span className="flex size-10 shrink-0 items-center justify-center rounded-full bg-orange-100 text-orange-700">
<Lock className="size-4" aria-hidden />
</span>
<div>
<p className="font-heading font-bold text-slate-900">
İlk {ACIK_SATIR} tercihte AI analizini gördün
</p>
<p className="mt-1 max-w-xl text-sm text-slate-600">
Kalan {24 - ACIK_SATIR} tercihin kişisel risk, gerekçe ve
AI yorumu paketle ılır. Program adları ve taban verileri
her zaman ücretsiz.
</p>
<section
className={`min-w-0 rounded-2xl border border-slate-200 bg-slate-50/60 p-3 sm:p-5${
kilitAcilisi ? " kt-unblur" : ""
}`}
>
<RaporListesi
rapor={rapor}
adaySira={raporParams.sira}
kilitliBaslangic={user.hasPaket ? undefined : ACIK_SATIR}
kilitOverlay={
<div className="flex flex-col items-start gap-3 sm:flex-row sm:items-center sm:justify-between">
<div className="flex gap-3">
<span className="flex size-10 shrink-0 items-center justify-center rounded-full bg-orange-100 text-orange-700">
<Lock className="size-4" aria-hidden />
</span>
<div>
<p className="font-heading font-bold text-slate-900">
İlk {ACIK_SATIR} tercihte AI analizini gördün
</p>
<p className="mt-1 max-w-xl text-sm text-slate-600">
Kalan {24 - ACIK_SATIR} tercihin kişisel risk, gerekçe ve
AI yorumu paketle ılır. Program adları ve taban
verileri her zaman ücretsiz.
</p>
</div>
</div>
<Button
asChild
className="h-11 w-full shrink-0 cursor-pointer bg-orange-500 text-white transition-[background-color,transform] duration-200 active:scale-[0.97] hover:bg-orange-600 sm:w-auto"
>
<Link href="/paket">
Analizleri {PAKET_FIYATI} TL
<ArrowRight data-icon="inline-end" aria-hidden />
</Link>
</Button>
</div>
<Button
asChild
className="h-11 w-full shrink-0 cursor-pointer bg-orange-500 text-white transition-[background-color,transform] duration-200 active:scale-[0.97] hover:bg-orange-600 sm:w-auto"
>
<Link href="/paket">
Analizleri {PAKET_FIYATI} TL
<ArrowRight data-icon="inline-end" aria-hidden />
</Link>
</Button>
</div>
}
/>
</section>
}
/>
</section>
{user.hasPaket ? (
<RevizyonKutusu
kalanHak={Math.max(0, MAX_REVIZYON - (satir.revisionCount ?? 0))}
/>
) : null}
{user.hasPaket ? (
<RevizyonKutusu
kalanHak={Math.max(0, MAX_REVIZYON - (satir!.revisionCount ?? 0))}
/>
) : null}
</div>
<div id="sohbet" className="scroll-mt-24 lg:sticky lg:top-28">
@@ -260,91 +280,87 @@ export default async function ListemPage({
/>
) : (
<div className="flex flex-col gap-6">
<section className="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
<div className="border-b border-slate-100 p-6">
<div className="flex items-center gap-2 text-primary">
<Sparkles className="size-4" aria-hidden />
<span className="text-xs font-bold uppercase tracking-wide">
Tercih Dönemi Paketi
</span>
</div>
<h2 className="mt-3 font-heading text-xl font-bold tracking-tight">
Tablo ücretsiz.
<br />
Karar desteği pakette.
</h2>
<p className="mt-2 text-sm leading-6 text-slate-600">
Sadece veriyi göstermez; hangi tercihin neden mantıklı
olduğunu sıralamana göre ıklar.
</p>
</div>
<div className="p-6">
<ul className="flex flex-col gap-3 text-sm">
{[
"24 tercihin tamamında kişisel risk analizi",
"Her program için kısa, gerekçeli AI yorumu",
"2 liste revizyonu ve paylaşılabilir PDF",
`${URUNLER.paket.credits} AI danışman sorusu`,
].map((ozellik) => (
<li key={ozellik} className="flex items-start gap-2.5">
<Check
className="mt-0.5 size-4 shrink-0 text-emerald-600"
aria-hidden
/>
<span>{ozellik}</span>
</li>
))}
</ul>
<div className="mt-6 flex items-end justify-between">
<div>
<p className="text-xs text-slate-500">Tek seferlik</p>
<p className="font-heading text-3xl font-bold">
{PAKET_FIYATI} TL
</p>
<section className="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
<div className="border-b border-slate-100 p-6">
<div className="flex items-center gap-2 text-primary">
<Sparkles className="size-4" aria-hidden />
<span className="text-xs font-bold uppercase tracking-wide">
Tercih Dönemi Paketi
</span>
</div>
<Badge variant="outline">Abonelik yok</Badge>
<h3 className="mt-3 font-heading text-xl font-bold tracking-tight">
Tablo ücretsiz.
<br />
Karar desteği pakette.
</h3>
<p className="mt-2 text-sm leading-6 text-slate-600">
Sadece veriyi göstermez; hangi tercihin neden mantıklı
olduğunu sıralamana göre ıklar.
</p>
</div>
<Button
asChild
size="lg"
className="mt-5 h-12 w-full cursor-pointer bg-orange-500 text-white transition-[background-color,transform] duration-200 active:scale-[0.97] hover:bg-orange-600"
>
<Link href="/paket">
Kişisel analizi
<ArrowRight data-icon="inline-end" aria-hidden />
</Link>
</Button>
<div className="p-6">
<ul className="flex flex-col gap-3 text-sm">
{[
"24 tercihin tamamında kişisel risk analizi",
"Her program için kısa, gerekçeli AI yorumu",
"2 liste revizyonu ve paylaşılabilir PDF",
`${URUNLER.paket.credits} AI danışman sorusu`,
].map((ozellik) => (
<li key={ozellik} className="flex items-start gap-2.5">
<Check
className="mt-0.5 size-4 shrink-0 text-emerald-600"
aria-hidden
/>
<span>{ozellik}</span>
</li>
))}
</ul>
<div className="mt-4 flex items-center justify-center gap-4 text-[11px] text-slate-500">
<span className="inline-flex items-center gap-1">
<ShieldCheck className="size-3.5" aria-hidden />
iyzico güvenli ödeme
</span>
<span className="inline-flex items-center gap-1">
<Bot className="size-3.5" aria-hidden />
Gerçek YÖK verisi
</span>
<div className="mt-6 flex items-end justify-between">
<div>
<p className="text-xs text-slate-500">Tek seferlik</p>
<p className="font-heading text-3xl font-bold">
{PAKET_FIYATI} TL
</p>
</div>
<Badge variant="outline">Abonelik yok</Badge>
</div>
<Button
asChild
size="lg"
className="mt-5 h-12 w-full cursor-pointer bg-orange-500 text-white transition-[background-color,transform] duration-200 active:scale-[0.97] hover:bg-orange-600"
>
<Link href="/paket">
Kişisel analizi
<ArrowRight data-icon="inline-end" aria-hidden />
</Link>
</Button>
<div className="mt-4 flex items-center justify-center gap-4 text-[11px] text-slate-500">
<span className="inline-flex items-center gap-1">
<ShieldCheck className="size-3.5" aria-hidden />
iyzico güvenli ödeme
</span>
<span className="inline-flex items-center gap-1">
<Bot className="size-3.5" aria-hidden />
Gerçek YÖK verisi
</span>
</div>
</div>
</div>
</section>
</section>
{/* Deneme kredileri sohbette geçerli: AI'ı satın almadan önce
tattırır (kalan kredi biterse composer paket CTA'sına döner). */}
<SohbetClient
baslangicKredi={user.creditBalance}
initialMesajlar={sohbetGecmisi as Mesaj[]}
className="h-[480px]"
/>
<SohbetClient
baslangicKredi={user.creditBalance}
initialMesajlar={sohbetGecmisi as Mesaj[]}
className="h-[480px]"
/>
</div>
)}
</div>
</div>
{/* Mobilde sohbet sağ rayla birlikte en alta düşer; yüzen kısayol
kullanıcıyı 24 satırı kaydırmadan danışmana ulaştırır. */}
<ViewportPortal>
<a
href="#sohbet"
@@ -354,6 +370,19 @@ export default async function ListemPage({
Danışmana sor
</a>
</ViewportPortal>
</>
);
return (
<main className="mx-auto w-full max-w-7xl px-4 py-8 sm:py-10">
<h1 className="font-heading text-2xl font-bold tracking-tight sm:text-3xl">
Tercih karar merkezin
</h1>
<p className="mt-2 max-w-2xl text-sm leading-6 text-slate-600">
Seçtiklerin ve AI listen burada yan yana ikisi birbirinden bağımsız.
</p>
<PagePixelDivider seed={71} className="mt-6" />
<ListemSekmeleri varsayilan={varsayilanSekme} aiIcerik={aiIcerik} />
</main>
);
}

View File

@@ -10,26 +10,11 @@ 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 { UniversiteProgramTablosu } from "@/components/universite-program-tablosu";
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));
}
@@ -69,12 +54,16 @@ export function UniversiteIcerik({
(sayfa - 1) * SAYFA_BOYU,
sayfa * SAYFA_BOYU,
);
const gruplar: { fakulte: string; programlar: Program[] }[] = [];
const gruplar: {
fakulte: string;
programlar: (Program & { bolumSlug: string | null })[];
}[] = [];
for (const p of dilim) {
const anahtar = p.fakulte ?? "Diğer";
const satir = { ...p, bolumSlug: bolumSlugFromIsim(p.isim) };
const son = gruplar[gruplar.length - 1];
if (son && son.fakulte === anahtar) son.programlar.push(p);
else gruplar.push({ fakulte: anahtar, programlar: [p] });
if (son && son.fakulte === anahtar) son.programlar.push(satir);
else gruplar.push({ fakulte: anahtar, programlar: [satir] });
}
const ilkSayfa = sayfa === 1;
@@ -178,6 +167,7 @@ export function UniversiteIcerik({
</p>
<div className="mt-6 grid grid-cols-2 gap-3 lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2">
<UniversiteKonumHaritasi universite={uni.ad} il={uni.il} />
<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">
@@ -216,7 +206,6 @@ export function UniversiteIcerik({
</dd>
</div>
</dl>
<UniversiteKonumHaritasi universite={uni.ad} il={uni.il} />
</div>
</>
) : (
@@ -238,10 +227,7 @@ export function UniversiteIcerik({
<PagePixelDivider seed={57} className="mt-8" />
<div
id="programlar"
className="mt-8"
>
<div id="programlar" className="mt-8">
<h2 className="font-heading text-xl font-bold text-slate-900">
Üniversite programları
</h2>
@@ -250,67 +236,7 @@ export function UniversiteIcerik({
</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>
))}
<UniversiteProgramTablosu gruplar={gruplar} />
<p className="mt-3 text-xs leading-5 text-slate-500">
&quot;&quot;: o yıl için yerleştirme verisi bulunmuyor (yeni ılan

View File

@@ -49,64 +49,64 @@ export default function UniversitelerPage() {
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" },
])}
/>
<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" },
])}
/>
<h1 className="font-heading text-3xl font-bold text-slate-900 sm:text-4xl">
Üniversitelerin Taban Puanları ve Sıralamaları 2026
</h1>
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">
{sayi(hepsi.length)} üniversitenin 2025 YKS yerleştirme verileri.
Üniversiteye tıklayınca tüm 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 2025 yerleştirme sonuçlarına dayanır; 2026 tercihleri için
rehber niteliğindedir.
</p>
<h1 className="font-heading text-3xl font-bold text-slate-900 sm:text-4xl">
Üniversitelerin Taban Puanları ve Sıralamaları 2026
</h1>
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">
{sayi(hepsi.length)} üniversitenin 2025 YKS yerleştirme verileri.
Üniversiteye tıklayınca tüm 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 2025 yerleştirme sonuçlarına dayanır; 2026 tercihleri için
rehber niteliğindedir.
</p>
<PagePixelDivider seed={63} className="mt-8" />
<PagePixelDivider seed={63} className="mt-8" />
<section className="mt-8">
<h2 className="font-heading text-2xl font-bold text-slate-900">
Devlet Üniversiteleri
<span className="ml-2 text-sm font-normal text-slate-400">
{sayi(devlet.length)}
</span>
</h2>
<UniLinkleri uniler={devlet} />
</section>
<section className="mt-10">
<h2 className="font-heading text-2xl font-bold text-slate-900">
Vakıf Üniversiteleri
<span className="ml-2 text-sm font-normal text-slate-400">
{sayi(vakif.length)}
</span>
</h2>
<UniLinkleri uniler={vakif} />
</section>
{digerUniler.length > 0 ? (
<section className="mt-10">
<section className="mt-8">
<h2 className="font-heading text-2xl font-bold text-slate-900">
KKTC ve Yurt Dışı
Devlet Üniversiteleri
<span className="ml-2 text-sm font-normal text-slate-400">
{sayi(digerUniler.length)}
{sayi(devlet.length)}
</span>
</h2>
<UniLinkleri uniler={digerUniler} />
<UniLinkleri uniler={devlet} />
</section>
) : null}
<div className="mt-12">
<CtaSiraForm />
</div>
</main>
<SiteFooter />
<section className="mt-10">
<h2 className="font-heading text-2xl font-bold text-slate-900">
Vakıf Üniversiteleri
<span className="ml-2 text-sm font-normal text-slate-400">
{sayi(vakif.length)}
</span>
</h2>
<UniLinkleri uniler={vakif} />
</section>
{digerUniler.length > 0 ? (
<section className="mt-10">
<h2 className="font-heading text-2xl font-bold text-slate-900">
KKTC ve Yurt Dışı
<span className="ml-2 text-sm font-normal text-slate-400">
{sayi(digerUniler.length)}
</span>
</h2>
<UniLinkleri uniler={digerUniler} />
</section>
) : null}
<div className="mt-12">
<CtaSiraForm />
</div>
</main>
<SiteFooter />
</>
);
}