feat(ui): Arc bileşenleri, landing bölümleri ve bildirim yığını
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>
This commit is contained in:
129
src/features/pazarlama/landing-verisi.ts
Normal file
129
src/features/pazarlama/landing-verisi.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
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 [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user