fix: ust serit tiklaninca donuyordu — Kanban havuzdaki tum ihaleleri (8 bin+ kart) ciziyordu; artik yalnizca takipteki/kolona tasinan ihaleler, sekme gecisi transition ile aninda vurgulaniyor

- KanbanBoard: sunucu her ilana varsayilan 'incelenecek' verdigi icin bos pano 8.372 kart basiyordu; yalnizca takipte veya kolona tasinmis ihaleler kaliyor
- TopNavigation: useTransition + spinner; agir sekmelerde tiklama karsiliksiz kalmiyor
- sunucu: 'yeni liste eskisinden kucukse alma' kurali havuzu 30 Agustos zirvesinde dondurmustu (gunlerce yeni ilan yok); eksiksiz inen liste her zaman devrede
- sunucu: isYeni/bugun eklenen UTC-TR gun farkindan hep 0'di; Europe/Istanbul gunune gore ve her yanitta tazeleniyor (eklenmeZamani da donmuyor)
- ust kartlar: 'bugun eklenenler' tum ilanlari sayiyordu ('once' metnine bakiyordu), 'son giristen beri' sabit 14'tu; sayimlar liste filtresiyle ayni olcut (ihaleTarih.js), arsiv/sonuclanan filtreleri 'sadece acik' kapisina takilmiyor

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 13:02:40 +03:00
co-authored by Claude Fable 5.1
parent 3eb0f91823
commit 920a147126
9 changed files with 226 additions and 56 deletions
+76
View File
@@ -0,0 +1,76 @@
/**
* İhale tarih yardımcıları — üst kartlar (SummaryCards) ile liste filtresi
* (TenderList) aynı ölçütü kullansın diye tek yerde.
*
* Kaynaklar tarihi farklı yazıyor:
* ilan.gov.tr → ilanTarihi "2026-09-02"
* DT (EKAP) → ilanTarihi "2026-09-02T00:00:00"
* arşiv → ilanTarihi "02.09.2026"
*/
// "gg.aa.yyyy" → Date. Ayrıştırılamayanı GEÇMİŞ SAYMAYIZ: tarihi belirsiz bir
// ihaleyi sessizce gizlemek, geçmiş bir ihaleyi göstermekten daha kötüdür.
export const ihaleGunu = (v) => {
const m = /^(\d{2})\.(\d{2})\.(\d{4})/.exec(String(v || '').trim());
if (!m) return null;
return new Date(Number(m[3]), Number(m[2]) - 1, Number(m[1]));
};
// Her biçimi "yyyy-aa-gg" dizesine indirger (karşılaştırma için).
export const ilanTarihiISO = (t) => {
const v = String(t?.ilanTarihi || '').trim();
let m = /^(\d{4})-(\d{2})-(\d{2})/.exec(v);
if (m) return `${m[1]}-${m[2]}-${m[3]}`;
m = /^(\d{2})\.(\d{2})\.(\d{4})/.exec(v);
if (m) return `${m[3]}-${m[2]}-${m[1]}`;
return '';
};
const iso = (d) => {
const y = d.getFullYear();
const a = String(d.getMonth() + 1).padStart(2, '0');
const g = String(d.getDate()).padStart(2, '0');
return `${y}-${a}-${g}`;
};
export const bugunISO = () => iso(new Date());
// Bugün yayımlanan ilan. Sunucunun isYeni bayrağı esas; o yoksa ilan tarihine bakılır.
export const bugunEklendi = (t) => Boolean(t?.isYeni) || (ilanTarihiISO(t) !== '' && ilanTarihiISO(t) === bugunISO());
// Önceki ziyaretten (ms) sonra yayımlanan ilan. Kayıt yoksa "bugün" kabul edilir.
export const girisSonrasiEklendi = (t, oncekiGirisMs) => {
const tarih = ilanTarihiISO(t);
if (!tarih) return false;
const esik = oncekiGirisMs ? iso(new Date(oncekiGirisMs)) : bugunISO();
return tarih >= esik;
};
// Süresi geçmemiş (veya tarihi okunamayan) ihale — listenin varsayılan görünümü.
export const acikIhale = (t, bugun) => {
const gun = ihaleGunu(t?.tarih);
return !(gun && gun < bugun);
};
// Bu hızlı filtreler doğası gereği GEÇMİŞ ihaleleri hedefler; "sadece açık
// ihaleler" kapısı bunlara uygulanmaz, yoksa hiç sonuç çıkmaz.
export const GECMIS_FILTRELER = new Set(['arsiv', 'sonuclananlar', 'sozlesmeler', 'iptal']);
// Hızlı filtre (üst kartlar + sol menü) ölçütü. Bilinmeyen id → her şey geçer.
export const hizliFiltreGecer = (t, id, { favoriteKurumlar = [], oncekiGirisMs = null } = {}) => {
switch (id) {
case 'favori_kurumlar': return favoriteKurumlar.includes((t.kurum || '').trim());
case 'dogrudan_temin': return t.alimTuru === 'Doğrudan Temin';
case 'ihaleler': return t.alimTuru !== 'Doğrudan Temin' && t.durum !== 'Sonuçlandı';
case 'bugun_eklenenler': return bugunEklendi(t);
case 'son_giris': return girisSonrasiEklendi(t, oncekiGirisMs);
case 'incelenenler': return Boolean(t.incelendi);
case 'incelenmedi': return !t.incelendi;
case 'takiptekiler': return Boolean(t.takipte);
case 'sozlesmeler': return t.durum === 'Sözleşme İmzalandı';
case 'sonuclananlar': return t.durum === 'Sonuçlandı';
case 'arsiv': return t.durum === 'Sonuçlandı';
case 'iptal': return t.durum === 'İptal';
default: return true;
}
};