fix: order calendar deadlines and surface undated tenders

This commit is contained in:
2026-09-06 14:05:24 +03:00
parent 2f34b42dc4
commit 6c33012ab8
4 changed files with 75 additions and 28 deletions
+28 -28
View File
@@ -1,8 +1,8 @@
import { useDeadlineClock } from '../../hooks/useDeadlineClock';
import { calendarMonthDays, istanbulDateKey, tenderDateKey, deadlineInfo } from '../../services/tenderCalendar';
import React, { useState } from 'react';
import { calendarMonthDays, istanbulDateKey, calendarAgenda } from '../../services/tenderCalendar';
import React, { useState, useMemo } from 'react';
import { useApp } from '../../context/AppContext';
import { CalendarDays, Clock, MapPin, AlertCircle } from 'lucide-react';
import { CalendarDays } from 'lucide-react';
export const TenderCalendar = () => {
const now = useDeadlineClock();
@@ -10,8 +10,22 @@ export const TenderCalendar = () => {
const today = istanbulDateKey(now);
const [month,setMonth] = useState(today.slice(0,7));
const [selectedDay, setSelectedDay] = useState(today);
const daysList = calendarMonthDays(month).map(day => ({...day,count:tenders.filter(t=>tenderDateKey(t)===day.key).length}));
const dayTenders = tenders.filter(t => tenderDateKey(t) === selectedDay);
const agenda=useMemo(()=>calendarAgenda(tenders,now),[tenders,now]);
const daysList=calendarMonthDays(month).map(day=>({...day,count:agenda.days.get(day.key)?.length || 0}));
const dayTenders=agenda.days.get(selectedDay) || [];
const card=({tender:t,info})=><button key={t.id} type="button" onClick={()=>setActiveTenderDetail(t)}
className="w-full text-left bg-white rounded-xl border border-slate-200 p-4 shadow-xs hover:shadow-md transition-all flex flex-wrap items-center justify-between gap-3">
<span className="min-w-0 space-y-1">
<span className="flex flex-wrap gap-2 text-xs">
<span className="bg-purple-100 text-purple-900 rounded px-2 py-1">{info.deadline?.toLocaleTimeString('tr-TR',{hour:'2-digit',minute:'2-digit',timeZone:'Europe/Istanbul'}) || (info.dateKey ? 'Saat doğrulanmalı' : 'Tarih doğrulanmalı')}</span>
<span>#{t.ikn}</span>
<span>{t.durum || ''}{t.durum ? ' · ' : ''}{info.label}</span>
</span>
<span className="block font-bold text-sm break-words">{t.baslik}</span>
<span className="block text-xs text-slate-500 break-words">{t.kurum} · {t.sehir}</span>
</span>
<span className="text-xs bg-amber-100 text-amber-900 rounded px-2 py-1">{t.alimTuru}</span>
</button>;
return (
<div className="space-y-4 my-4 animate-fade-in">
@@ -38,6 +52,8 @@ export const TenderCalendar = () => {
{daysList.map(d => (
<button
key={d.key}
aria-pressed={selectedDay===d.key}
aria-label={`${d.date} ${d.label}, ${d.count} ihale`}
onClick={() => setSelectedDay(d.key)}
className={`min-w-0 p-3 rounded-xl border text-center transition-all cursor-pointer ${
selectedDay === d.key
@@ -60,31 +76,15 @@ export const TenderCalendar = () => {
<div className="space-y-3">
<h2 className="text-sm font-bold">{selectedDay.split('-').reverse().join('.')} tarihli ihaleler</h2>
{!dayTenders.length && <p className="text-sm text-slate-500">Bu gün için yüklenen havuzda ihale yok.</p>}
{dayTenders.map(t => (
<div
key={t.id}
role="button" tabIndex={0} onKeyDown={e=>{if(e.key==='Enter' || e.key===' '){e.preventDefault();setActiveTenderDetail(t);}}}
onClick={() => setActiveTenderDetail(t)}
className="bg-white rounded-xl border border-slate-200 p-4 shadow-xs hover:shadow-md cursor-pointer transition-all flex items-center justify-between gap-4"
>
<div className="space-y-1">
<div className="flex items-center gap-2">
<span className="bg-purple-100 text-purple-900 text-[10px] font-extrabold px-2 py-0.5 rounded">
⏰ {deadlineInfo(t, now).deadline?.toLocaleTimeString('tr-TR',{hour:'2-digit',minute:'2-digit',timeZone:'Europe/Istanbul'}) || 'Saat doğrulanmalı'}
</span>
<span className="text-xs text-slate-400 font-mono">#{t.ikn}</span>
</div>
<h3 className="font-bold text-sm text-slate-900 hover:text-purple-600">{t.baslik}</h3>
<p className="text-xs text-slate-500 font-medium">🏢 {t.kurum} • 📍 {t.sehir}</p>
</div>
<span className="bg-amber-500 text-white font-extrabold text-xs px-3 py-1.5 rounded-lg shrink-0">
{t.alimTuru}
</span>
</div>
))}
<p className="text-xs text-slate-500">Saati bilinen ihaleler erken saatten geç saate sıralanır; saati doğrulanmamış kayıtlar sonda gösterilir.</p>
{dayTenders.map(card)}
</div>
{agenda.undated.length>0&&<section aria-label="Tarihi doğrulanacak ihaleler" className="space-y-3 border border-amber-300 rounded-xl p-3">
<h2 className="text-sm font-bold">Tarihi doğrulanacak ihaleler ({agenda.undated.length})</h2>
<p className="text-xs">Bu kayıtlar bir güne yerleştirilemedi. İlan detayından resmi tarih ve saati kontrol edin.</p>
{agenda.undated.map(card)}
</section>}
</div>
);
};