fix: align list expiry and date ordering with tender deadlines
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { useDeadlineClock } from '../hooks/useDeadlineClock';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useApp } from '../context/AppContext';
|
||||
import { Database, PlusCircle, Eye, Layers, Trophy, TrendingDown } from 'lucide-react';
|
||||
import { bugunEklendi, acikIhale } from '../services/ihaleTarih';
|
||||
|
||||
export const LiveStatsBar = () => {
|
||||
const now = useDeadlineClock();
|
||||
const { liveStats, tenders } = useApp();
|
||||
|
||||
const yukluIhale = tenders.length;
|
||||
@@ -11,9 +13,9 @@ export const LiveStatsBar = () => {
|
||||
const kalemSayisi = tenders.reduce((s, t) => s + (t.items?.length || 0), 0);
|
||||
// Üst kartla (BUGÜN EKLENENLER) AYNI ölçüt: bugün yayımlanan ve süresi
|
||||
// geçmemiş ilanlar (ilan.gov.tr + DT). İki bant farklı sayı göstermesin.
|
||||
const bugun = new Date(); bugun.setHours(0, 0, 0, 0);
|
||||
|
||||
const bugunEklenen = tenders.length
|
||||
? tenders.filter((t) => acikIhale(t, bugun) && bugunEklendi(t)).length
|
||||
? tenders.filter((t) => acikIhale(t, now) && bugunEklendi(t)).length
|
||||
: liveStats.bugunEklenen;
|
||||
|
||||
// Havuz sağlığı: kaynak (ilan.gov.tr) sayısı 7 GÜNLÜK ORTALAMANIN çok altındaysa
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { useDeadlineClock } from '../hooks/useDeadlineClock';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useApp } from '../context/AppContext';
|
||||
import { Layers, CalendarPlus, Star, BellRing, EyeOff } from 'lucide-react';
|
||||
import { acikIhale, hizliFiltreGecer } from '../services/ihaleTarih';
|
||||
|
||||
export const SummaryCards = () => {
|
||||
const now = useDeadlineClock();
|
||||
const { tenders, quickFilter, setQuickFilter, setActiveTab, oncekiGiris, favoriteKurumlar } = useApp();
|
||||
|
||||
// Sayımlar listedeki filtreyle AYNI ölçütü kullanır (ihaleTarih.js); karta
|
||||
// tıklayınca gelen liste ile karttaki sayı birbirini tutar.
|
||||
const sayim = useMemo(() => {
|
||||
const bugun = new Date(); bugun.setHours(0, 0, 0, 0);
|
||||
const secenek = { favoriteKurumlar: favoriteKurumlar || [], oncekiGirisMs: oncekiGiris };
|
||||
const say = (id) => tenders.filter((t) => acikIhale(t, bugun) && hizliFiltreGecer(t, id, secenek)).length;
|
||||
const say = (id) => tenders.filter((t) => acikIhale(t, now) && hizliFiltreGecer(t, id, secenek)).length;
|
||||
return {
|
||||
all: say('all'),
|
||||
bugun_eklenenler: say('bugun_eklenenler'),
|
||||
@@ -19,7 +20,7 @@ export const SummaryCards = () => {
|
||||
son_giris: say('son_giris'),
|
||||
incelenmedi: say('incelenmedi'),
|
||||
};
|
||||
}, [tenders, oncekiGiris, favoriteKurumlar]);
|
||||
}, [tenders, oncekiGiris, favoriteKurumlar, now]);
|
||||
|
||||
const cards = [
|
||||
{
|
||||
|
||||
@@ -3,7 +3,8 @@ import { useApp } from '../context/AppContext';
|
||||
import { TenderCard } from './TenderCard';
|
||||
import { Download, SlidersHorizontal, Layers, Loader2 } from 'lucide-react';
|
||||
import confetti from 'canvas-confetti';
|
||||
import { acikIhale, hizliFiltreGecer, GECMIS_FILTRELER } from '../services/ihaleTarih';
|
||||
import { useDeadlineClock } from '../hooks/useDeadlineClock';
|
||||
import { acikIhale, hizliFiltreGecer, GECMIS_FILTRELER, compareTenderDeadlines } from '../services/ihaleTarih';
|
||||
|
||||
// İl adları kaynaklar arasında farklı yazılabiliyor (Ankara / ANKARA);
|
||||
// filtre büyük-küçük harf ve Türkçe karakter duyarlılığına takılmasın.
|
||||
@@ -35,8 +36,7 @@ export const TenderList = () => {
|
||||
const [gosterilen, setGosterilen] = useState(SAYFA_ADIM);
|
||||
const dipRef = useRef(null);
|
||||
|
||||
// Bugünün başlangıcı — "bugün ihalesi olan" listede kalsın.
|
||||
const bugun = useMemo(() => { const d = new Date(); d.setHours(0, 0, 0, 0); return d; }, []);
|
||||
const now = useDeadlineClock();
|
||||
|
||||
// Filter logic
|
||||
const filteredTenders = useMemo(() => {
|
||||
@@ -57,7 +57,7 @@ export const TenderList = () => {
|
||||
// Geçmiş ihaleler: varsayılan olarak GİZLİ. Kullanıcı "geçmişi de göster"
|
||||
// derse (onlyValidOnly=false) hepsi gelir. Tarihi okunamayan kayıt gizlenmez.
|
||||
// Arşiv / sonuçlananlar gibi geçmişi hedefleyen hızlı filtrelerde kapı kapalı.
|
||||
if (onlyValidOnly && !GECMIS_FILTRELER.has(quickFilter) && !acikIhale(tender, bugun)) return false;
|
||||
if (onlyValidOnly && !GECMIS_FILTRELER.has(quickFilter) && !acikIhale(tender, now)) return false;
|
||||
|
||||
// Hızlı filtre (üst kartlar + sol menü) — ölçütler ihaleTarih.js'de tek yerde
|
||||
if (!hizliFiltreGecer(tender, quickFilter, { favoriteKurumlar: favoriteKurumlar || [], oncekiGirisMs: oncekiGiris, uyumEsigi })) return false;
|
||||
@@ -73,11 +73,11 @@ export const TenderList = () => {
|
||||
}).sort((a, b) => {
|
||||
if (sortBy === 'uyum') return (b.aiMatchScore ?? -1) - (a.aiMatchScore ?? -1) || b.id.localeCompare(a.id);
|
||||
if (sortBy === 'newest') return b.id.localeCompare(a.id);
|
||||
if (sortBy === 'urgent') return a.tarih.localeCompare(b.tarih);
|
||||
if (sortBy === 'distant') return b.tarih.localeCompare(a.tarih);
|
||||
if (sortBy === 'urgent') return compareTenderDeadlines(a,b);
|
||||
if (sortBy === 'distant') return compareTenderDeadlines(a,b,-1);
|
||||
return 0;
|
||||
});
|
||||
}, [tenders, searchQuery, kalemEslesen, uyumEsigi, selectedCity, selectedAlimTuru, selectedSektorler, selectedCpvler, selectedKurumTipi, quickFilter, sortBy, onlyValidOnly, bugun, favoriteKurumlar, oncekiGiris]);
|
||||
}, [tenders, searchQuery, kalemEslesen, uyumEsigi, selectedCity, selectedAlimTuru, selectedSektorler, selectedCpvler, selectedKurumTipi, quickFilter, sortBy, onlyValidOnly, now, favoriteKurumlar, oncekiGiris]);
|
||||
|
||||
const gorunenTenders = filteredTenders.slice(0, gosterilen);
|
||||
const dahaVar = gosterilen < filteredTenders.length;
|
||||
@@ -148,7 +148,7 @@ export const TenderList = () => {
|
||||
{/* Varsayılan: yalnızca ihale tarihi geçmemiş ilanlar */}
|
||||
<label
|
||||
className="flex items-center gap-1.5 text-xs font-semibold text-slate-700 cursor-pointer select-none whitespace-nowrap"
|
||||
title="Kapalı: ihale tarihi geçmiş ilanlar da listelenir"
|
||||
title="Süresi geçtiği bilinen, iptal edilen ve sonuçlanan kayıtlar gizlenir. Tarihi veya saati bilinmeyen kayıtlar ayrıca doğrulanmalıdır."
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -156,10 +156,10 @@ export const TenderList = () => {
|
||||
onChange={(e) => setOnlyValidOnly(e.target.checked)}
|
||||
className="accent-blue-600"
|
||||
/>
|
||||
Sadece açık ihaleler
|
||||
Süresi geçenleri gizle
|
||||
</label>
|
||||
|
||||
<select
|
||||
<select aria-label="İhale sıralaması"
|
||||
value={sortBy}
|
||||
onChange={(e) => setSortBy(e.target.value)}
|
||||
className="bg-slate-50 border border-slate-300 rounded-lg px-3 py-1.5 text-xs font-semibold text-slate-700 focus:ring-2 focus:ring-blue-500"
|
||||
|
||||
+16
-13
@@ -1,3 +1,4 @@
|
||||
import { deadlineInfo, tenderDateKey, istanbulDateKey } from './tenderCalendar.js';
|
||||
/**
|
||||
* İhale tarih yardımcıları — üst kartlar (SummaryCards) ile liste filtresi
|
||||
* (TenderList) aynı ölçütü kullansın diye tek yerde.
|
||||
@@ -11,9 +12,8 @@
|
||||
// "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]));
|
||||
const key = tenderDateKey({tarih:v});
|
||||
return key ? new Date(key+'T00:00:00+03:00') : null;
|
||||
};
|
||||
|
||||
// Her biçimi "yyyy-aa-gg" dizesine indirger (karşılaştırma için).
|
||||
@@ -26,12 +26,7 @@ export const ilanTarihiISO = (t) => {
|
||||
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}`;
|
||||
};
|
||||
const iso = istanbulDateKey;
|
||||
|
||||
export const bugunISO = () => iso(new Date());
|
||||
|
||||
@@ -47,10 +42,18 @@ export const girisSonrasiEklendi = (t, oncekiGirisMs) => {
|
||||
};
|
||||
|
||||
// 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);
|
||||
};
|
||||
export const acikIhale = (t, now = new Date()) =>
|
||||
!['Sonuçlandı','Sözleşme İmzalandı','İptal'].includes(t?.durum) && !t?.kazananFirma && deadlineInfo(t,now).expired !== true;
|
||||
|
||||
export function compareTenderDeadlines(a,b,direction = 1) {
|
||||
const x=deadlineInfo(a),y=deadlineInfo(b);
|
||||
if (!x.dateKey || !y.dateKey) return x.dateKey ? -1 : y.dateKey ? 1 : 0;
|
||||
const day=x.dateKey.localeCompare(y.dateKey);
|
||||
if (day) return day * direction;
|
||||
// Unknown times follow known times on the same day in either direction.
|
||||
if (!x.deadline || !y.deadline) return x.deadline ? -1 : y.deadline ? 1 : 0;
|
||||
return (x.deadline-y.deadline)*direction;
|
||||
}
|
||||
|
||||
// Bu hızlı filtreler doğası gereği GEÇMİŞ ihaleleri hedefler; "sadece açık
|
||||
// ihaleler" kapısı bunlara uygulanmaz, yoksa hiç sonuç çıkmaz.
|
||||
|
||||
Reference in New Issue
Block a user