Files
kolaytercih/src/features/rapor/tercih-farki.ts
bilalgursen f7ed8f2f0b fix: denetim bulguları — çip adı sahte 'tercihlerin değişmiş', çekmece kartı, senkron riski
- tercihFarki eski 'İş garantisi' parametresini yeni ada eşleyerek kıyaslar;
  ad değişikliği 3 kredilik güncelleme çağrısı üretmesin
- çekmece kartı yalnız profil yokken sıra ister, butonu sıra kapısını açar
  (profilsiz ziyaretçi /listem giriş duvarına gönderilmiyordu → çıkmaz)
- sunucuylaBirlestir risksiz gelen kayıtların riskini doldurur
- /paket'teki fiyat kıyasına kaynak satırı

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 16:06:15 +03:00

118 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Kayıtlı listenin üretildiği tercihler (rapor params) ile kullanıcının şu
// anki tercih profilini karşılaştırır. /listem başlığındaki "Tercihlerin
// değişmiş" butonu ve açtığı güncelleme modalı bu farktan doğar.
//
// İzomorfik: yalnızca tip ve etiket sabitlerine bağlı — hem client bileşeni
// hem sunucu tarafı tüketebilir.
//
// Eski akışla üretilmiş raporlarda sihirbaz alanları (kategoriler/iller/
// universiteTipi/oncelikler) hiç yok. Olmayan alan "değişmiş" sayılmaz:
// kıyaslanacak bir değer yokken kullanıcıyı 3 kredilik güncellemeye
// çağırmak dürüst olmaz. Sıralama ve puan türü her raporda dolu olduğundan
// onlar her zaman kıyaslanır.
import {
PUAN_TURU_ETIKET,
UNIVERSITE_TIPI_ETIKET,
oncelikNormalize,
type TercihProfili,
} from "@/features/sihirbaz/sihirbaz-sabitler";
import type { RaporParams } from "./types/rapor";
export type FarkAlani =
| "sira"
| "tur"
| "kategoriler"
| "iller"
| "universiteTipi"
| "oncelikler";
export const FARK_ETIKET: Record<FarkAlani, string> = {
sira: "başarı sıralaman",
tur: "puan türün",
kategoriler: "ilgi alanların",
iller: "şehir tercihlerin",
universiteTipi: "devlet/vakıf tercihin",
oncelikler: "önceliklerin",
};
// Sıra bağımsız küme kıyası: sihirbaz seçimleri tekrarsız olarak doğrulanır
// (bkz. sihirbazDogrula), aynı seçimlerin farklı sırayla kaydedilmesi
// "değişiklik" değildir.
function kumeFarkli(
eski: readonly string[] | undefined,
yeni: readonly string[],
): boolean {
if (!eski) return false;
if (eski.length !== yeni.length) return true;
const kume = new Set(eski);
return yeni.some((x) => !kume.has(x));
}
function liste(degerler: readonly string[] | undefined): string {
return degerler && degerler.length > 0 ? degerler.join(", ") : "—";
}
export function tercihFarki(
params: RaporParams,
profil: TercihProfili,
): FarkAlani[] {
const farklar: FarkAlani[] = [];
const s = profil.secimler;
if (Math.round(params.sira) !== Math.round(profil.sira)) farklar.push("sira");
if (params.tur !== profil.tur) farklar.push("tur");
if (kumeFarkli(params.kategoriler, s.kategoriler)) farklar.push("kategoriler");
if (kumeFarkli(params.iller, s.iller)) farklar.push("iller");
if (
params.universiteTipi !== undefined &&
params.universiteTipi !== s.universiteTipi
) {
farklar.push("universiteTipi");
}
if (kumeFarkli(params.oncelikler?.map(oncelikNormalize), s.oncelikler)) {
farklar.push("oncelikler");
}
return farklar;
}
export type FarkSatiri = { alan: FarkAlani; etiket: string; eski: string; yeni: string };
/** Değişen her alanın eski → yeni değeri; uyarıda tek tek gösterilir. */
export function farkSatirlari(
params: RaporParams,
profil: TercihProfili,
farklar: FarkAlani[],
): FarkSatiri[] {
const s = profil.secimler;
const sayi = (n: number) => Math.round(n).toLocaleString("tr-TR");
return farklar.map((alan) => {
const ortak = { alan, etiket: FARK_ETIKET[alan] };
switch (alan) {
case "sira":
return { ...ortak, eski: `${sayi(params.sira)}.`, yeni: `${sayi(profil.sira)}.` };
case "tur":
return {
...ortak,
eski: PUAN_TURU_ETIKET[params.tur] ?? params.tur,
yeni: PUAN_TURU_ETIKET[profil.tur] ?? profil.tur,
};
case "kategoriler":
return { ...ortak, eski: liste(params.kategoriler), yeni: liste(s.kategoriler) };
case "iller":
return { ...ortak, eski: liste(params.iller), yeni: liste(s.iller) };
case "universiteTipi":
return {
...ortak,
eski: UNIVERSITE_TIPI_ETIKET[params.universiteTipi ?? "farketmez"],
yeni: UNIVERSITE_TIPI_ETIKET[s.universiteTipi],
};
case "oncelikler":
return { ...ortak, eski: liste(params.oncelikler?.map(oncelikNormalize)),
yeni: liste(s.oncelikler), };
}
});
}