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 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
|
||||
* rapordaki kaydedilmiş sıra geçmişi (bkz. raporSiraSerisi) bu tipe uyar. */
|
||||
export type SiraSerisi = Pick<
|
||||
Program,
|
||||
"sira2021" | "sira2022" | "sira2023" | "sira2024" | "sira2025"
|
||||
>;
|
||||
/** Taban sıra geçmişinin yapısal alt kümesi: hem DB programı hem de rapordaki
|
||||
* kaydedilmiş sıra geçmişi (bkz. raporSiraSerisi) bu tipe uyar. Dizi `ilkYil`'a
|
||||
* (yoksa veri penceresinin ilk yılına) demirlidir: eleman yılı
|
||||
* `ilkYil + indeks`. Uzunluk sabit DEĞİL — eski kayıtlı raporlar daha kısa
|
||||
* 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. */
|
||||
export function raporSiraSerisi(gecmis?: (number | null)[]): SiraSerisi {
|
||||
const g = gecmis ?? [];
|
||||
return {
|
||||
sira2021: g[0] ?? null,
|
||||
sira2022: g[1] ?? null,
|
||||
sira2023: g[2] ?? null,
|
||||
sira2024: g[3] ?? null,
|
||||
sira2025: g[4] ?? null,
|
||||
};
|
||||
/** Rapordaki kayıtlı diziyi seriye çevirir. Dizi DOLDURULMAZ: 5 elemanlı eski
|
||||
* kayıt 5 elemanlı kalır, "son iki yıl" kaydın kendi son iki yılıdır. */
|
||||
export function raporSiraSerisi(
|
||||
gecmis?: (number | null)[],
|
||||
ilkYil?: number,
|
||||
): SiraSerisi {
|
||||
return { siraGecmisi: gecmis ?? [], ilkYil };
|
||||
}
|
||||
|
||||
/** 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 {
|
||||
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 }) {
|
||||
// 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)
|
||||
@@ -32,14 +39,10 @@ export function ProgramSiraGecmisi({ program }: { program: SiraSerisi }) {
|
||||
let min = Infinity;
|
||||
let max = -Infinity;
|
||||
const ozetParcalari: string[] = [];
|
||||
for (const [yil, sira] of [
|
||||
[2021, program.sira2021],
|
||||
[2022, program.sira2022],
|
||||
[2023, program.sira2023],
|
||||
[2024, program.sira2024],
|
||||
[2025, program.sira2025],
|
||||
] as const) {
|
||||
const ilkYil = program.ilkYil ?? ILK_YIL;
|
||||
for (const [indeks, sira] of program.siraGecmisi.entries()) {
|
||||
if (sira == null) continue;
|
||||
const yil = ilkYil + indeks;
|
||||
yillar.push([yil, sira]);
|
||||
if (sira < min) min = 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. */
|
||||
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;
|
||||
|
||||
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) {
|
||||
return (
|
||||
<TrendingUp
|
||||
|
||||
@@ -25,6 +25,12 @@ import { VeriKaynagi } from "./veri-kaynagi";
|
||||
import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
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");
|
||||
|
||||
@@ -65,20 +71,20 @@ function bolumSayfaDilimi(bolum: BolumDetay, sayfa: number) {
|
||||
let minSira: number | null = null;
|
||||
let maxSira: number | null = null;
|
||||
let verisiz = 0;
|
||||
// Sırası 2025'te olmayıp 2024'ten gelen satırlar: aralık metni yılı dürüstçe
|
||||
// söylesin (tablonun Sıralama sütunu bu satırlarda "—" gösterir).
|
||||
let yalniz2024 = 0;
|
||||
// Sırası son yılda olmayıp bir önceki yıldan gelen satırlar: aralık metni
|
||||
// yılı dürüstçe söylesin (tablonun Sıralama sütunu bu satırlarda "—" gösterir).
|
||||
let yalnizOnceki = 0;
|
||||
for (const p of dilim) {
|
||||
const efektif = p.sira2025 ?? p.sira2024;
|
||||
const efektif = p.efektifSira;
|
||||
if (efektif == null) {
|
||||
verisiz += 1;
|
||||
continue;
|
||||
}
|
||||
if (p.sira2025 == null) yalniz2024 += 1;
|
||||
if (p.siraSon == null) yalnizOnceki += 1;
|
||||
if (minSira == null || efektif < minSira) minSira = 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". */
|
||||
@@ -99,13 +105,13 @@ function siraAraligiMetni({
|
||||
minSira,
|
||||
maxSira,
|
||||
verisiz,
|
||||
yalniz2024,
|
||||
yalnizOnceki,
|
||||
}: ReturnType<typeof bolumSayfaDilimi>): string | null {
|
||||
if (dilim.length === 0) return null;
|
||||
if (minSira == null || maxSira == null) {
|
||||
return "taban verisi olmayan programlar";
|
||||
}
|
||||
const yil = siraYiliEtiketi(yalniz2024, dilim.length - verisiz);
|
||||
const yil = siraYiliEtiketi(yalnizOnceki, dilim.length - verisiz);
|
||||
const aralik =
|
||||
minSira === maxSira
|
||||
? `${yil} taban sırası ${sayi(minSira)} olan programlar`
|
||||
@@ -134,8 +140,8 @@ export function bolumSayfaMetadata(slug: string, sayfa: number): Metadata {
|
||||
const meta = sayfaMetadata({
|
||||
title: baslik,
|
||||
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ı ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${stats.toplam} programın 2025 yerleştirme verileri.`,
|
||||
? `${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 ${SON_YIL} yerleştirme verileri.`,
|
||||
path: `/bolum/${slug}/sayfa/${sayfa}`,
|
||||
openGraph: {
|
||||
images: ustSegmentOgGorseli(
|
||||
@@ -151,8 +157,8 @@ export function bolumSayfaMetadata(slug: string, sayfa: number): Metadata {
|
||||
? ` En iyi taban sıralaması ${sayi(stats.minSira)}.`
|
||||
: "";
|
||||
return sayfaMetadata({
|
||||
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.`,
|
||||
title: `${bolum.ad} Taban Puanları ve Başarı Sıralamaları ${BASLIK_YILI}`,
|
||||
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}`,
|
||||
});
|
||||
}
|
||||
@@ -230,7 +236,7 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
|
||||
</h1>
|
||||
{ilkSayfa ? (
|
||||
<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.
|
||||
</p>
|
||||
) : (
|
||||
|
||||
@@ -5,6 +5,7 @@ import Link from "next/link";
|
||||
import { ChevronDown, ChevronUp } from "lucide-react";
|
||||
import type { Program } from "@/types/yokatlas";
|
||||
import { trBaslikDuzeni } from "@/lib/slug";
|
||||
import { VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||
import { Sayfalama, SAYFA_BOYU } from "./sayfalama";
|
||||
import {
|
||||
ProgramSiraGecmisi,
|
||||
@@ -24,14 +25,14 @@ import {
|
||||
|
||||
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
||||
|
||||
type SiralamaAlani = "sira2025" | "puan2025" | "kontenjan2025";
|
||||
type SiralamaAlani = "siraSon" | "puanSon" | "kontenjanSon";
|
||||
type SiralamaYonu = "artan" | "azalan";
|
||||
export type BolumProgrami = Program & { universiteSlug: string | null };
|
||||
|
||||
const ALAN_ETIKETLERI: Record<SiralamaAlani, string> = {
|
||||
sira2025: "Sıralama",
|
||||
puan2025: "Puan",
|
||||
kontenjan2025: "Kontenjan",
|
||||
siraSon: "Sıralama",
|
||||
puanSon: "Puan",
|
||||
kontenjanSon: "Kontenjan",
|
||||
};
|
||||
|
||||
function SiralamaDugmesi({
|
||||
@@ -157,11 +158,11 @@ export function BolumProgramListesi({
|
||||
<TableRow>
|
||||
<TableHead>Üniversite</TableHead>
|
||||
<TableHead className="hidden text-right md:table-cell">
|
||||
Son 5 yıl
|
||||
Son {VERI_YIL_SAYISI} yıl
|
||||
</TableHead>
|
||||
<TableHead className="hidden text-right lg:table-cell">
|
||||
<SiralamaDugmesi
|
||||
alan="puan2025"
|
||||
alan="puanSon"
|
||||
aktifAlan={aktifAlan}
|
||||
yon={yon}
|
||||
onSirala={sirala}
|
||||
@@ -169,7 +170,7 @@ export function BolumProgramListesi({
|
||||
</TableHead>
|
||||
<TableHead className="hidden text-right sm:table-cell">
|
||||
<SiralamaDugmesi
|
||||
alan="kontenjan2025"
|
||||
alan="kontenjanSon"
|
||||
aktifAlan={aktifAlan}
|
||||
yon={yon}
|
||||
onSirala={sirala}
|
||||
@@ -177,7 +178,7 @@ export function BolumProgramListesi({
|
||||
</TableHead>
|
||||
<TableHead className="text-right">
|
||||
<SiralamaDugmesi
|
||||
alan="sira2025"
|
||||
alan="siraSon"
|
||||
aktifAlan={aktifAlan}
|
||||
yon={yon}
|
||||
onSirala={sirala}
|
||||
@@ -243,17 +244,17 @@ export function BolumProgramListesi({
|
||||
<ProgramSiraGecmisi program={p} />
|
||||
</TableCell>
|
||||
<TableCell className="hidden text-right text-xs tabular-nums text-slate-500 lg:table-cell">
|
||||
{p.puan2025 != null
|
||||
? p.puan2025.toFixed(2).replace(".", ",")
|
||||
{p.puanSon != null
|
||||
? p.puanSon.toFixed(2).replace(".", ",")
|
||||
: "—"}
|
||||
</TableCell>
|
||||
<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 className="text-right text-xs tabular-nums text-slate-500">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<ProgramTabanTrendi program={p} />
|
||||
{p.sira2025 != null ? `~${sayi(p.sira2025)}.` : "—"}
|
||||
{p.siraSon != null ? `~${sayi(p.siraSon)}.` : "—"}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell className="pr-3 text-right">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { ReactNode } from "react";
|
||||
import Link from "next/link";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { ONCEKI_YIL, SON_YIL } from "@/lib/veri-yillari";
|
||||
|
||||
/** Programatik tablo sayfaları için satır dilimi boyutu. */
|
||||
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);
|
||||
}
|
||||
|
||||
/** Aralıktaki sıraların hangi yıldan geldiği: hepsi 2025 / hepsi 2024 / karışık. */
|
||||
export function siraYiliEtiketi(yalniz2024: number, siraliSayi: number): string {
|
||||
if (yalniz2024 === 0) return "2025";
|
||||
return yalniz2024 === siraliSayi ? "2024" : "2024–2025";
|
||||
/**
|
||||
* Aralıktaki sıraların hangi yıldan geldiği: hepsi son yıl / hepsi bir önceki
|
||||
* yıl / karışık. `yalnizOnceki`: sırası son yılda olmayıp bir önceki yıldan
|
||||
* 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 { Skeleton } from "@/components/ui/skeleton";
|
||||
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");
|
||||
|
||||
@@ -91,13 +92,13 @@ function fakulteOzeti({
|
||||
if (adlar.length === 0) return null;
|
||||
if (adlar.length === 1) {
|
||||
const siralar = dilim
|
||||
.map((p) => p.sira2025 ?? p.sira2024)
|
||||
.map((p) => p.efektifSira)
|
||||
.filter((n): n is number => n != null);
|
||||
if (siralar.length === 0) return adlar[0];
|
||||
const min = Math.min(...siralar);
|
||||
const max = Math.max(...siralar);
|
||||
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,
|
||||
);
|
||||
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.
|
||||
const birimler = fakulteOzeti(uniSayfaDilimi(uni, sayfa));
|
||||
return sayfaMetadata({
|
||||
title: `${uni.ad} Taban Puanları 2026 (Sayfa ${sayfa})`,
|
||||
title: `${uni.ad} Taban Puanları ${BASLIK_YILI} (Sayfa ${sayfa})`,
|
||||
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ı ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${uni.programSayisi} programın 2025 yerleştirme verileri.`,
|
||||
? `${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 ${SON_YIL} yerleştirme verileri.`,
|
||||
path: `/universite/${slug}/sayfa/${sayfa}`,
|
||||
openGraph: {
|
||||
images: ustSegmentOgGorseli(
|
||||
@@ -130,8 +131,8 @@ export function uniSayfaMetadata(slug: string, sayfa: number): Metadata {
|
||||
});
|
||||
}
|
||||
return sayfaMetadata({
|
||||
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.`,
|
||||
title: `${uni.ad} Taban Puanları ve Başarı Sıralamaları ${BASLIK_YILI}`,
|
||||
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}`,
|
||||
});
|
||||
}
|
||||
@@ -241,7 +242,7 @@ export function UniversiteIcerik({
|
||||
<div className="mt-4 flex items-center gap-4">
|
||||
<UniLogo slug={slug} ad={uni.ad} boy="lg" />
|
||||
<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}`}
|
||||
</h1>
|
||||
</div>
|
||||
@@ -250,7 +251,7 @@ export function UniversiteIcerik({
|
||||
<>
|
||||
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">
|
||||
{uni.ad}
|
||||
{uni.il ? ` (${trBaslikDuzeni(uni.il)})` : ""}, 2025 YKS
|
||||
{uni.il ? ` (${trBaslikDuzeni(uni.il)})` : ""}, {SON_YIL} YKS
|
||||
yerleştirme sonuçlarına göre{" "}
|
||||
<strong>{sayi(uni.programSayisi)} programda</strong> öğrenci aldı
|
||||
{uni.fakulteSayisi > 1
|
||||
@@ -267,10 +268,11 @@ export function UniversiteIcerik({
|
||||
{uni.stats.toplamKontenjan > 0 ? (
|
||||
<> Toplam kontenjan {sayi(uni.stats.toplamKontenjan)}.</>
|
||||
) : 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>
|
||||
2026 taban puanları ve sıralamaları, yerleştirme sonuçları
|
||||
açıklandığında netleşir.
|
||||
{TERCIH_YILI} taban puanları ve sıralamaları, yerleştirme
|
||||
sonuçları açıklandığında netleşir.
|
||||
</em>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import type { Program } from "@/types/yokatlas";
|
||||
import { SON_YIL, VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||
import {
|
||||
ProgramSiraGecmisi,
|
||||
ProgramTabanTrendi,
|
||||
@@ -48,10 +49,10 @@ export function UniversiteProgramTablosu({
|
||||
<TableRow>
|
||||
<TableHead>Program</TableHead>
|
||||
<TableHead className="hidden text-right md:table-cell">
|
||||
Son 5 yıl
|
||||
Son {VERI_YIL_SAYISI} yıl
|
||||
</TableHead>
|
||||
<TableHead className="hidden text-right lg:table-cell">
|
||||
2025 puan
|
||||
{SON_YIL} puan
|
||||
</TableHead>
|
||||
<TableHead className="hidden text-right sm:table-cell">
|
||||
Kontenjan
|
||||
@@ -93,13 +94,13 @@ export function UniversiteProgramTablosu({
|
||||
<ProgramSiraGecmisi program={p} />
|
||||
</TableCell>
|
||||
<TableCell className="hidden text-right text-xs tabular-nums text-slate-500 lg:table-cell">
|
||||
{p.puan2025 != null
|
||||
? p.puan2025.toFixed(2).replace(".", ",")
|
||||
{p.puanSon != null
|
||||
? p.puanSon.toFixed(2).replace(".", ",")
|
||||
: "—"}
|
||||
</TableCell>
|
||||
<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 className="text-right text-xs tabular-nums text-slate-500">
|
||||
|
||||
@@ -24,8 +24,7 @@ type ProgramOzet = Pick<
|
||||
| "il"
|
||||
| "unitur"
|
||||
| "tur"
|
||||
| "sira2025"
|
||||
| "sira2024"
|
||||
| "efektifSira"
|
||||
>;
|
||||
|
||||
const PROFILSIZ_EKLEME_TOAST_ID = "profilsiz-ekleme";
|
||||
|
||||
@@ -22,6 +22,7 @@ import type {
|
||||
} from "@/types/yokatlas";
|
||||
import { DILIM_ETIKET, riskHesapla, type RiskSeviyesi } from "@/lib/risk";
|
||||
import { bolumSayfaSlug, uniSayfaSlug } from "@/lib/slug";
|
||||
import { VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||
import {
|
||||
ProgramSiraGecmisi,
|
||||
ProgramTabanTrendi,
|
||||
@@ -239,7 +240,7 @@ export function ProgramTablosu({
|
||||
<TableHead className="w-8" />
|
||||
<TableHead>Program</TableHead>
|
||||
<TableHead className="hidden text-right md:table-cell">
|
||||
Son 5 yıl
|
||||
Son {VERI_YIL_SAYISI} yıl
|
||||
</TableHead>
|
||||
<TableHead className="hidden text-right sm:table-cell">
|
||||
Kontenjan
|
||||
@@ -357,19 +358,19 @@ function ProgramSatiri({
|
||||
<TableCell
|
||||
className="hidden text-right text-xs tabular-nums text-slate-500 sm:table-cell"
|
||||
title={
|
||||
p.kontenjan2025 != null && p.yerlesen2025 != null
|
||||
? `${p.kontenjan2025} kontenjan, ${p.yerlesen2025} yerleşen`
|
||||
p.kontenjanSon != null && p.yerlesenSon != null
|
||||
? `${p.kontenjanSon} kontenjan, ${p.yerlesenSon} yerleşen`
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{p.kontenjan2025 != null ? (
|
||||
{p.kontenjanSon != null ? (
|
||||
<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">
|
||||
boş kaldı
|
||||
</span>
|
||||
) : null}
|
||||
{p.kontenjan2025.toLocaleString("tr-TR")} kişi
|
||||
{p.kontenjanSon.toLocaleString("tr-TR")} kişi
|
||||
</span>
|
||||
) : (
|
||||
"—"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// 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,
|
||||
// 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
|
||||
// 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
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { bolumSayfaSlug, uniSayfaSlug } from "@/lib/slug";
|
||||
import { VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||
import type { RaporSonuc } from "@/features/rapor/types/rapor";
|
||||
import {
|
||||
DILIM_ETIKET,
|
||||
@@ -47,7 +48,7 @@ const RISK_STIL: Record<RiskSeviyesi, { nokta: string; icon: typeof Rocket }> =
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -161,6 +162,19 @@ export function RaporListesi({
|
||||
return iller.size === 1 ? [...iller][0] : null;
|
||||
}, [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 p = rapor.programlar[t.programId];
|
||||
const analizKilitli = index >= kilitIdx;
|
||||
@@ -168,7 +182,7 @@ export function RaporListesi({
|
||||
(adaySira != null ? riskHesapla(p?.efektifSira, adaySira) : null) ??
|
||||
dilimdenRisk(t.dilim);
|
||||
const stil = RISK_STIL[risk];
|
||||
const seri = raporSiraSerisi(p?.siraGecmisi);
|
||||
const seri = raporSiraSerisi(p?.siraGecmisi, p?.ilkYil);
|
||||
const taban = p?.efektifSira ?? programEtkinSira(seri);
|
||||
const acikMi = acik.has(t.sira);
|
||||
const devlet = p?.unitur === "DEVLET";
|
||||
@@ -366,7 +380,7 @@ export function RaporListesi({
|
||||
<TableHead className="w-8 pl-4 pr-0">#</TableHead>
|
||||
<TableHead>Program</TableHead>
|
||||
<TableHead className="hidden text-right md:table-cell">
|
||||
Son 5 yıl
|
||||
Son {yilSayisi} yıl
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
<span className="hidden sm:inline">Yapay Zeka </span>Risk
|
||||
|
||||
@@ -105,7 +105,7 @@ export function TadimlikSatiri({
|
||||
|
||||
const p = satir.program;
|
||||
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 devlet = p.unitur === "DEVLET";
|
||||
const girisUrl = `/giris?callback=${encodeURIComponent(sonucHref())}`;
|
||||
|
||||
Reference in New Issue
Block a user