Allow retrying item extraction from stored tender documents

This commit is contained in:
2026-09-06 09:35:51 +03:00
parent 32ea749669
commit a109c2e548
3 changed files with 59 additions and 12 deletions
+49 -12
View File
@@ -26,13 +26,25 @@ export const TenderDocuments = ({ tender }) => {
const ikn = String(tender?.ikn || '').replace(/[\s\u00a0]+/g, '') || undefined;
const [belgeler, setBelgeler] = useState([]);
const [yukleniyor, setYukleniyor] = useState(false);
const [listeHata, setListeHata] = useState(null);
const [listeBekliyor, setListeBekliyor] = useState(false);
const listeIstegi = useRef(0);
const dosyaRef = useRef(null);
const belgeleriYukle = useCallback(() => {
const belgeleriYukle = useCallback(async () => {
if (!ikn) return;
listTenderDocuments(ikn).then(setBelgeler).catch(() => {});
const request = ++listeIstegi.current;
setListeBekliyor(true); setListeHata(null);
try {
const rows = await listTenderDocuments(ikn);
if (request === listeIstegi.current) setBelgeler(rows);
} catch (e) {
if (request === listeIstegi.current) setListeHata(e.message);
} finally {
if (request === listeIstegi.current) setListeBekliyor(false);
}
}, [ikn]);
useEffect(() => { belgeleriYukle(); }, [belgeleriYukle]);
useEffect(() => { setBelgeler([]); belgeleriYukle(); return () => { listeIstegi.current++; }; }, [belgeleriYukle]);
// EKAP dokümanı kullanıcının tarayıcısına iniyor; indirdiği dosyayı buraya
// yükleyince ihaleye bağlanır ve metni AI analizine verilebilir.
@@ -96,6 +108,17 @@ export const TenderDocuments = ({ tender }) => {
return text;
};
const kalemleriYenidenCikar = async (b) => {
setBusy('kalem'); setError(null); setBilgi(null);
try {
const metin = await belgeMetni(b);
const { kalemCikar } = await import('../services/kalem');
const result = await kalemCikar({ ikn, metin, dosya:b.ad, documentId:b.id });
setBilgi({ isleme: `${b.ad}: ${result.sayi || 0} kalem çıkarıldı.` });
} catch (e) { setError(`Kalem çıkarımı tamamlanamadı: ${e.message}. Dosya kaydı korunmuştur.`); }
finally { await belgeleriYukle(); setBusy(null); }
};
// Yüklenen PDF'i AI'ya ver
const belgeyiAnalizEt = async (b, mod) => {
setBusy('belge'); setError(null);
@@ -294,12 +317,7 @@ export const TenderDocuments = ({ tender }) => {
</p>
)}
{error && (
<div className="bg-red-50 border border-red-200 rounded-lg p-2.5 flex gap-2">
<AlertTriangle className="w-3.5 h-3.5 text-red-600 shrink-0 mt-0.5" />
<p className="text-[11px] text-red-800 leading-relaxed">{error}</p>
</div>
)}
</>
)}
@@ -334,15 +352,27 @@ export const TenderDocuments = ({ tender }) => {
</div>
</div>
{error && (
<div className="bg-red-50 border border-red-200 rounded-lg p-2.5 flex gap-2">
<AlertTriangle className="w-3.5 h-3.5 text-red-600 shrink-0 mt-0.5" />
<p className="text-[11px] text-red-800 leading-relaxed">{error}</p>
</div>
)}
{bilgi?.isleme && <p role="status" className="text-xs text-slate-700">{bilgi.isleme}</p>}
{listeBekliyor && <p role="status" className="text-xs text-slate-500">Belge listesi yükleniyor…</p>}
{listeHata && <div role="alert" className="text-xs text-red-700">
Belge listesi alınamadı: {listeHata}
<button onClick={belgeleriYukle} className="ml-2 underline">Listeyi yeniden yükle</button>
</div>}
{belgeler.length > 0 && (
<div className="space-y-1.5">
<p className="text-[11px] font-bold text-slate-700 flex items-center gap-1.5">
<Paperclip className="w-3.5 h-3.5 text-slate-400" /> Bu ihaleye yüklenen belgeler ({belgeler.length})
</p>
{belgeler.map((b) => (
<div key={b.id} className="flex items-center gap-2 bg-white border border-slate-200 rounded-lg px-2.5 py-2">
<div key={b.id} className="flex flex-wrap items-center gap-2 bg-white border border-slate-200 rounded-lg px-2.5 py-2">
<FileText className="w-3.5 h-3.5 text-slate-400 shrink-0" />
<span className="text-xs text-slate-800 truncate flex-1" title={b.ad}>{b.ad}</span><span className="block text-[10px] text-slate-500">{documentProcessingLabel(b.isleme)}</span>
<div className="flex-1 min-w-32"><span className="block text-xs text-slate-800 break-all">{b.ad}</span><span className="block text-[10px] text-slate-500">{documentProcessingLabel(b.isleme)}</span></div>
<span className="text-[10px] text-slate-400 whitespace-nowrap">
{(b.boyut / 1024).toLocaleString('tr-TR', { maximumFractionDigits: 0 })} KB
</span>
@@ -361,7 +391,14 @@ export const TenderDocuments = ({ tender }) => {
>
Derin
</button>
<button onClick={() => belgeSil(b.id)} className="text-slate-300 hover:text-red-600" aria-label="Sil">
{b.tur === 'ihale' && okunabilirMi(b.ad) && (
<button onClick={() => kalemleriYenidenCikar(b)} disabled={busy !== null || yukleniyor || !b.erisilebilir}
aria-label={`${b.ad} için kalem çıkarımını yeniden dene`}
className="text-[10px] font-bold text-blue-700 disabled:opacity-40">
{busy === 'kalem' ? 'İşleniyor…' : 'Kalemleri yeniden çıkar'}
</button>
)}
<button disabled={busy !== null || yukleniyor} onClick={() => belgeSil(b.id)} className="text-slate-300 hover:text-red-600" aria-label="Sil">
<Trash2 className="w-3.5 h-3.5" />
</button>
</div>