diff --git a/docs/quality/evidence/qa-excel-ready-link.txt b/docs/quality/evidence/qa-excel-ready-link.txt new file mode 100644 index 0000000..b115c0a --- /dev/null +++ b/docs/quality/evidence/qa-excel-ready-link.txt @@ -0,0 +1,2 @@ +09:28:30 itibarıyla 5 ihale için Excel hazır.Excel dosyasını indir — ihaleist_ihale_listesi_1788676110092.xlsx +İndirme bağlantısı doğrudan tıklandı. Diskte dosya doğrulaması yapılmadı. diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index f681cff..7149c35 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -272,3 +272,10 @@ SQLite snapshot, referanslı belge kopyası, SHA-256 manifesti, bütünlük kont - Kanıtlar: `evidence/qa-integrated-search.txt`, `evidence/qa-integrated-form.txt` (yalnız yapay test firma verisi). - Excel düğmesi sonrası arayüz hata göstermedi; ancak download olayı 3 saniye ve yeniden denemede 15 saniye içinde gözlenemedi. İndirme GEÇTİ sayılmadı; tarayıcı olay izlemesi mi uygulama indirme yolu mu ayrıştırılmalı. Önceki dosya üretme/yeniden okuma testi bu uçtan uca açığı kapatmaz. - Bu kanıt resmi beş ihale dosyası kabulü değildir; sentetik yerel regresyondur. Gerçek mobil viewport, gerçek belge ve resmi gönderim doğrulaması açık. + +### D03 / D09 — Görünür Excel indirme bağlantısı + +- Liste dışa aktarımı artık “Excel Hazırla” → dosya adı/kayıt sayısı/hazırlanma saati → doğrudan indirme bağlantısı akışını kullanıyor. Asenkron üretim sonrası görünmez programatik tıklamaya bağımlılık kaldırıldı; hazır dosya tekrar indirilebilir. +- Oluşan blob bağlantısı yeni dosya hazırlanırken veya ekran kapanırken temizleniyor. Oturum değişince eski hazır dosya bağlantısı gösterilmiyor. Çıktı tıklama anındaki sonuç kümesini temsil ediyor; yeni filtre için yeniden hazırlama gerekiyor. +- Tarayıcıda beş sentetik ihale için dosya hazır durumu ve `.xlsx` bağlantısı görüldü, doğrudan bağlantı tıklandı. Kanıt: `evidence/qa-excel-ready-link.txt`. Üretim derlemesi geçti; dosya içeriği için önceki XLSX roundtrip regresyonu geçerlidir. +- İndirme geçmişini kontrol etmek üzere chrome://downloads erişimi tarayıcı URL güvenlik politikası tarafından engellendi; başka yoldan bu geçmişe erişilmedi. Diskte nihai dosya ve masaüstü Excel açılışı doğrulanmış sayılmıyor. Önceki indirme olayı zaman aşımının kesin kök nedeni henüz belirlenmedi. diff --git a/src/components/TenderList.jsx b/src/components/TenderList.jsx index e38ad20..08ab3d4 100644 --- a/src/components/TenderList.jsx +++ b/src/components/TenderList.jsx @@ -101,12 +101,16 @@ export const TenderList = () => { const [exporting,setExporting] = useState(false); const [exportError,setExportError] = useState(null); + const [preparedExport,setPreparedExport] = useState(null); + const preparedUrl = useRef(null); const exportPending = useRef(false); const mounted = useRef(true); - useEffect(()=>{mounted.current=true;return()=>{mounted.current=false;};},[]); + useEffect(()=>{mounted.current=true;return()=>{mounted.current=false;if(preparedUrl.current)URL.revokeObjectURL(preparedUrl.current);};},[]); const handleExportExcel = async () => { if (exportPending.current) return; exportPending.current = true; setExporting(true); setExportError(null); + if(preparedUrl.current) URL.revokeObjectURL(preparedUrl.current); + preparedUrl.current=null;setPreparedExport(null); const session = getToken(); // Export the full filtered result at click time, including rows below the fold. const selected = filteredTenders.map(t=>({...t})); @@ -114,10 +118,9 @@ export const TenderList = () => { const bytes = await createTenderWorkbook(selected); if (!mounted.current || getToken() !== session) return; const url = URL.createObjectURL(new Blob([bytes], {type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'})); - const link = document.createElement('a'); - link.href = url; link.download = `ihaleist_ihale_listesi_${Date.now()}.xlsx`; - document.body.appendChild(link); link.click(); link.remove(); - setTimeout(()=>URL.revokeObjectURL(url),1000); + preparedUrl.current=url; + setPreparedExport({url,name:`ihaleist_ihale_listesi_${Date.now()}.xlsx`,count:selected.length, + query:searchQuery.trim(),session,createdAt:new Date().toLocaleTimeString('tr-TR')}); } catch (error) { if (mounted.current && getToken() === session) setExportError(error.message); } finally { exportPending.current=false; if (mounted.current) setExporting(false); } }; @@ -165,12 +168,16 @@ export const TenderList = () => { className="bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs px-3.5 py-2 rounded-lg flex items-center gap-1.5 transition-colors shadow-xs shrink-0" > - {exporting ? 'Excel hazırlanıyor…' : 'Excel İndir'} + {exporting ? 'Excel hazırlanıyor…' : 'Excel Hazırla'} {sortBy === 'newest' &&

Yayın tarihi bilinmeyen kayıtlar sonda gösterilir. Aynı gün içinde saati bilinen kayıtlar önce gelir.

} + {preparedExport && preparedExport.session === getToken() &&
+

{preparedExport.createdAt} itibarıyla {preparedExport.count} ihale için Excel hazır{preparedExport.query ? ` — arama: ${preparedExport.query}` : ''}.

+ Excel dosyasını indir — {preparedExport.name} +
} {exportError &&

{exportError}

} {/* Tender Cards List */} {gorunenTenders.length === 0 ? (