diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md
index c888435..1dd08b0 100644
--- a/docs/quality/uygulama-durumu.md
+++ b/docs/quality/uygulama-durumu.md
@@ -214,3 +214,10 @@ SQLite snapshot, referanslı belge kopyası, SHA-256 manifesti, bütünlük kont
- Service worker v3 yalnız bilinen kabuk ve varlık yollarını önbellekliyor. API, authorization başlıklı istekler ve rastgele GET yolları ağda kalıyor. Başarısız/yanlış MIME yanıtları sağlam kabuğu veya JS/CSS’yi bozamıyor; HTML yedeği varlık isteğine dönmüyor. Aktivasyon sadece eski İhaleist cache adlarını temizliyor.
- Kanıt: gerçek sw.js kodunu VM içinde fetch/activate olaylarıyla çalıştıran iki regresyon eklendi; 59/59 test ve üretim derlemesi geçti. API/özel yol, authorization, sunucu 500, offline kabuk, offline asset, yanlış MIME ve başka uygulama cache’ini koruma kontrol edildi.
- Sınır: VM testleri gerçek service worker kurulumu/güncellemesi veya cihazda offline kullanım kabulü değildir. Bunlar ve hesap değişiminin gerçek tarayıcıdaki tüm PWA senaryoları hâlâ açık.
+
+### D02 / D05 — Ana liste tarih filtresi ve sıralaması
+
+- Acil/uzak sıralamada gg.aa.yyyy metninin alfabetik karşılaştırılması kaldırıldı; yıl/ay/gün ve aynı gün içinde saat karşılaştırılıyor. Tarihi bilinmeyenler iki yönde de sonda kalıyor.
+- Ana liste, özet kartları ve canlı üst sayacın açık ihale hesabı ortak deadlineInfo ile çalışıyor; 30 saniyelik saat güncellemesi sayfa açıkken süre dolmasını yansıtıyor. İptal/sonuç/sözleşme ve kazananı bilinen kayıtlar açık kabul edilmiyor. Tarih veya saat bilinmiyorsa uydurulmuyor.
+- Filtre etiketi ve açıklaması eksik tarihli kaydın kesin açık olduğu izlenimini azaltacak şekilde güncellendi; sıralama alanına erişilebilir ad eklendi. Bugün/önceki ziyaret gün anahtarı Türkiye saatine bağlandı.
+- Kanıt: 61/61 test ve build geçti. Ay/yıl geçişi, aynı gün saat sırası, bilinmeyen tarih, tam son teklif anı, eksik saatte Türkiye gece yarısı ve kapalı durumlar test edildi. Yeni eklenenler sıralamasının kayıt kimliği kullanımı, tüm kaynakların ilan zamanı doğrulaması ve tarayıcıda saat ilerletme kabulü hâlâ açık.
diff --git a/src/components/LiveStatsBar.jsx b/src/components/LiveStatsBar.jsx
index 2eaaa76..16ddf22 100644
--- a/src/components/LiveStatsBar.jsx
+++ b/src/components/LiveStatsBar.jsx
@@ -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
diff --git a/src/components/SummaryCards.jsx b/src/components/SummaryCards.jsx
index 22fa2ad..e3509a1 100644
--- a/src/components/SummaryCards.jsx
+++ b/src/components/SummaryCards.jsx
@@ -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 = [
{
diff --git a/src/components/TenderList.jsx b/src/components/TenderList.jsx
index ba7626a..66cc026 100644
--- a/src/components/TenderList.jsx
+++ b/src/components/TenderList.jsx
@@ -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 */}
-