feat(veri): bileşenler yılsız Program'a ve veri-yillari sözlüğüne geçti (P-B b)
- SiraSerisi = { siraGecmisi, ilkYil? }; yardımcılar dizinin kendi son iki
elemanıyla çalışır, yıl ilkYil + indeks. raporSiraSerisi diziyi doldurmaz:
5 elemanlı eski raporlar 2024→2025 trendini göstermeye devam eder.
- Tablo başlıkları "Son ${VERI_YIL_SAYISI} yıl"; rapor listesi başlığı kaydın
kendi pencere uzunluğunu söyler (eski rapor: 5).
- siraYiliEtiketi SON_YIL/ONCEKI_YIL'dan; bölüm/üniversite sayfa metinleri ve
metadata SON_YIL, TERCIH_YILI, BASLIK_YILI sözlüğünden.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -1,30 +1,37 @@
|
|||||||
import { TrendingDown, TrendingUp } from "lucide-react";
|
import { TrendingDown, TrendingUp } from "lucide-react";
|
||||||
import type { Program } from "@/types/yokatlas";
|
import { ILK_YIL } from "@/lib/veri-yillari";
|
||||||
|
|
||||||
/** Taban sıra sütunlarının yapısal alt kümesi: hem DB programı hem de
|
/** Taban sıra geçmişinin yapısal alt kümesi: hem DB programı hem de rapordaki
|
||||||
* rapordaki kaydedilmiş sıra geçmişi (bkz. raporSiraSerisi) bu tipe uyar. */
|
* kaydedilmiş sıra geçmişi (bkz. raporSiraSerisi) bu tipe uyar. Dizi `ilkYil`'a
|
||||||
export type SiraSerisi = Pick<
|
* (yoksa veri penceresinin ilk yılına) demirlidir: eleman yılı
|
||||||
Program,
|
* `ilkYil + indeks`. Uzunluk sabit DEĞİL — eski kayıtlı raporlar daha kısa
|
||||||
"sira2021" | "sira2022" | "sira2023" | "sira2024" | "sira2025"
|
* dizi taşır; yardımcılar dizinin kendi son iki elemanıyla çalışır. */
|
||||||
>;
|
export type SiraSerisi = {
|
||||||
|
siraGecmisi: readonly (number | null)[];
|
||||||
|
ilkYil?: number;
|
||||||
|
};
|
||||||
|
|
||||||
/** Rapordaki [2021..2025] dizisini sütunlu seriye çevirir. */
|
/** Rapordaki kayıtlı diziyi seriye çevirir. Dizi DOLDURULMAZ: 5 elemanlı eski
|
||||||
export function raporSiraSerisi(gecmis?: (number | null)[]): SiraSerisi {
|
* kayıt 5 elemanlı kalır, "son iki yıl" kaydın kendi son iki yılıdır. */
|
||||||
const g = gecmis ?? [];
|
export function raporSiraSerisi(
|
||||||
return {
|
gecmis?: (number | null)[],
|
||||||
sira2021: g[0] ?? null,
|
ilkYil?: number,
|
||||||
sira2022: g[1] ?? null,
|
): SiraSerisi {
|
||||||
sira2023: g[2] ?? null,
|
return { siraGecmisi: gecmis ?? [], ilkYil };
|
||||||
sira2024: g[3] ?? null,
|
}
|
||||||
sira2025: g[4] ?? null,
|
|
||||||
};
|
/** Serinin son iki yılı: [bir önceki, son]. */
|
||||||
|
function sonIkiSira(program: SiraSerisi): [number | null, number | null] {
|
||||||
|
const g = program.siraGecmisi;
|
||||||
|
return [g[g.length - 2] ?? null, g[g.length - 1] ?? null];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function programEtkinSira(program: SiraSerisi): number | null {
|
export function programEtkinSira(program: SiraSerisi): number | null {
|
||||||
return program.sira2025 ?? program.sira2024;
|
const [onceki, son] = sonIkiSira(program);
|
||||||
|
return son ?? onceki;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 2021–2025 taban sıralamasını sonuç listesindeki kompakt çizgiyle gösterir. */
|
/** Taban sıralaması geçmişini sonuç listesindeki kompakt çizgiyle gösterir. */
|
||||||
export function ProgramSiraGecmisi({ program }: { program: SiraSerisi }) {
|
export function ProgramSiraGecmisi({ program }: { program: SiraSerisi }) {
|
||||||
// Satır başına render edilen bileşen: filter/map/min/max/map/map'in 6 ayrı
|
// Satır başına render edilen bileşen: filter/map/min/max/map/map'in 6 ayrı
|
||||||
// geçişi tek döngüde toplanır (js-combine-iterations, js-min-max-loop)
|
// geçişi tek döngüde toplanır (js-combine-iterations, js-min-max-loop)
|
||||||
@@ -32,14 +39,10 @@ export function ProgramSiraGecmisi({ program }: { program: SiraSerisi }) {
|
|||||||
let min = Infinity;
|
let min = Infinity;
|
||||||
let max = -Infinity;
|
let max = -Infinity;
|
||||||
const ozetParcalari: string[] = [];
|
const ozetParcalari: string[] = [];
|
||||||
for (const [yil, sira] of [
|
const ilkYil = program.ilkYil ?? ILK_YIL;
|
||||||
[2021, program.sira2021],
|
for (const [indeks, sira] of program.siraGecmisi.entries()) {
|
||||||
[2022, program.sira2022],
|
|
||||||
[2023, program.sira2023],
|
|
||||||
[2024, program.sira2024],
|
|
||||||
[2025, program.sira2025],
|
|
||||||
] as const) {
|
|
||||||
if (sira == null) continue;
|
if (sira == null) continue;
|
||||||
|
const yil = ilkYil + indeks;
|
||||||
yillar.push([yil, sira]);
|
yillar.push([yil, sira]);
|
||||||
if (sira < min) min = sira;
|
if (sira < min) min = sira;
|
||||||
if (sira > max) max = sira;
|
if (sira > max) max = sira;
|
||||||
@@ -89,12 +92,13 @@ export function ProgramSiraGecmisi({ program }: { program: SiraSerisi }) {
|
|||||||
|
|
||||||
/** Son iki yılın tabanı %10'dan fazla değiştiğinde yönü işaretler. */
|
/** Son iki yılın tabanı %10'dan fazla değiştiğinde yönü işaretler. */
|
||||||
export function ProgramTabanTrendi({ program }: { program: SiraSerisi }) {
|
export function ProgramTabanTrendi({ program }: { program: SiraSerisi }) {
|
||||||
if (program.sira2025 == null || program.sira2024 == null) return null;
|
const [onceki, son] = sonIkiSira(program);
|
||||||
|
if (son == null || onceki == null) return null;
|
||||||
|
|
||||||
const oran = (program.sira2024 - program.sira2025) / program.sira2024;
|
const oran = (onceki - son) / onceki;
|
||||||
if (Math.abs(oran) < 0.1) return null;
|
if (Math.abs(oran) < 0.1) return null;
|
||||||
|
|
||||||
const gecis = `${program.sira2024.toLocaleString("tr-TR")} → ${program.sira2025.toLocaleString("tr-TR")}`;
|
const gecis = `${onceki.toLocaleString("tr-TR")} → ${son.toLocaleString("tr-TR")}`;
|
||||||
if (oran > 0) {
|
if (oran > 0) {
|
||||||
return (
|
return (
|
||||||
<TrendingUp
|
<TrendingUp
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ import { VeriKaynagi } from "./veri-kaynagi";
|
|||||||
import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form";
|
import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form";
|
||||||
import { SiteFooter } from "@/components/site-footer";
|
import { SiteFooter } from "@/components/site-footer";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
import {
|
||||||
|
BASLIK_YILI,
|
||||||
|
ONCEKI_YIL,
|
||||||
|
SON_YIL,
|
||||||
|
TERCIH_YILI,
|
||||||
|
} from "@/lib/veri-yillari";
|
||||||
|
|
||||||
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
||||||
|
|
||||||
@@ -65,20 +71,20 @@ function bolumSayfaDilimi(bolum: BolumDetay, sayfa: number) {
|
|||||||
let minSira: number | null = null;
|
let minSira: number | null = null;
|
||||||
let maxSira: number | null = null;
|
let maxSira: number | null = null;
|
||||||
let verisiz = 0;
|
let verisiz = 0;
|
||||||
// Sırası 2025'te olmayıp 2024'ten gelen satırlar: aralık metni yılı dürüstçe
|
// Sırası son yılda olmayıp bir önceki yıldan gelen satırlar: aralık metni
|
||||||
// söylesin (tablonun Sıralama sütunu bu satırlarda "—" gösterir).
|
// yılı dürüstçe söylesin (tablonun Sıralama sütunu bu satırlarda "—" gösterir).
|
||||||
let yalniz2024 = 0;
|
let yalnizOnceki = 0;
|
||||||
for (const p of dilim) {
|
for (const p of dilim) {
|
||||||
const efektif = p.sira2025 ?? p.sira2024;
|
const efektif = p.efektifSira;
|
||||||
if (efektif == null) {
|
if (efektif == null) {
|
||||||
verisiz += 1;
|
verisiz += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (p.sira2025 == null) yalniz2024 += 1;
|
if (p.siraSon == null) yalnizOnceki += 1;
|
||||||
if (minSira == null || efektif < minSira) minSira = efektif;
|
if (minSira == null || efektif < minSira) minSira = efektif;
|
||||||
if (maxSira == null || efektif > maxSira) maxSira = efektif;
|
if (maxSira == null || efektif > maxSira) maxSira = efektif;
|
||||||
}
|
}
|
||||||
return { tumProgramlar, dilim, minSira, maxSira, verisiz, yalniz2024 };
|
return { tumProgramlar, dilim, minSira, maxSira, verisiz, yalnizOnceki };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** <title> için kısa sıra: 2.425.061 → "2,43 mn", 95.400 → "95 bin". */
|
/** <title> için kısa sıra: 2.425.061 → "2,43 mn", 95.400 → "95 bin". */
|
||||||
@@ -99,13 +105,13 @@ function siraAraligiMetni({
|
|||||||
minSira,
|
minSira,
|
||||||
maxSira,
|
maxSira,
|
||||||
verisiz,
|
verisiz,
|
||||||
yalniz2024,
|
yalnizOnceki,
|
||||||
}: ReturnType<typeof bolumSayfaDilimi>): string | null {
|
}: ReturnType<typeof bolumSayfaDilimi>): string | null {
|
||||||
if (dilim.length === 0) return null;
|
if (dilim.length === 0) return null;
|
||||||
if (minSira == null || maxSira == null) {
|
if (minSira == null || maxSira == null) {
|
||||||
return "taban verisi olmayan programlar";
|
return "taban verisi olmayan programlar";
|
||||||
}
|
}
|
||||||
const yil = siraYiliEtiketi(yalniz2024, dilim.length - verisiz);
|
const yil = siraYiliEtiketi(yalnizOnceki, dilim.length - verisiz);
|
||||||
const aralik =
|
const aralik =
|
||||||
minSira === maxSira
|
minSira === maxSira
|
||||||
? `${yil} taban sırası ${sayi(minSira)} olan programlar`
|
? `${yil} taban sırası ${sayi(minSira)} olan programlar`
|
||||||
@@ -134,8 +140,8 @@ export function bolumSayfaMetadata(slug: string, sayfa: number): Metadata {
|
|||||||
const meta = sayfaMetadata({
|
const meta = sayfaMetadata({
|
||||||
title: baslik,
|
title: baslik,
|
||||||
description: aralik
|
description: aralik
|
||||||
? `${bolum.ad} taban puanları, sayfa ${sayfa}: ${aralik}. Toplam ${stats.toplam} programın son yerleştirme verileri (2025; 2025'te verisi olmayanlarda 2024) — taban puanı, başarı sıralaması ve kontenjan.`
|
? `${bolum.ad} taban puanları, sayfa ${sayfa}: ${aralik}. Toplam ${stats.toplam} programın son yerleştirme verileri (${SON_YIL}; o yıl verisi olmayanlarda ${ONCEKI_YIL}) — taban puanı, başarı sıralaması ve kontenjan.`
|
||||||
: `${bolum.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${stats.toplam} programın 2025 yerleştirme verileri.`,
|
: `${bolum.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${stats.toplam} programın ${SON_YIL} yerleştirme verileri.`,
|
||||||
path: `/bolum/${slug}/sayfa/${sayfa}`,
|
path: `/bolum/${slug}/sayfa/${sayfa}`,
|
||||||
openGraph: {
|
openGraph: {
|
||||||
images: ustSegmentOgGorseli(
|
images: ustSegmentOgGorseli(
|
||||||
@@ -151,8 +157,8 @@ export function bolumSayfaMetadata(slug: string, sayfa: number): Metadata {
|
|||||||
? ` En iyi taban sıralaması ${sayi(stats.minSira)}.`
|
? ` En iyi taban sıralaması ${sayi(stats.minSira)}.`
|
||||||
: "";
|
: "";
|
||||||
return sayfaMetadata({
|
return sayfaMetadata({
|
||||||
title: `${bolum.ad} Taban Puanları ve Başarı Sıralamaları 2026`,
|
title: `${bolum.ad} Taban Puanları ve Başarı Sıralamaları ${BASLIK_YILI}`,
|
||||||
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.`,
|
description: `${bolum.ad} ${SON_YIL} yerleştirme verileri: ${stats.toplam} programın taban puanı ve sıralaması, ${stats.devlet} devlet / ${stats.vakif} vakıf.${enIyi} ${TERCIH_YILI} tercih rehberi.`,
|
||||||
path: `/bolum/${slug}`,
|
path: `/bolum/${slug}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -230,7 +236,7 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
|
|||||||
</h1>
|
</h1>
|
||||||
{ilkSayfa ? (
|
{ilkSayfa ? (
|
||||||
<p className="mt-3 max-w-2xl text-sm leading-6 text-muted-foreground sm:text-base">
|
<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
|
{sayi(tumProgramlar.length)} üniversite programının {SON_YIL} taban
|
||||||
puanlarını, başarı sıralamalarını ve kontenjanlarını incele.
|
puanlarını, başarı sıralamalarını ve kontenjanlarını incele.
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Link from "next/link";
|
|||||||
import { ChevronDown, ChevronUp } from "lucide-react";
|
import { ChevronDown, ChevronUp } from "lucide-react";
|
||||||
import type { Program } from "@/types/yokatlas";
|
import type { Program } from "@/types/yokatlas";
|
||||||
import { trBaslikDuzeni } from "@/lib/slug";
|
import { trBaslikDuzeni } from "@/lib/slug";
|
||||||
|
import { VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||||
import { Sayfalama, SAYFA_BOYU } from "./sayfalama";
|
import { Sayfalama, SAYFA_BOYU } from "./sayfalama";
|
||||||
import {
|
import {
|
||||||
ProgramSiraGecmisi,
|
ProgramSiraGecmisi,
|
||||||
@@ -24,14 +25,14 @@ import {
|
|||||||
|
|
||||||
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
||||||
|
|
||||||
type SiralamaAlani = "sira2025" | "puan2025" | "kontenjan2025";
|
type SiralamaAlani = "siraSon" | "puanSon" | "kontenjanSon";
|
||||||
type SiralamaYonu = "artan" | "azalan";
|
type SiralamaYonu = "artan" | "azalan";
|
||||||
export type BolumProgrami = Program & { universiteSlug: string | null };
|
export type BolumProgrami = Program & { universiteSlug: string | null };
|
||||||
|
|
||||||
const ALAN_ETIKETLERI: Record<SiralamaAlani, string> = {
|
const ALAN_ETIKETLERI: Record<SiralamaAlani, string> = {
|
||||||
sira2025: "Sıralama",
|
siraSon: "Sıralama",
|
||||||
puan2025: "Puan",
|
puanSon: "Puan",
|
||||||
kontenjan2025: "Kontenjan",
|
kontenjanSon: "Kontenjan",
|
||||||
};
|
};
|
||||||
|
|
||||||
function SiralamaDugmesi({
|
function SiralamaDugmesi({
|
||||||
@@ -157,11 +158,11 @@ export function BolumProgramListesi({
|
|||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Üniversite</TableHead>
|
<TableHead>Üniversite</TableHead>
|
||||||
<TableHead className="hidden text-right md:table-cell">
|
<TableHead className="hidden text-right md:table-cell">
|
||||||
Son 5 yıl
|
Son {VERI_YIL_SAYISI} yıl
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="hidden text-right lg:table-cell">
|
<TableHead className="hidden text-right lg:table-cell">
|
||||||
<SiralamaDugmesi
|
<SiralamaDugmesi
|
||||||
alan="puan2025"
|
alan="puanSon"
|
||||||
aktifAlan={aktifAlan}
|
aktifAlan={aktifAlan}
|
||||||
yon={yon}
|
yon={yon}
|
||||||
onSirala={sirala}
|
onSirala={sirala}
|
||||||
@@ -169,7 +170,7 @@ export function BolumProgramListesi({
|
|||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="hidden text-right sm:table-cell">
|
<TableHead className="hidden text-right sm:table-cell">
|
||||||
<SiralamaDugmesi
|
<SiralamaDugmesi
|
||||||
alan="kontenjan2025"
|
alan="kontenjanSon"
|
||||||
aktifAlan={aktifAlan}
|
aktifAlan={aktifAlan}
|
||||||
yon={yon}
|
yon={yon}
|
||||||
onSirala={sirala}
|
onSirala={sirala}
|
||||||
@@ -177,7 +178,7 @@ export function BolumProgramListesi({
|
|||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="text-right">
|
<TableHead className="text-right">
|
||||||
<SiralamaDugmesi
|
<SiralamaDugmesi
|
||||||
alan="sira2025"
|
alan="siraSon"
|
||||||
aktifAlan={aktifAlan}
|
aktifAlan={aktifAlan}
|
||||||
yon={yon}
|
yon={yon}
|
||||||
onSirala={sirala}
|
onSirala={sirala}
|
||||||
@@ -243,17 +244,17 @@ export function BolumProgramListesi({
|
|||||||
<ProgramSiraGecmisi program={p} />
|
<ProgramSiraGecmisi program={p} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="hidden text-right text-xs tabular-nums text-slate-500 lg:table-cell">
|
<TableCell className="hidden text-right text-xs tabular-nums text-slate-500 lg:table-cell">
|
||||||
{p.puan2025 != null
|
{p.puanSon != null
|
||||||
? p.puan2025.toFixed(2).replace(".", ",")
|
? p.puanSon.toFixed(2).replace(".", ",")
|
||||||
: "—"}
|
: "—"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="hidden text-right text-xs tabular-nums text-slate-500 sm:table-cell">
|
<TableCell className="hidden text-right text-xs tabular-nums text-slate-500 sm:table-cell">
|
||||||
{p.kontenjan2025 != null ? `${sayi(p.kontenjan2025)} kişi` : "—"}
|
{p.kontenjanSon != null ? `${sayi(p.kontenjanSon)} kişi` : "—"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right text-xs tabular-nums text-slate-500">
|
<TableCell className="text-right text-xs tabular-nums text-slate-500">
|
||||||
<span className="inline-flex items-center gap-1">
|
<span className="inline-flex items-center gap-1">
|
||||||
<ProgramTabanTrendi program={p} />
|
<ProgramTabanTrendi program={p} />
|
||||||
{p.sira2025 != null ? `~${sayi(p.sira2025)}.` : "—"}
|
{p.siraSon != null ? `~${sayi(p.siraSon)}.` : "—"}
|
||||||
</span>
|
</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="pr-3 text-right">
|
<TableCell className="pr-3 text-right">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||||
|
import { ONCEKI_YIL, SON_YIL } from "@/lib/veri-yillari";
|
||||||
|
|
||||||
/** Programatik tablo sayfaları için satır dilimi boyutu. */
|
/** Programatik tablo sayfaları için satır dilimi boyutu. */
|
||||||
export const SAYFA_BOYU = 50;
|
export const SAYFA_BOYU = 50;
|
||||||
@@ -13,10 +14,19 @@ export function sayfaDilimi<T>(liste: readonly T[], sayfa: number): T[] {
|
|||||||
return liste.slice((sayfa - 1) * SAYFA_BOYU, sayfa * SAYFA_BOYU);
|
return liste.slice((sayfa - 1) * SAYFA_BOYU, sayfa * SAYFA_BOYU);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Aralıktaki sıraların hangi yıldan geldiği: hepsi 2025 / hepsi 2024 / karışık. */
|
/**
|
||||||
export function siraYiliEtiketi(yalniz2024: number, siraliSayi: number): string {
|
* Aralıktaki sıraların hangi yıldan geldiği: hepsi son yıl / hepsi bir önceki
|
||||||
if (yalniz2024 === 0) return "2025";
|
* yıl / karışık. `yalnizOnceki`: sırası son yılda olmayıp bir önceki yıldan
|
||||||
return yalniz2024 === siraliSayi ? "2024" : "2024–2025";
|
* gelen satır sayısı; `siraliSayi`: sırası olan toplam satır.
|
||||||
|
*/
|
||||||
|
export function siraYiliEtiketi(
|
||||||
|
yalnizOnceki: number,
|
||||||
|
siraliSayi: number,
|
||||||
|
): string {
|
||||||
|
if (yalnizOnceki === 0) return String(SON_YIL);
|
||||||
|
return yalnizOnceki === siraliSayi
|
||||||
|
? String(ONCEKI_YIL)
|
||||||
|
: `${ONCEKI_YIL}–${SON_YIL}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import { UniLogo } from "@/components/uni-logo";
|
|||||||
import { uniLogoYolu } from "@/lib/uni-logolar";
|
import { uniLogoYolu } from "@/lib/uni-logolar";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import type { Program } from "@/types/yokatlas";
|
import type { Program } from "@/types/yokatlas";
|
||||||
|
import { BASLIK_YILI, SON_YIL, TERCIH_YILI } from "@/lib/veri-yillari";
|
||||||
|
|
||||||
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
||||||
|
|
||||||
@@ -91,13 +92,13 @@ function fakulteOzeti({
|
|||||||
if (adlar.length === 0) return null;
|
if (adlar.length === 0) return null;
|
||||||
if (adlar.length === 1) {
|
if (adlar.length === 1) {
|
||||||
const siralar = dilim
|
const siralar = dilim
|
||||||
.map((p) => p.sira2025 ?? p.sira2024)
|
.map((p) => p.efektifSira)
|
||||||
.filter((n): n is number => n != null);
|
.filter((n): n is number => n != null);
|
||||||
if (siralar.length === 0) return adlar[0];
|
if (siralar.length === 0) return adlar[0];
|
||||||
const min = Math.min(...siralar);
|
const min = Math.min(...siralar);
|
||||||
const max = Math.max(...siralar);
|
const max = Math.max(...siralar);
|
||||||
const yil = siraYiliEtiketi(
|
const yil = siraYiliEtiketi(
|
||||||
dilim.filter((p) => p.sira2025 == null && p.sira2024 != null).length,
|
dilim.filter((p) => p.siraSon == null && p.efektifSira != null).length,
|
||||||
siralar.length,
|
siralar.length,
|
||||||
);
|
);
|
||||||
return min === max
|
return min === max
|
||||||
@@ -116,10 +117,10 @@ export function uniSayfaMetadata(slug: string, sayfa: number): Metadata {
|
|||||||
// farklılaştırıcı (o sayfadaki birimler) açıklamaya ve gövdeye gider.
|
// farklılaştırıcı (o sayfadaki birimler) açıklamaya ve gövdeye gider.
|
||||||
const birimler = fakulteOzeti(uniSayfaDilimi(uni, sayfa));
|
const birimler = fakulteOzeti(uniSayfaDilimi(uni, sayfa));
|
||||||
return sayfaMetadata({
|
return sayfaMetadata({
|
||||||
title: `${uni.ad} Taban Puanları 2026 (Sayfa ${sayfa})`,
|
title: `${uni.ad} Taban Puanları ${BASLIK_YILI} (Sayfa ${sayfa})`,
|
||||||
description: birimler
|
description: birimler
|
||||||
? `${uni.ad} taban puanları, sayfa ${sayfa}: ${birimler}. Toplam ${uni.programSayisi} programın 2025 yerleştirme verileri — taban puanı, başarı sıralaması ve kontenjan.`
|
? `${uni.ad} taban puanları, sayfa ${sayfa}: ${birimler}. Toplam ${uni.programSayisi} programın ${SON_YIL} yerleştirme verileri — taban puanı, başarı sıralaması ve kontenjan.`
|
||||||
: `${uni.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${uni.programSayisi} programın 2025 yerleştirme verileri.`,
|
: `${uni.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${uni.programSayisi} programın ${SON_YIL} yerleştirme verileri.`,
|
||||||
path: `/universite/${slug}/sayfa/${sayfa}`,
|
path: `/universite/${slug}/sayfa/${sayfa}`,
|
||||||
openGraph: {
|
openGraph: {
|
||||||
images: ustSegmentOgGorseli(
|
images: ustSegmentOgGorseli(
|
||||||
@@ -130,8 +131,8 @@ export function uniSayfaMetadata(slug: string, sayfa: number): Metadata {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return sayfaMetadata({
|
return sayfaMetadata({
|
||||||
title: `${uni.ad} Taban Puanları ve Başarı Sıralamaları 2026`,
|
title: `${uni.ad} Taban Puanları ve Başarı Sıralamaları ${BASLIK_YILI}`,
|
||||||
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.`,
|
description: `${uni.ad} ${SON_YIL} yerleştirme verileri: ${uni.programSayisi} programın taban puanları, başarı sıralamaları ve kontenjanları${uni.il ? ` (${trBaslikDuzeni(uni.il)})` : ""}. ${TERCIH_YILI} tercih rehberi.`,
|
||||||
path: `/universite/${slug}`,
|
path: `/universite/${slug}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -241,7 +242,7 @@ export function UniversiteIcerik({
|
|||||||
<div className="mt-4 flex items-center gap-4">
|
<div className="mt-4 flex items-center gap-4">
|
||||||
<UniLogo slug={slug} ad={uni.ad} boy="lg" />
|
<UniLogo slug={slug} ad={uni.ad} boy="lg" />
|
||||||
<h1 className="font-heading text-3xl font-bold text-slate-900 sm:text-4xl">
|
<h1 className="font-heading text-3xl font-bold text-slate-900 sm:text-4xl">
|
||||||
{uni.ad} Taban Puanları 2026
|
{uni.ad} Taban Puanları {BASLIK_YILI}
|
||||||
{ilkSayfa ? "" : ` — Sayfa ${sayfa}`}
|
{ilkSayfa ? "" : ` — Sayfa ${sayfa}`}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -250,7 +251,7 @@ export function UniversiteIcerik({
|
|||||||
<>
|
<>
|
||||||
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">
|
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">
|
||||||
{uni.ad}
|
{uni.ad}
|
||||||
{uni.il ? ` (${trBaslikDuzeni(uni.il)})` : ""}, 2025 YKS
|
{uni.il ? ` (${trBaslikDuzeni(uni.il)})` : ""}, {SON_YIL} YKS
|
||||||
yerleştirme sonuçlarına göre{" "}
|
yerleştirme sonuçlarına göre{" "}
|
||||||
<strong>{sayi(uni.programSayisi)} programda</strong> öğrenci aldı
|
<strong>{sayi(uni.programSayisi)} programda</strong> öğrenci aldı
|
||||||
{uni.fakulteSayisi > 1
|
{uni.fakulteSayisi > 1
|
||||||
@@ -267,10 +268,11 @@ export function UniversiteIcerik({
|
|||||||
{uni.stats.toplamKontenjan > 0 ? (
|
{uni.stats.toplamKontenjan > 0 ? (
|
||||||
<> Toplam kontenjan {sayi(uni.stats.toplamKontenjan)}.</>
|
<> Toplam kontenjan {sayi(uni.stats.toplamKontenjan)}.</>
|
||||||
) : null}{" "}
|
) : null}{" "}
|
||||||
Aşağıdaki tablolar 2026 tercihleri için rehber niteliğindedir;{" "}
|
Aşağıdaki tablolar {TERCIH_YILI} tercihleri için rehber
|
||||||
|
niteliğindedir;{" "}
|
||||||
<em>
|
<em>
|
||||||
2026 taban puanları ve sıralamaları, yerleştirme sonuçları
|
{TERCIH_YILI} taban puanları ve sıralamaları, yerleştirme
|
||||||
açıklandığında netleşir.
|
sonuçları açıklandığında netleşir.
|
||||||
</em>
|
</em>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import type { Program } from "@/types/yokatlas";
|
import type { Program } from "@/types/yokatlas";
|
||||||
|
import { SON_YIL, VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||||
import {
|
import {
|
||||||
ProgramSiraGecmisi,
|
ProgramSiraGecmisi,
|
||||||
ProgramTabanTrendi,
|
ProgramTabanTrendi,
|
||||||
@@ -48,10 +49,10 @@ export function UniversiteProgramTablosu({
|
|||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Program</TableHead>
|
<TableHead>Program</TableHead>
|
||||||
<TableHead className="hidden text-right md:table-cell">
|
<TableHead className="hidden text-right md:table-cell">
|
||||||
Son 5 yıl
|
Son {VERI_YIL_SAYISI} yıl
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="hidden text-right lg:table-cell">
|
<TableHead className="hidden text-right lg:table-cell">
|
||||||
2025 puan
|
{SON_YIL} puan
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="hidden text-right sm:table-cell">
|
<TableHead className="hidden text-right sm:table-cell">
|
||||||
Kontenjan
|
Kontenjan
|
||||||
@@ -93,13 +94,13 @@ export function UniversiteProgramTablosu({
|
|||||||
<ProgramSiraGecmisi program={p} />
|
<ProgramSiraGecmisi program={p} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="hidden text-right text-xs tabular-nums text-slate-500 lg:table-cell">
|
<TableCell className="hidden text-right text-xs tabular-nums text-slate-500 lg:table-cell">
|
||||||
{p.puan2025 != null
|
{p.puanSon != null
|
||||||
? p.puan2025.toFixed(2).replace(".", ",")
|
? p.puanSon.toFixed(2).replace(".", ",")
|
||||||
: "—"}
|
: "—"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="hidden text-right text-xs tabular-nums text-slate-500 sm:table-cell">
|
<TableCell className="hidden text-right text-xs tabular-nums text-slate-500 sm:table-cell">
|
||||||
{p.kontenjan2025 != null
|
{p.kontenjanSon != null
|
||||||
? `${sayi(p.kontenjan2025)} kişi`
|
? `${sayi(p.kontenjanSon)} kişi`
|
||||||
: "—"}
|
: "—"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right text-xs tabular-nums text-slate-500">
|
<TableCell className="text-right text-xs tabular-nums text-slate-500">
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ type ProgramOzet = Pick<
|
|||||||
| "il"
|
| "il"
|
||||||
| "unitur"
|
| "unitur"
|
||||||
| "tur"
|
| "tur"
|
||||||
| "sira2025"
|
| "efektifSira"
|
||||||
| "sira2024"
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
const PROFILSIZ_EKLEME_TOAST_ID = "profilsiz-ekleme";
|
const PROFILSIZ_EKLEME_TOAST_ID = "profilsiz-ekleme";
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import type {
|
|||||||
} from "@/types/yokatlas";
|
} from "@/types/yokatlas";
|
||||||
import { DILIM_ETIKET, riskHesapla, type RiskSeviyesi } from "@/lib/risk";
|
import { DILIM_ETIKET, riskHesapla, type RiskSeviyesi } from "@/lib/risk";
|
||||||
import { bolumSayfaSlug, uniSayfaSlug } from "@/lib/slug";
|
import { bolumSayfaSlug, uniSayfaSlug } from "@/lib/slug";
|
||||||
|
import { VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||||
import {
|
import {
|
||||||
ProgramSiraGecmisi,
|
ProgramSiraGecmisi,
|
||||||
ProgramTabanTrendi,
|
ProgramTabanTrendi,
|
||||||
@@ -239,7 +240,7 @@ export function ProgramTablosu({
|
|||||||
<TableHead className="w-8" />
|
<TableHead className="w-8" />
|
||||||
<TableHead>Program</TableHead>
|
<TableHead>Program</TableHead>
|
||||||
<TableHead className="hidden text-right md:table-cell">
|
<TableHead className="hidden text-right md:table-cell">
|
||||||
Son 5 yıl
|
Son {VERI_YIL_SAYISI} yıl
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="hidden text-right sm:table-cell">
|
<TableHead className="hidden text-right sm:table-cell">
|
||||||
Kontenjan
|
Kontenjan
|
||||||
@@ -357,19 +358,19 @@ function ProgramSatiri({
|
|||||||
<TableCell
|
<TableCell
|
||||||
className="hidden text-right text-xs tabular-nums text-slate-500 sm:table-cell"
|
className="hidden text-right text-xs tabular-nums text-slate-500 sm:table-cell"
|
||||||
title={
|
title={
|
||||||
p.kontenjan2025 != null && p.yerlesen2025 != null
|
p.kontenjanSon != null && p.yerlesenSon != null
|
||||||
? `${p.kontenjan2025} kontenjan, ${p.yerlesen2025} yerleşen`
|
? `${p.kontenjanSon} kontenjan, ${p.yerlesenSon} yerleşen`
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{p.kontenjan2025 != null ? (
|
{p.kontenjanSon != null ? (
|
||||||
<span className="inline-flex items-center gap-1.5">
|
<span className="inline-flex items-center gap-1.5">
|
||||||
{p.yerlesen2025 != null && p.yerlesen2025 < p.kontenjan2025 ? (
|
{p.yerlesenSon != null && p.yerlesenSon < p.kontenjanSon ? (
|
||||||
<span className="rounded bg-amber-50 px-1 py-px text-[10px] font-medium leading-4 text-amber-600">
|
<span className="rounded bg-amber-50 px-1 py-px text-[10px] font-medium leading-4 text-amber-600">
|
||||||
boş kaldı
|
boş kaldı
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
{p.kontenjan2025.toLocaleString("tr-TR")} kişi
|
{p.kontenjanSon.toLocaleString("tr-TR")} kişi
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
"—"
|
"—"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// 24'lük listenin gövdesi. Görsel dil sonuç sayfasındaki ham program
|
// 24'lük listenin gövdesi. Görsel dil sonuç sayfasındaki ham program
|
||||||
// tablosuyla birebir aynıdır (bkz. program-tablosu.tsx): aynı beyaz kart,
|
// tablosuyla birebir aynıdır (bkz. program-tablosu.tsx): aynı beyaz kart,
|
||||||
// aynı satır düzeni, aynı "Son 5 yıl" / "Taban sıra" sütunları. Buradaki fark
|
// aynı satır düzeni, aynı "Son N yıl" / "Taban sıra" sütunları. Buradaki fark
|
||||||
// içerik: satır sırası tercih numarası, risk sütunu Yapay Zeka'dan gelir ve
|
// içerik: satır sırası tercih numarası, risk sütunu Yapay Zeka'dan gelir ve
|
||||||
// satır açıldığında gerekçe/risk notu görünür. Program adı ve tabanlar herkese
|
// satır açıldığında gerekçe/risk notu görünür. Program adı ve tabanlar herkese
|
||||||
// açık; Yapay Zeka katmanı kilitliBaslangic sonrasında ücretlidir (metin
|
// açık; Yapay Zeka katmanı kilitliBaslangic sonrasında ücretlidir (metin
|
||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { bolumSayfaSlug, uniSayfaSlug } from "@/lib/slug";
|
import { bolumSayfaSlug, uniSayfaSlug } from "@/lib/slug";
|
||||||
|
import { VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||||
import type { RaporSonuc } from "@/features/rapor/types/rapor";
|
import type { RaporSonuc } from "@/features/rapor/types/rapor";
|
||||||
import {
|
import {
|
||||||
DILIM_ETIKET,
|
DILIM_ETIKET,
|
||||||
@@ -47,7 +48,7 @@ const RISK_STIL: Record<RiskSeviyesi, { nokta: string; icon: typeof Rocket }> =
|
|||||||
guvenli: { nokta: "bg-emerald-500", icon: ShieldCheck },
|
guvenli: { nokta: "bg-emerald-500", icon: ShieldCheck },
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sütun sayısı: # · Program · Son 5 yıl · Risk · Taban sıra · detay oku
|
// Sütun sayısı: # · Program · Son N yıl · Risk · Taban sıra · detay oku
|
||||||
const SUTUN_SAYISI = 6;
|
const SUTUN_SAYISI = 6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,6 +162,19 @@ export function RaporListesi({
|
|||||||
return iller.size === 1 ? [...iller][0] : null;
|
return iller.size === 1 ? [...iller][0] : null;
|
||||||
}, [rapor]);
|
}, [rapor]);
|
||||||
|
|
||||||
|
// Sütun başlığı kaydın KENDİ penceresini söyler: eski raporların sıra
|
||||||
|
// geçmişi üretildiği günün yıl sayısını taşır (dizi doldurulmaz), başlık
|
||||||
|
// çizgide olmayan bir yılı vaat etmesin. Geçmişi hiç olmayan kayıtta
|
||||||
|
// bugünkü pencereye düşer.
|
||||||
|
const yilSayisi = useMemo(() => {
|
||||||
|
let enUzun = 0;
|
||||||
|
for (const t of rapor.tercihler) {
|
||||||
|
const n = rapor.programlar[t.programId]?.siraGecmisi?.length ?? 0;
|
||||||
|
if (n > enUzun) enUzun = n;
|
||||||
|
}
|
||||||
|
return enUzun > 0 ? enUzun : VERI_YIL_SAYISI;
|
||||||
|
}, [rapor]);
|
||||||
|
|
||||||
const satir = (t: RaporSonuc["tercihler"][number], index: number) => {
|
const satir = (t: RaporSonuc["tercihler"][number], index: number) => {
|
||||||
const p = rapor.programlar[t.programId];
|
const p = rapor.programlar[t.programId];
|
||||||
const analizKilitli = index >= kilitIdx;
|
const analizKilitli = index >= kilitIdx;
|
||||||
@@ -168,7 +182,7 @@ export function RaporListesi({
|
|||||||
(adaySira != null ? riskHesapla(p?.efektifSira, adaySira) : null) ??
|
(adaySira != null ? riskHesapla(p?.efektifSira, adaySira) : null) ??
|
||||||
dilimdenRisk(t.dilim);
|
dilimdenRisk(t.dilim);
|
||||||
const stil = RISK_STIL[risk];
|
const stil = RISK_STIL[risk];
|
||||||
const seri = raporSiraSerisi(p?.siraGecmisi);
|
const seri = raporSiraSerisi(p?.siraGecmisi, p?.ilkYil);
|
||||||
const taban = p?.efektifSira ?? programEtkinSira(seri);
|
const taban = p?.efektifSira ?? programEtkinSira(seri);
|
||||||
const acikMi = acik.has(t.sira);
|
const acikMi = acik.has(t.sira);
|
||||||
const devlet = p?.unitur === "DEVLET";
|
const devlet = p?.unitur === "DEVLET";
|
||||||
@@ -366,7 +380,7 @@ export function RaporListesi({
|
|||||||
<TableHead className="w-8 pl-4 pr-0">#</TableHead>
|
<TableHead className="w-8 pl-4 pr-0">#</TableHead>
|
||||||
<TableHead>Program</TableHead>
|
<TableHead>Program</TableHead>
|
||||||
<TableHead className="hidden text-right md:table-cell">
|
<TableHead className="hidden text-right md:table-cell">
|
||||||
Son 5 yıl
|
Son {yilSayisi} yıl
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<span className="hidden sm:inline">Yapay Zeka </span>Risk
|
<span className="hidden sm:inline">Yapay Zeka </span>Risk
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export function TadimlikSatiri({
|
|||||||
|
|
||||||
const p = satir.program;
|
const p = satir.program;
|
||||||
const risk = riskHesapla(p.efektifSira, sira) ?? dilimdenRisk(satir.dilim);
|
const risk = riskHesapla(p.efektifSira, sira) ?? dilimdenRisk(satir.dilim);
|
||||||
const seri = raporSiraSerisi(p.siraGecmisi);
|
const seri = raporSiraSerisi(p.siraGecmisi, p.ilkYil);
|
||||||
const taban = p.efektifSira ?? programEtkinSira(seri);
|
const taban = p.efektifSira ?? programEtkinSira(seri);
|
||||||
const devlet = p.unitur === "DEVLET";
|
const devlet = p.unitur === "DEVLET";
|
||||||
const girisUrl = `/giris?callback=${encodeURIComponent(sonucHref())}`;
|
const girisUrl = `/giris?callback=${encodeURIComponent(sonucHref())}`;
|
||||||
|
|||||||
Reference in New Issue
Block a user