fix(liste): kayıtlı manuel liste tabanları yeni veri yılına tazelenir; yıl metinleri 2021–2026
All checks were successful
Deploy / deploy (push) Successful in 7m24s
All checks were successful
Deploy / deploy (push) Successful in 7m24s
Denetim bulgusu (2026-09-21): ManuelTercih.efektifSira ekleme anının snapshot'ıydı; 2026 verisi yüklenince /listem 2025 tabanıyla renk verirken /sonuc tablosu 2026 gösteriyordu ve hiçbir yol yenilemiyordu. - db.ts: efektifSiralarGetir(ids) — güncel COALESCE(sira2026, sira2025). - liste-actions: listeTabanlariniTazele (oturumsuz, ≤24 id); sunucu kaydı (listeSenkronla/listeyiKaydet) tazelenmiş yazılır. - liste-store: tabanlariGuncelle — efektif sıra + profil varsa risk yenilenir, arşivlenmiş program eski değerini korur, değişiklik yoksa yayın yok. - liste-senkron: girişli/girişsiz herkes için sekme oturumu başına bir kez (anahtar SON_YIL taşır). - meraklisina-icerik: "son beş yılın" → VERI_YIL_SAYISI. - rehber (4 yazı): katalog sayfalarını anlatan "2021–2025 / son 5 yıl" ifadeleri 2021–2026 / son yıllar; yazıların kendi örnek tabloları 2021–2025 olarak etiketli kaldı. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -13,9 +13,19 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { PROFIL_DEGISTI_EVENT } from "@/features/sihirbaz/sihirbaz-sabitler";
|
||||
import { tercihProfiliOku, tercihProfiliYaz } from "@/features/sihirbaz/sihirbaz-profil";
|
||||
import { listeOku, sunucuylaBirlestir, useManuelListe } from "../liste-store";
|
||||
import { SON_YIL } from "@/lib/veri-yillari";
|
||||
import {
|
||||
listeOku,
|
||||
sunucuylaBirlestir,
|
||||
tabanlariGuncelle,
|
||||
useManuelListe,
|
||||
} from "../liste-store";
|
||||
import { oturumDurumuBildir } from "../liste-oturum-sinyali";
|
||||
import { listeSenkronla, listeyiKaydet } from "../liste-actions";
|
||||
import {
|
||||
listeSenkronla,
|
||||
listeTabanlariniTazele,
|
||||
listeyiKaydet,
|
||||
} from "../liste-actions";
|
||||
|
||||
const DEBOUNCE_MS = 2000;
|
||||
|
||||
@@ -29,6 +39,33 @@ export function ListeSenkron({ userId }: { userId: string | null }) {
|
||||
oturumDurumuBildir(Boolean(userId));
|
||||
}, [userId]);
|
||||
|
||||
// Yeni veri yılı: kayıtlı satırların efektif sırası ekleme anının
|
||||
// snapshot'ıdır. Girişli/girişsiz herkes için, sekme oturumu başına bir kez
|
||||
// güncel sıralar çekilir ve risk yeniden hesaplanır — /listem ile /sonuc
|
||||
// tablosu aynı yılın tabanını göstersin. Anahtar SON_YIL'i taşır: yıl
|
||||
// değişince eski işaret geçersizleşir.
|
||||
useEffect(() => {
|
||||
const mevcut = listeOku();
|
||||
if (mevcut.length === 0) return;
|
||||
const anahtar = `kolaytercih.liste-taban.${SON_YIL}`;
|
||||
try {
|
||||
if (sessionStorage.getItem(anahtar)) return;
|
||||
} catch {}
|
||||
let iptal = false;
|
||||
void (async () => {
|
||||
const tabanlar = await listeTabanlariniTazele(mevcut.map((t) => t.id));
|
||||
if (iptal) return;
|
||||
tabanlariGuncelle(tabanlar);
|
||||
try {
|
||||
sessionStorage.setItem(anahtar, "1");
|
||||
} catch {}
|
||||
})();
|
||||
return () => {
|
||||
iptal = true;
|
||||
};
|
||||
// Yalnız mount'ta: liste sonradan değişse de ekleme anı zaten güncel veriyle olur
|
||||
}, []);
|
||||
|
||||
// Giriş anı merge'i — sekme oturumu başına bir kez (idempotent olsa da
|
||||
// her gezinmede sunucuya gitmesin diye sessionStorage ile tekilleştirilir).
|
||||
useEffect(() => {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { getSession } from "@/lib/session";
|
||||
import { appDb, schema } from "@/lib/appdb";
|
||||
import { efektifSiralarGetir } from "@/lib/db";
|
||||
import { tercihProfiliDogrula, type TercihProfili } from "@/features/sihirbaz/sihirbaz-sabitler";
|
||||
import {
|
||||
MANUEL_LISTE_MAX,
|
||||
@@ -24,11 +25,48 @@ export type ListeSenkronSonucu =
|
||||
| { ok: true; items: ManuelTercih[]; profil: TercihProfili | null }
|
||||
| { ok: false };
|
||||
|
||||
const PROGRAM_ID = /^\d{1,12}$/;
|
||||
|
||||
/**
|
||||
* Kayıtların taşıdığı efektif sıra ekleme anının snapshot'ıdır; yeni
|
||||
* yerleştirme yılı yüklenince eskir. Güncel değeri DB'den okuyup satıra yazar;
|
||||
* güncel kılavuzda olmayan program (arşiv) eski değerini korur. Risk burada
|
||||
* yenilenmez — aday sırasını istemci bilir (bkz. liste-store tabanlariGuncelle).
|
||||
*/
|
||||
function tabanlariTazele(items: ManuelTercih[]): ManuelTercih[] {
|
||||
const guncel = efektifSiralarGetir(
|
||||
items.map((t) => t.id).filter((id) => PROGRAM_ID.test(id)),
|
||||
);
|
||||
return items.map((t) =>
|
||||
t.id in guncel ? { ...t, efektifSira: guncel[t.id] } : t,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* İstemcinin (girişli ya da girişsiz) localStorage listesini yeni veri yılına
|
||||
* tazelemesi için: id → güncel efektif sıra. Program verisi herkese açık, oturum
|
||||
* gerekmez. Yanıt yalnız listedeki (en fazla 24) id'yi kapsar.
|
||||
*/
|
||||
export async function listeTabanlariniTazele(
|
||||
ids: unknown,
|
||||
): Promise<Record<string, number | null>> {
|
||||
if (!Array.isArray(ids)) return {};
|
||||
const temiz = [
|
||||
...new Set(
|
||||
ids.filter(
|
||||
(id): id is string => typeof id === "string" && PROGRAM_ID.test(id),
|
||||
),
|
||||
),
|
||||
].slice(0, MANUEL_LISTE_MAX);
|
||||
return efektifSiralarGetir(temiz);
|
||||
}
|
||||
|
||||
async function kaydiYaz(
|
||||
userId: string,
|
||||
items: ManuelTercih[],
|
||||
itemsHam: ManuelTercih[],
|
||||
profil: TercihProfili | null,
|
||||
): Promise<void> {
|
||||
const items = tabanlariTazele(itemsHam);
|
||||
const now = new Date();
|
||||
await appDb
|
||||
.insert(schema.savedLists)
|
||||
@@ -65,10 +103,12 @@ export async function listeSenkronla(girdi: {
|
||||
const sunucuProfil = tercihProfiliDogrula(mevcut?.profil);
|
||||
|
||||
const clientIdler = new Set(clientItems.map((t) => t.id));
|
||||
const items = [
|
||||
...clientItems,
|
||||
...sunucuItems.filter((t) => !clientIdler.has(t.id)),
|
||||
].slice(0, MANUEL_LISTE_MAX);
|
||||
const items = tabanlariTazele(
|
||||
[
|
||||
...clientItems,
|
||||
...sunucuItems.filter((t) => !clientIdler.has(t.id)),
|
||||
].slice(0, MANUEL_LISTE_MAX),
|
||||
);
|
||||
const profil = clientProfil ?? sunucuProfil;
|
||||
|
||||
await kaydiYaz(userId, items, profil);
|
||||
|
||||
@@ -194,6 +194,31 @@ export function programdanManuelTercih(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Yeni veri yılı: sunucudan gelen güncel efektif sıralarla kayıtları tazeler,
|
||||
* profil varsa riski yeniden hesaplar. Haritada olmayan id (arşivlenmiş
|
||||
* program) dokunulmaz. Değişiklik yoksa referans korunur, yayın yapılmaz.
|
||||
*/
|
||||
export function tabanlariGuncelle(tabanlar: Record<string, number | null>) {
|
||||
yukle();
|
||||
const profil = tercihProfiliOku();
|
||||
let degisti = false;
|
||||
const yeni = liste.map((t) => {
|
||||
if (!(t.id in tabanlar) || tabanlar[t.id] === t.efektifSira) return t;
|
||||
degisti = true;
|
||||
const efektifSira = tabanlar[t.id];
|
||||
return {
|
||||
...t,
|
||||
efektifSira,
|
||||
risk: profil ? riskHesapla(efektifSira, profil.sira) : t.risk,
|
||||
};
|
||||
});
|
||||
if (!degisti) return;
|
||||
liste = yeni;
|
||||
kaydet();
|
||||
yayinla();
|
||||
}
|
||||
|
||||
/** Profil sırasına göre listedeki riskleri yeniden hesaplar (program silmez). */
|
||||
export function riskleriYenile(profil?: TercihProfili | null) {
|
||||
yukle();
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
SiralamaDemo,
|
||||
} from "./meraklisina-demo";
|
||||
import { HuniSemasi } from "./huni-semasi";
|
||||
import { VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||
|
||||
/**
|
||||
* Algoritmanın öğrenci diline çevrilmiş anlatımı. Teknik detay (SQL, çarpan
|
||||
@@ -135,8 +136,8 @@ export function MeraklisinaIcerik() {
|
||||
Tüm bu hesaplar <strong>geçen yılların</strong>{" "}
|
||||
yerleştirme sonuçlarına dayanır. Kontenjanlar, tercih davranışları
|
||||
ve puanlar her yıl değişir; geçen yıl “güvenli”
|
||||
görünen bir bölüm bu yıl dolabilir. Bu yüzden satırlarda son beş
|
||||
yılın taban seyrini de gösteririz — çizgi yukarı tırmanıyorsa
|
||||
görünen bir bölüm bu yıl dolabilir. Bu yüzden satırlarda son{" "}
|
||||
{VERI_YIL_SAYISI} yılın taban seyrini de gösteririz — çizgi yukarı tırmanıyorsa
|
||||
bölüm zorlaşıyor demektir. KolayTercih karar <em>destek</em>{" "}
|
||||
aracıdır; listenin son hâli ve sorumluluğu her zaman sana aittir.
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user