Sonner/vaul yerine kendi bildirim yığını ve alt sayfa; çip grubu, kart destesi, kaydırma eylemi, yazı dönüşümü, bölme tutamacı; landing'e program duvarı, süzülen haplar ve veri akışı; yaylar tek dosyada (lib/hareket.ts). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
130 lines
4.2 KiB
TypeScript
130 lines
4.2 KiB
TypeScript
import "server-only";
|
||
|
||
// Ana sayfadaki iki etkileşimli parçanın verisi (sunucuda, build/istek anında):
|
||
// - Kart destesi demosu (DESIGN.md §4.27): örnek bir sıralamaya göre GERÇEK
|
||
// programlar. Uydurma taban sıralaması gösterilmez; veri okunamazsa bölüm
|
||
// hiç çizilmez.
|
||
// - Süzülen haplar (§4.30): yalnız katalogda sayfası olan bölüm ve şehirler.
|
||
|
||
import { dilimAra } from "@/lib/db";
|
||
import { bolumSlugFromIsim, getIlBySlug } from "@/lib/katalog";
|
||
import { ozelKontenjanSatiriMi } from "@/lib/rapor-havuzu";
|
||
import { riskHesapla, type RiskSeviyesi } from "@/lib/risk";
|
||
import { bolumBazAdi, ilSayfaHref, trBaslikDuzeni, turkishSlugify } from "@/lib/slug";
|
||
import type { DilimKey } from "@/types/yokatlas";
|
||
|
||
/** Demonun örnek öğrencisi: Sayısal, 85.000. sıra (§10.1'deki örnekle aynı). */
|
||
export const ORNEK_SIRA = 85_000;
|
||
const ORNEK_TUR = "say" as const;
|
||
|
||
export interface OrnekProgram {
|
||
id: string;
|
||
isim: string;
|
||
universite: string;
|
||
il: string | null;
|
||
tabanSira: number;
|
||
risk: RiskSeviyesi;
|
||
}
|
||
|
||
/**
|
||
* Her dilimden birkaç program: sınıra yapışık olanlar yerine dilimin içine
|
||
* yayılmış (riskli / az riskli / güvenli farkı kartta görünsün), her üniversite
|
||
* ve her bölüm adı en çok bir kez.
|
||
*/
|
||
export function ornekProgramlar(): OrnekProgram[] {
|
||
try {
|
||
const gorulenUni = new Set<string>();
|
||
const gorulenBolum = new Set<string>();
|
||
const sec = (dilim: DilimKey, adet: number, adim: number): OrnekProgram[] => {
|
||
const { programlar } = dilimAra(ORNEK_SIRA, ORNEK_TUR, dilim, {
|
||
uniturGrubu: "devlet",
|
||
barajDisiHaric: true,
|
||
limit: 600,
|
||
});
|
||
const secilen: OrnekProgram[] = [];
|
||
for (let i = adim; i < programlar.length && secilen.length < adet; i += adim) {
|
||
const p = programlar[i];
|
||
const bolum = bolumBazAdi(p.isim);
|
||
const risk = riskHesapla(p.efektifSira, ORNEK_SIRA);
|
||
if (
|
||
p.efektifSira == null ||
|
||
!risk ||
|
||
ozelKontenjanSatiriMi(p.isim) ||
|
||
/\(/.test(p.isim) ||
|
||
gorulenUni.has(p.universite) ||
|
||
gorulenBolum.has(bolum)
|
||
) {
|
||
// Elenen satırın bir sonrakine bak; adım bozulmasın diye geri al
|
||
i -= adim - 1;
|
||
continue;
|
||
}
|
||
gorulenUni.add(p.universite);
|
||
gorulenBolum.add(bolum);
|
||
secilen.push({
|
||
id: p.id,
|
||
isim: p.isim,
|
||
universite: trBaslikDuzeni(p.universite),
|
||
il: p.il ? trBaslikDuzeni(p.il) : null,
|
||
tabanSira: p.efektifSira,
|
||
risk,
|
||
});
|
||
}
|
||
return secilen;
|
||
};
|
||
const hayal = sec("hayal", 2, 60);
|
||
const dengeli = sec("dengeli", 3, 45);
|
||
const guvenli = sec("garanti", 2, 90);
|
||
// Sıra karışık: aynı risk art arda gelmesin
|
||
return [dengeli[0], hayal[0], guvenli[0], dengeli[1], hayal[1], dengeli[2], guvenli[1]].filter(
|
||
(p): p is OrnekProgram => Boolean(p),
|
||
);
|
||
} catch {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
export interface KatalogHapi {
|
||
etiket: string;
|
||
href: string;
|
||
tur: "bolum" | "sehir";
|
||
}
|
||
|
||
const HAP_BOLUMLERI = [
|
||
"Tıp",
|
||
"Hukuk",
|
||
"Bilgisayar Mühendisliği",
|
||
"Psikoloji",
|
||
"Diş Hekimliği",
|
||
"Hemşirelik",
|
||
"Mimarlık",
|
||
"Yazılım Mühendisliği",
|
||
"Eczacılık",
|
||
"İşletme",
|
||
];
|
||
const HAP_SEHIRLERI = ["İstanbul", "Ankara", "İzmir", "Eskişehir", "Bursa", "Antalya"];
|
||
|
||
/** Bölüm ve şehir hapları; katalogda karşılığı olmayan ad sessizce düşer. */
|
||
export function katalogHaplari(): KatalogHapi[] {
|
||
try {
|
||
const bolumler: KatalogHapi[] = HAP_BOLUMLERI.flatMap((ad) => {
|
||
const slug = bolumSlugFromIsim(ad);
|
||
return slug ? [{ etiket: ad, href: `/bolum/${slug}`, tur: "bolum" as const }] : [];
|
||
});
|
||
const sehirler: KatalogHapi[] = HAP_SEHIRLERI.flatMap((ad) =>
|
||
getIlBySlug(turkishSlugify(ad))
|
||
? [{ etiket: ad, href: ilSayfaHref(ad), tur: "sehir" as const }]
|
||
: [],
|
||
);
|
||
// Bölüm ve şehir karışık dizilsin: her üç bölümden sonra bir şehir
|
||
const sonuc: KatalogHapi[] = [];
|
||
let s = 0;
|
||
bolumler.forEach((b, i) => {
|
||
sonuc.push(b);
|
||
if (i % 2 === 1 && s < sehirler.length) sonuc.push(sehirler[s++]);
|
||
});
|
||
return [...sonuc, ...sehirler.slice(s)];
|
||
} catch {
|
||
return [];
|
||
}
|
||
}
|