fix: export filtered tender list as real XLSX

This commit is contained in:
2026-09-06 09:19:41 +03:00
parent 9c7055a543
commit f593d5e372
4 changed files with 75 additions and 26 deletions
+26 -26
View File
@@ -2,7 +2,8 @@ import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react'
import { useApp } from '../context/AppContext';
import { TenderCard } from './TenderCard';
import { Download, SlidersHorizontal, Layers, Loader2 } from 'lucide-react';
import confetti from 'canvas-confetti';
import { createTenderWorkbook } from '../services/tenderWorkbook';
import { getToken } from '../services/auth';
import { useDeadlineClock } from '../hooks/useDeadlineClock';
import { acikIhale, hizliFiltreGecer, GECMIS_FILTRELER, compareTenderDeadlines } from '../services/ihaleTarih';
@@ -107,29 +108,27 @@ export const TenderList = () => {
return () => gozlemci.disconnect();
}, [dahaVar, dahaGoster]);
const handleExportExcel = () => {
confetti({ particleCount: 80, spread: 60, origin: { y: 0.6 } });
const headers = ['IKN', 'Baslik', 'Kurum', 'Sehir', 'Tarih', 'Alim Turu', 'Sektor', 'CPV'];
const rows = filteredTenders.map(t => [
t.ikn,
`"${t.baslik.replace(/"/g, '""')}"`,
`"${t.kurum.replace(/"/g, '""')}"`,
t.sehir,
t.tarih,
t.alimTuru,
t.sektor,
`"${(t.cpvLabel || (t.cpv || []).join(', ') || '').replace(/"/g, '""')}"`
]);
const csvContent = 'data:text/csv;charset=utf-8,\uFEFF' + [headers.join(','), ...rows.map(r => r.join(','))].join('\n');
const encodedUri = encodeURI(csvContent);
const link = document.createElement('a');
link.setAttribute('href', encodedUri);
link.setAttribute('download', `ihaleist_ihale_listesi_${Date.now()}.csv`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
const [exporting,setExporting] = useState(false);
const [exportError,setExportError] = useState(null);
const exportPending = useRef(false);
const mounted = useRef(true);
useEffect(()=>{mounted.current=true;return()=>{mounted.current=false;};},[]);
const handleExportExcel = async () => {
if (exportPending.current) return;
exportPending.current = true; setExporting(true); setExportError(null);
const session = getToken();
// Export the full filtered result at click time, including rows below the fold.
const selected = filteredTenders.map(t=>({...t}));
try {
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);
} catch (error) { if (mounted.current && getToken() === session) setExportError(error.message); }
finally { exportPending.current=false; if (mounted.current) setExporting(false); }
};
return (
@@ -171,15 +170,16 @@ export const TenderList = () => {
</select>
<button
onClick={handleExportExcel}
onClick={handleExportExcel} disabled={exporting || !filteredTenders.length}
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"
>
<Download className="w-4 h-4" />
<span>Excel İndir</span>
<span>{exporting ? 'Excel hazırlanıyor…' : 'Excel İndir'}</span>
</button>
</div>
</div>
{exportError && <p role="alert" className="text-sm text-red-700">{exportError}</p>}
{/* Tender Cards List */}
{gorunenTenders.length === 0 ? (
<div className="bg-white rounded-xl border border-slate-200 p-12 text-center shadow-xs">