feat(katalog+tasarim): şehir adları her yerde link; piksel yüzeylere beyaz ton

- ilSayfaHref: üniversite sayfası il hapı, /universiteler listesi, bölüm /
  sonuç / listem / tadımlık satırlarındaki il metası şehir sayfasına gider.
- Şehir kümeleri tüm (üniversite, il) çiftlerinden: DB'deki her il değerinin
  sayfası var (ODTÜ Ankara + KKTC); dev'de eski globalThis önbelleğine dayanıklı.
- PikselZemin ton="acik", CtaBandi/CtaSiraForm ton="beyaz": /universiteler
  altı ve /meraklisina kapanışı beyaz, diğer bantlar mavi (dönüşümlü).
- ilUlkeHaritasi: il değerinden ülke haritası + şehir konumu.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-24 14:56:11 +03:00
parent c27f24e6a6
commit 259534fa9c
13 changed files with 193 additions and 49 deletions

View File

@@ -180,6 +180,39 @@ export function yurtdisiUniKonum(
return cozum && "anahtar" in cozum ? sehirKonumu(cozum.anahtar) : null;
}
/**
* Yalnızca il değerinden ülke haritası (şehir sayfası). "KIBRIS" genel
* değeri ülke seviyesinde kalır; "ÜSKÜP-MAKEDONYA" gibi ülke ekli değerler
* ilk parçasıyla çözülür. Türkiye ili ya da bilinmeyen değer → null.
*/
export function ilUlkeHaritasi(il: string): {
ulke: UlkeKodu;
ulkeAd: string;
harita: UlkeHarita;
sehir: YurtdisiKonum | null;
} | null {
const anahtar =
il in SEHIRLER
? (il as SehirAnahtari)
: (() => {
const ilk = il.split("-")[0].trim();
return ilk in SEHIRLER ? (ilk as SehirAnahtari) : null;
})();
const ulke: UlkeKodu | null = anahtar
? SEHIRLER[anahtar].ulke
: il === "KIBRIS"
? "kktc"
: null;
if (!ulke) return null;
const harita = ULKE_HARITALARI[ulke];
return {
ulke,
ulkeAd: harita.ad,
harita,
sehir: anahtar ? sehirKonumu(anahtar) : null,
};
}
/** Yalnızca il değerinden (kilitli satırlar) yurtdışı şehir konumu. */
export function yurtdisiIlKonum(il: string | null): YurtdisiKonum | null {
return il && il in SEHIRLER ? sehirKonumu(il as SehirAnahtari) : null;

View File

@@ -64,6 +64,8 @@ const globalKatalog = globalThis as unknown as {
uniOzetler?: UniOzet[];
iller?: IlOzet[];
ilMap?: Map<string, IlOzet>;
/** slug → (il → o ildeki program sayısı); getAllUniversiteler doldurur */
uniIlAdetleri?: Map<string, Map<string, number>>;
};
function bolumMapGetir(): Map<string, BolumKayit> {
@@ -335,6 +337,7 @@ export function getAllUniversiteler(): UniOzet[] {
}
sayac.set(row.il, (sayac.get(row.il) ?? 0) + row.adet);
}
globalKatalog.uniIlAdetleri = ilAdetleri;
for (const [slug, sayac] of ilAdetleri) {
let enIyi: string | null = null;
let enCok = 0;
@@ -378,7 +381,9 @@ export type IlOzet = {
slug: string;
/** Görünen ad: İstanbul, Bakü-Azerbaycan */
ad: string;
/** Bu ilde yer alan üniversiteler (UniOzet.il esas; ad sırasıyla) */
/** DB biçiminde ham il ("İSTANBUL"); harita anahtarı */
il: string;
/** Bu ilde programı olan üniversiteler (ad sırasıyla) */
uniler: UniOzet[];
programSayisi: number;
};
@@ -395,25 +400,42 @@ function ilGorunenAd(il: string): string {
/**
* Üniversiteleri iline göre kümeler; sıralama üniversite sayısına göre
* azalan, eşitlikte ad. Her üniversite tek ilde sayılır (UniOzet.il: en çok
* programının bulunduğu il). İli olmayan (NULL/boş) üniversite hiçbir ile
* girmez. KKTC ve yurt dışı şehirler de birer ildir (LEFKOŞA, BAKÜ-…).
* azalan, eşitlikte ad. Bir üniversite **programı olan her ilde** sayılır
* (ODTÜ hem Ankara'da hem KKTC'de; öğrenci "KKTC'de okuyabileceğim
* üniversiteler" diye bakar) — böylece DB'deki her il değerinin sayfası
* vardır ve program satırlarındaki her şehir adı güvenle link olur
* (`ilSayfaHref`). `programSayisi` o ildeki program sayısıdır.
*/
export function getAllIller(): IlOzet[] {
if (!globalKatalog.iller) {
// Dev'de globalThis önbelleği HMR'ı aşar: uniOzetler eski modül koduyla
// kurulduysa il sayacı yoktur; özetler yeniden kurulur.
if (!globalKatalog.uniIlAdetleri) globalKatalog.uniOzetler = undefined;
const uniler = getAllUniversiteler();
const ilAdetleri = globalKatalog.uniIlAdetleri!;
const bySlug = new Map<string, IlOzet>();
for (const u of getAllUniversiteler()) {
const il = u.il?.trim();
if (!il) continue;
const slug = turkishSlugify(il);
if (!slug) continue;
let kayit = bySlug.get(slug);
if (!kayit) {
kayit = { slug, ad: ilGorunenAd(il), uniler: [], programSayisi: 0 };
bySlug.set(slug, kayit);
for (const u of uniler) {
const sayac = ilAdetleri.get(u.slug);
if (!sayac) continue;
for (const [ham, adet] of sayac) {
const il = ham.trim();
if (!il) continue;
const slug = turkishSlugify(il);
if (!slug) continue;
let kayit = bySlug.get(slug);
if (!kayit) {
kayit = {
slug,
ad: ilGorunenAd(il),
il,
uniler: [],
programSayisi: 0,
};
bySlug.set(slug, kayit);
}
kayit.uniler.push(u);
kayit.programSayisi += adet;
}
kayit.uniler.push(u);
kayit.programSayisi += u.programSayisi;
}
globalKatalog.ilMap = bySlug;
globalKatalog.iller = [...bySlug.values()].sort(

View File

@@ -52,6 +52,14 @@ export function bolumSayfaSlug(isim: string): string {
return turkishSlugify(bolumBazAdi(isim));
}
/**
* /universiteler/[il] şehir sayfası adresi. Şehir sayfaları DB'deki her il
* değeri için üretildiğinden (katalog.ts getAllIller) adres her zaman vardır.
*/
export function ilSayfaHref(il: string): string {
return `/universiteler/${turkishSlugify(il)}`;
}
/** ALL-CAPS veritabanı adlarını tr-TR başlık düzenine çevirir (İSTANBUL → İstanbul). */
export function trBaslikDuzeni(s: string): string {
return s