refactor: optimize AramaFunnelKarti styles and enhance city data structure
All checks were successful
Deploy / deploy (push) Successful in 7m30s

Removed unnecessary elements and optimized styles in the AramaFunnelKarti component for improved layout. Updated the city data structure in harita-ulkeler.ts to include coordinates, enhancing geographical data handling. Additionally, refactored risk color definitions in risk.ts for better clarity and maintainability.
This commit is contained in:
bilalgursen
2026-08-09 03:55:56 +03:00
parent f752d27101
commit 696dc69763
9 changed files with 378 additions and 86 deletions

View File

@@ -3,6 +3,11 @@
import type { RaporSonuc } from "@/features/rapor/types/rapor";
import type { HaritaPin } from "@/features/liste/components/tercih-haritasi";
import { ilKonum, uniAdiNormalize, uniKonum } from "@/lib/harita";
import {
yurtdisiIlKonum,
yurtdisiUniKonum,
type UlkeKodu,
} from "@/lib/harita-ulkeler";
import { dilimdenRisk, riskHesapla, type RiskSeviyesi } from "@/lib/risk";
const RISK_AGIRLIK: Record<RiskSeviyesi, number> = {
@@ -11,18 +16,71 @@ const RISK_AGIRLIK: Record<RiskSeviyesi, number> = {
riskli: 2,
};
/** Türkiye dışındaki tercihlerin ülke mini haritalarında şehir başına pini. */
export type YurtdisiPin = {
id: string;
ulke: UlkeKodu;
ulkeAd: string;
sehir: string;
x: number;
y: number;
/** Bu şehirdeki tercihlerin liste sıraları (1-24) */
tercihSiralari: number[];
/** tercihSiralari ile hizalı — pin rengi çoğunluk riskiyle belirlenir */
riskler: RiskSeviyesi[];
};
/**
* Yurtdışı tercihini şehir pinine ekler; şehir çözülemezse false (harita
* dışı). Kilitli satırlarda universite null verilir — pin şehir bazlı
* olduğundan üniversite kimliği zaten görünmez.
*/
export function yurtdisiPinKaydet(
gruplar: Map<string, YurtdisiPin>,
universite: string | null,
il: string | null,
sira: number,
risk: RiskSeviyesi,
): boolean {
const konum = universite
? yurtdisiUniKonum(universite, il)
: yurtdisiIlKonum(il);
if (!konum) return false;
const key = `${konum.ulke}|${konum.sehir}`;
const mevcut = gruplar.get(key);
if (mevcut) {
mevcut.tercihSiralari.push(sira);
mevcut.riskler.push(risk);
} else {
gruplar.set(key, {
id: key,
...konum,
tercihSiralari: [sira],
riskler: [risk],
});
}
return true;
}
/**
* Rapordan harita pinleri türetir. Açık satırlar üniversite bazında gruplanır
* (kampüs konumunda, adıyla); kilitli satırlar il başına tek adsız "?" pinine
* toplanır — maskeli veride üniversite adı zaten client'a inmediği için
* harita paywall'ı delemez. Haritada karşılığı olmayan tercihler sayılır.
* harita paywall'ı delemez. Türkiye dışındaki tercihler ülke mini haritaları
* için şehir bazlı yurtdışı pinlerine ayrılır; orada da karşılığı olmayanlar
* sayılır.
*/
export function pinleriTuret(
rapor: RaporSonuc,
adaySira: number,
kilitliBaslangic: number,
): { pinler: HaritaPin[]; haritaDisi: number } {
): {
pinler: HaritaPin[];
yurtdisiPinler: YurtdisiPin[];
haritaDisi: number;
} {
const gruplar = new Map<string, HaritaPin>();
const yurtdisi = new Map<string, YurtdisiPin>();
let haritaDisi = 0;
rapor.tercihler.forEach((t, i) => {
@@ -34,13 +92,16 @@ export function pinleriTuret(
if (kilitli) {
const konum = ilKonum(p.il);
if (!konum) {
haritaDisi += 1;
if (!yurtdisiPinKaydet(yurtdisi, null, p.il, t.sira, risk))
haritaDisi += 1;
return;
}
const key = `kilit|${p.il}`;
const mevcut = gruplar.get(key);
if (mevcut) {
mevcut.tercihSiralari.push(t.sira);
// Kilitli satırda bölüm adı da gösterilmez — şeritte "Kilitli tercih"
mevcut.bolumler.push("");
if (RISK_AGIRLIK[risk] > RISK_AGIRLIK[mevcut.risk]) mevcut.risk = risk;
} else {
// Açık pinlerle üst üste binmesin diye il merkezinin hafif altına
@@ -51,6 +112,7 @@ export function pinleriTuret(
x: konum.x,
y: konum.y + 9,
tercihSiralari: [t.sira],
bolumler: [""],
risk,
kilitli: true,
});
@@ -60,13 +122,15 @@ export function pinleriTuret(
const konum = uniKonum(p.universite, p.il);
if (!konum) {
haritaDisi += 1;
if (!yurtdisiPinKaydet(yurtdisi, p.universite, p.il, t.sira, risk))
haritaDisi += 1;
return;
}
const key = `${uniAdiNormalize(p.universite)}|${p.il ?? ""}`;
const mevcut = gruplar.get(key);
if (mevcut) {
mevcut.tercihSiralari.push(t.sira);
mevcut.bolumler.push(p.isim);
if (RISK_AGIRLIK[risk] > RISK_AGIRLIK[mevcut.risk]) mevcut.risk = risk;
} else {
gruplar.set(key, {
@@ -76,10 +140,15 @@ export function pinleriTuret(
x: konum.x,
y: konum.y,
tercihSiralari: [t.sira],
bolumler: [p.isim],
risk,
});
}
});
return { pinler: [...gruplar.values()], haritaDisi };
return {
pinler: [...gruplar.values()],
yurtdisiPinler: [...yurtdisi.values()],
haritaDisi,
};
}

View File

@@ -6,6 +6,7 @@ import {
type UlkeKodu,
type UlkeHarita,
} from "./harita-ulkeler-verisi";
import type { LatLng } from "./uni-koordinatlar";
export {
ULKE_HARITALARI,
@@ -25,23 +26,41 @@ export const ULKE_ISO: Record<UlkeKodu, string> = {
arnavutluk: "AL",
};
type Sehir = { ulke: UlkeKodu; ad: string };
type Sehir = { ulke: UlkeKodu; ad: string; koord: LatLng };
/** DB'deki il değerlerinden ve düzeltme tablosundan şehir anahtarları. */
const SEHIRLER: Record<string, Sehir> = {
GİRNE: { ulke: "kktc", ad: "Girne" },
LEFKOŞA: { ulke: "kktc", ad: "Lefkoşa" },
GAZİMAĞUSA: { ulke: "kktc", ad: "Gazimağusa" },
GÜZELYURT: { ulke: "kktc", ad: "Güzelyurt" },
LEFKE: { ulke: "kktc", ad: "Lefke" },
"BAKÜ-AZERBAYCAN": { ulke: "azerbaycan", ad: "Bakü" },
"BİŞKEK-KIRGIZİSTAN": { ulke: "kirgizistan", ad: "Bişkek" },
"TÜRKİSTAN-KAZAKİSTAN": { ulke: "kazakistan", ad: "Türkistan" },
"SARAYBOSNA - BOSNA - HERSEK": { ulke: "bosna-hersek", ad: "Saraybosna" },
ÜSKÜP: { ulke: "makedonya", ad: "Üsküp" },
TİRAN: { ulke: "arnavutluk", ad: "Tiran" },
GİRNE: { ulke: "kktc", ad: "Girne", koord: [35.341, 33.319] },
LEFKOŞA: { ulke: "kktc", ad: "Lefkoşa", koord: [35.19, 33.365] },
GAZİMAĞUSA: { ulke: "kktc", ad: "Gazimağusa", koord: [35.125, 33.941] },
GÜZELYURT: { ulke: "kktc", ad: "Güzelyurt", koord: [35.198, 32.994] },
LEFKE: { ulke: "kktc", ad: "Lefke", koord: [35.111, 32.849] },
"BAKÜ-AZERBAYCAN": {
ulke: "azerbaycan",
ad: "Bakü",
koord: [40.409, 49.867],
},
"BİŞKEK-KIRGIZİSTAN": {
ulke: "kirgizistan",
ad: "Bişkek",
koord: [42.875, 74.603],
},
"TÜRKİSTAN-KAZAKİSTAN": {
ulke: "kazakistan",
ad: "Türkistan",
koord: [43.297, 68.252],
},
"SARAYBOSNA - BOSNA - HERSEK": {
ulke: "bosna-hersek",
ad: "Saraybosna",
koord: [43.856, 18.413],
},
ÜSKÜP: { ulke: "makedonya", ad: "Üsküp", koord: [41.997, 21.428] },
TİRAN: { ulke: "arnavutluk", ad: "Tiran", koord: [41.328, 19.818] },
};
type SehirAnahtari = keyof typeof SEHIRLER;
/**
* Kaynak verideki gürültü için üniversite adına göre düzeltme:
* - Uluslararası Balkan Üniversitesi Üsküp'tedir; YÖK Atlas satırlarının
@@ -51,7 +70,7 @@ const SEHIRLER: Record<string, Sehir> = {
* null → Türkiye haritasında kalır.
* Anahtar: uniAdiNormalize edilmiş adın tr-TR büyük harf hali.
*/
const UNI_DUZELTME: Record<string, keyof typeof SEHIRLER | null> = {
const UNI_DUZELTME: Record<string, SehirAnahtari | null> = {
"ULUSLARARASI BALKAN ÜNİVERSİTESİ": "ÜSKÜP",
"TİRAN NEW YORK ÜNİVERSİTESİ": "TİRAN",
"KIBRIS AYDIN ÜNİVERSİTESİ": "GİRNE", // il alanı yalnızca "KIBRIS"
@@ -62,6 +81,24 @@ const UNI_DUZELTME: Record<string, keyof typeof SEHIRLER | null> = {
/** il değeri Türkiye dışını mı gösteriyor? ("KIBRIS" genel değeri dahil) */
const YURTDISI_IL = new Set([...Object.keys(SEHIRLER), "KIBRIS"]);
/** Üniversite adı + il'den şehir anahtarı; şehri bilinmeyen KKTC'de ülke. */
type Cozum = { anahtar: SehirAnahtari } | { ulke: UlkeKodu } | null;
function coz(universite: string, il: string | null): Cozum {
const ad = universite
.replace(/\s*\([^)]*\)\s*$/, "")
.trim()
.toLocaleUpperCase("tr-TR");
if (ad in UNI_DUZELTME) {
const anahtar = UNI_DUZELTME[ad];
return anahtar === null ? null : { anahtar };
}
if (!il || !YURTDISI_IL.has(il)) return null;
if (il in SEHIRLER) return { anahtar: il as SehirAnahtari };
return { ulke: "kktc" }; // il "KIBRIS": ülke bilinir, şehir bilinmez
}
export type UlkeKonum = {
ulke: UlkeKodu;
ulkeAd: string;
@@ -71,45 +108,59 @@ export type UlkeKonum = {
sehir: string | null;
};
function sehirdenKonum(anahtar: keyof typeof SEHIRLER): UlkeKonum {
const sehir = SEHIRLER[anahtar];
const harita = ULKE_HARITALARI[sehir.ulke];
return {
ulke: sehir.ulke,
ulkeAd: harita.ad,
iso: ULKE_ISO[sehir.ulke],
harita,
sehir: sehir.ad,
};
}
/**
* Üniversite Türkiye dışındaysa ülke haritası + şehir konumu döner;
* Üniversite Türkiye dışındaysa ülke haritası + şehir adı döner;
* Türkiye'deyse (veya çözülemiyorsa) null — Türkiye haritası kullanılır.
*/
export function uniUlkeKonum(
universite: string,
il: string | null,
): UlkeKonum | null {
const ad = universite
.replace(/\s*\([^)]*\)\s*$/, "")
.trim()
.toLocaleUpperCase("tr-TR");
if (ad in UNI_DUZELTME) {
const anahtar = UNI_DUZELTME[ad];
return anahtar === null ? null : sehirdenKonum(anahtar);
}
if (!il || !YURTDISI_IL.has(il)) return null;
if (il in SEHIRLER) return sehirdenKonum(il as keyof typeof SEHIRLER);
// il "KIBRIS": ülke bilinir, şehir bilinmez — haritayı işaretsiz göster
const harita = ULKE_HARITALARI.kktc;
const cozum = coz(universite, il);
if (!cozum) return null;
const ulke = "anahtar" in cozum ? SEHIRLER[cozum.anahtar].ulke : cozum.ulke;
const harita = ULKE_HARITALARI[ulke];
return {
ulke: "kktc",
ulke,
ulkeAd: harita.ad,
iso: ULKE_ISO.kktc,
iso: ULKE_ISO[ulke],
harita,
sehir: null,
sehir: "anahtar" in cozum ? SEHIRLER[cozum.anahtar].ad : null,
};
}
/** Yurtdışı şehir merkezinin kendi ülke haritasındaki viewBox konumu. */
export type YurtdisiKonum = {
ulke: UlkeKodu;
ulkeAd: string;
sehir: string;
x: number;
y: number;
};
function sehirKonumu(anahtar: SehirAnahtari): YurtdisiKonum {
const sehir = SEHIRLER[anahtar];
const { ad, proje } = ULKE_HARITALARI[sehir.ulke];
const [lat, lng] = sehir.koord;
return {
ulke: sehir.ulke,
ulkeAd: ad,
sehir: sehir.ad,
x: proje.ax * lng + proje.bx,
y: proje.ay * lat + proje.by,
};
}
/** Yurtdışı üniversitenin şehir konumu; şehir çözülemiyorsa null. */
export function yurtdisiUniKonum(
universite: string,
il: string | null,
): YurtdisiKonum | null {
const cozum = coz(universite, il);
return cozum && "anahtar" in cozum ? sehirKonumu(cozum.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

@@ -12,6 +12,13 @@ export const RISK_ETIKET: Record<RiskSeviyesi, string> = {
riskli: "Riskli",
};
/** Harita pinlerinin SVG dolgu renkleri — risk renk dilinin oklch karşılığı. */
export const RISK_PIN_RENK: Record<RiskSeviyesi, string> = {
guvenli: "oklch(0.696 0.17 162.5)", // emerald-500
"az-riskli": "oklch(0.769 0.188 70.1)", // amber-500
riskli: "oklch(0.637 0.237 25.3)", // red-500
};
/**
* Deterministik risk: programın efektif taban sıralaması (COALESCE 2025→2024)
* adayın sıralamasıyla kıyaslanır. Eşikler db.ts'teki dilim aralıklarıyla