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>
This commit is contained in:
@@ -52,6 +52,9 @@ export default function PaketPage({
|
|||||||
Tercih danışmanları bu dönemde 1.000–3.000 TL alıyor. KolayTercih
|
Tercih danışmanları bu dönemde 1.000–3.000 TL alıyor. KolayTercih
|
||||||
aynı işi gerçek YÖK Atlas verisiyle, dakikalar içinde yapar.
|
aynı işi gerçek YÖK Atlas verisiyle, dakikalar içinde yapar.
|
||||||
</p>
|
</p>
|
||||||
|
<p className="mt-2 text-xs text-slate-500">
|
||||||
|
Danışman fiyat aralığı: Armut ortalama fiyatı, Eylül 2026.
|
||||||
|
</p>
|
||||||
</Parallax>
|
</Parallax>
|
||||||
|
|
||||||
<Suspense fallback={<PaketSatinAlSkeleton />}>
|
<Suspense fallback={<PaketSatinAlSkeleton />}>
|
||||||
|
|||||||
@@ -46,7 +46,11 @@ import { RISK_ETIKET, type RiskSeviyesi } from "@/lib/risk";
|
|||||||
import { bolumBazAdi, turkishSlugify } from "@/lib/slug";
|
import { bolumBazAdi, turkishSlugify } from "@/lib/slug";
|
||||||
import { olay } from "@/lib/analitik";
|
import { olay } from "@/lib/analitik";
|
||||||
import { CihazCifti, IsletimSistemiLogosu } from "./cihaz-ikonlari";
|
import { CihazCifti, IsletimSistemiLogosu } from "./cihaz-ikonlari";
|
||||||
import { sonucaGitIste } from "../hooks/use-tercih-profili";
|
import {
|
||||||
|
profilToplamaAc,
|
||||||
|
sonucaGitIste,
|
||||||
|
useTercihProfili,
|
||||||
|
} from "../hooks/use-tercih-profili";
|
||||||
import { useOturumDurumu } from "../liste-oturum-sinyali";
|
import { useOturumDurumu } from "../liste-oturum-sinyali";
|
||||||
import { useGirisUrl } from "../liste-giris-url";
|
import { useGirisUrl } from "../liste-giris-url";
|
||||||
import {
|
import {
|
||||||
@@ -92,8 +96,10 @@ export function ListeCekmecesi() {
|
|||||||
|
|
||||||
const doluluk = Math.round((liste.length / MANUEL_LISTE_MAX) * 100);
|
const doluluk = Math.round((liste.length / MANUEL_LISTE_MAX) * 100);
|
||||||
// Profilsiz + ile eklenenlerin riski henüz hesaplanmamıştır; alt kart
|
// Profilsiz + ile eklenenlerin riski henüz hesaplanmamıştır; alt kart
|
||||||
// olmayan bir analizi "hazır" diye duyurmasın.
|
// olmayan bir analizi "hazır" diye duyurmasın. Profil varken null risk,
|
||||||
const riskEksik = liste.some((t) => t.risk == null);
|
// programın taban sırası olmadığındandır — sıra istemek yanlış olur.
|
||||||
|
const profil = useTercihProfili();
|
||||||
|
const riskEksik = !profil && liste.some((t) => t.risk == null);
|
||||||
|
|
||||||
function programaEkleGit() {
|
function programaEkleGit() {
|
||||||
cekmeceKapat();
|
cekmeceKapat();
|
||||||
@@ -278,16 +284,30 @@ export function ListeCekmecesi() {
|
|||||||
: "Her seçimin yerleşme riski hazır — son kararı sen ver."}
|
: "Her seçimin yerleşme riski hazır — son kararı sen ver."}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
{riskEksik ? (
|
||||||
asChild
|
<Button
|
||||||
size="lg"
|
size="lg"
|
||||||
className="shrink-0 cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
|
className="shrink-0 cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
|
||||||
>
|
onClick={() => {
|
||||||
<Link href="/listem" onClick={cekmeceKapat}>
|
cekmeceKapat();
|
||||||
Listemde aç
|
profilToplamaAc(null, true, "arti_toast");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sıralamamı gir
|
||||||
<ArrowRight data-icon="inline-end" />
|
<ArrowRight data-icon="inline-end" />
|
||||||
</Link>
|
</Button>
|
||||||
</Button>
|
) : (
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
size="lg"
|
||||||
|
className="shrink-0 cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
|
||||||
|
>
|
||||||
|
<Link href="/listem" onClick={cekmeceKapat}>
|
||||||
|
Listemde aç
|
||||||
|
<ArrowRight data-icon="inline-end" />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</DrawerFooter>
|
</DrawerFooter>
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
|
|||||||
@@ -152,6 +152,9 @@ export function sunucuylaBirlestir(sunucuItems: ManuelTercih[]): ManuelTercih[]
|
|||||||
liste = [...liste, ...ekler].slice(0, MANUEL_LISTE_MAX);
|
liste = [...liste, ...ekler].slice(0, MANUEL_LISTE_MAX);
|
||||||
kaydet();
|
kaydet();
|
||||||
yayinla();
|
yayinla();
|
||||||
|
// Başka cihazda profilsiz eklenmiş kayıtlar risksiz gelir; profil lokalde
|
||||||
|
// zaten varsa PROFIL_DEGISTI_EVENT yayılmaz, riski burada doldur.
|
||||||
|
if (liste.some((t) => t.risk == null)) riskleriYenile();
|
||||||
return liste;
|
return liste;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
import {
|
import {
|
||||||
PUAN_TURU_ETIKET,
|
PUAN_TURU_ETIKET,
|
||||||
UNIVERSITE_TIPI_ETIKET,
|
UNIVERSITE_TIPI_ETIKET,
|
||||||
|
oncelikNormalize,
|
||||||
type TercihProfili,
|
type TercihProfili,
|
||||||
} from "@/features/sihirbaz/sihirbaz-sabitler";
|
} from "@/features/sihirbaz/sihirbaz-sabitler";
|
||||||
import type { RaporParams } from "./types/rapor";
|
import type { RaporParams } from "./types/rapor";
|
||||||
@@ -69,7 +70,9 @@ export function tercihFarki(
|
|||||||
) {
|
) {
|
||||||
farklar.push("universiteTipi");
|
farklar.push("universiteTipi");
|
||||||
}
|
}
|
||||||
if (kumeFarkli(params.oncelikler, s.oncelikler)) farklar.push("oncelikler");
|
if (kumeFarkli(params.oncelikler?.map(oncelikNormalize), s.oncelikler)) {
|
||||||
|
farklar.push("oncelikler");
|
||||||
|
}
|
||||||
|
|
||||||
return farklar;
|
return farklar;
|
||||||
}
|
}
|
||||||
@@ -107,7 +110,8 @@ export function farkSatirlari(
|
|||||||
yeni: UNIVERSITE_TIPI_ETIKET[s.universiteTipi],
|
yeni: UNIVERSITE_TIPI_ETIKET[s.universiteTipi],
|
||||||
};
|
};
|
||||||
case "oncelikler":
|
case "oncelikler":
|
||||||
return { ...ortak, eski: liste(params.oncelikler), yeni: liste(s.oncelikler) };
|
return { ...ortak, eski: liste(params.oncelikler?.map(oncelikNormalize)),
|
||||||
|
yeni: liste(s.oncelikler), };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export function sihirbazDogrula(girdi: unknown): SihirbazSecimleri | null {
|
|||||||
p.oncelikler
|
p.oncelikler
|
||||||
// Eski kayıtlı profiller: çip adı "İş garantisi" idi (vaat diliyle
|
// Eski kayıtlı profiller: çip adı "İş garantisi" idi (vaat diliyle
|
||||||
// çeliştiği için değişti); seçim kaybolmasın diye yeni ada eşlenir.
|
// çeliştiği için değişti); seçim kaybolmasın diye yeni ada eşlenir.
|
||||||
.map((x) => (x === "İş garantisi" ? "İş bulma kolaylığı" : x))
|
.map(oncelikNormalize)
|
||||||
.filter(
|
.filter(
|
||||||
(x): x is string =>
|
(x): x is string =>
|
||||||
typeof x === "string" &&
|
typeof x === "string" &&
|
||||||
@@ -124,6 +124,15 @@ export function sihirbazDogrula(girdi: unknown): SihirbazSecimleri | null {
|
|||||||
return { kategoriler, iller, universiteTipi, oncelikler };
|
return { kategoriler, iller, universiteTipi, oncelikler };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eski çip adı "İş garantisi" → "İş bulma kolaylığı". Kayıtlı profil ve eski
|
||||||
|
* rapor parametreleri eski adı taşıyabilir; karşılaştırma/gösterim öncesi
|
||||||
|
* yeni ada eşlenir ki ad değişikliği "tercihlerin değişmiş" diye okunmasın.
|
||||||
|
*/
|
||||||
|
export function oncelikNormalize<T>(x: T): T | string {
|
||||||
|
return x === "İş garantisi" ? "İş bulma kolaylığı" : x;
|
||||||
|
}
|
||||||
|
|
||||||
/** localStorage'taki ortak sıra + sihirbaz profilini güvenle doğrular. */
|
/** localStorage'taki ortak sıra + sihirbaz profilini güvenle doğrular. */
|
||||||
export function tercihProfiliDogrula(girdi: unknown): TercihProfili | null {
|
export function tercihProfiliDogrula(girdi: unknown): TercihProfili | null {
|
||||||
if (typeof girdi !== "object" || girdi === null) return null;
|
if (typeof girdi !== "object" || girdi === null) return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user