diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 418ea8c..29b1c42 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -295,3 +295,9 @@ D01 kapsamında `belgeler.isleme` sürümlü migration ile eklendi. Dosyanın ka Doğrulama: 70 otomatik test ve üretim derlemesi geçti. Gerçek HTTP testinde özel firma belgesi, bekleyen ihale metni, yetkisiz istek, bozuk metin → hata → yeniden deneme → kalemsiz sonuç ve ayrı GET üzerinden kalıcı durum kontrol edildi. Mevcut bundle boyutu/import uyarıları sürüyor. Sınırlar: Bu paket tam belge yaşam döngüsü kabulü değildir. Tarayıcıda PDF okuma hatasının sunucuya kalıcı aktarımı, ekrandan mevcut belgeyi yeniden işleme, dosya/DB çökme atomikliği ve gerçek resmi dosyalarla uçtan uca kabul halen tamamlanmadı. Canlıya dağıtım yapılmadı. + +### Kaydedilmiş ihale dokümanında yeniden kalem çıkarımı + +İhale detayında okunabilir, erişilebilir ihale dosyalarına yeniden kalem çıkarımı eklendi. Aynı dosya indirilip metni çıkarılıyor veya mevcut sunucu metin çıkarıcısı kullanılıyor; aynı documentId ile yetkili kalem aktarımı yeniden çağrılıyor. Yeni dosya yüklemesi gerekmiyor. İşlem sırasında silme engelleniyor; sonuç veya hata görünür, belge listesi yeniden okunuyor. Liste okuma hatası artık sessizce yutulmuyor; yükleniyor, hata ve yeniden yükle durumları var. Eski liste isteğinin yeni ihale listesini ezmesi istek numarasıyla önleniyor. Belge satırları dar alanda sarılabiliyor. + +Doğrulama: üretim derlemesi geçti; HTTP testi, başarısız çıkarım ve yeniden deneme sonrasında aynı belge kaydının ve orijinal dosya baytlarının korunduğunu kontrol ediyor. Bu düğmenin tarayıcı kabulü, genel Doküman Deposu'na aynı işlemin taşınması ve tarayıcı metin okuma hatasının sunucuda kalıcılaştırılması henüz tamamlanmadı. diff --git a/src/components/TenderDocuments.jsx b/src/components/TenderDocuments.jsx index 408ca2f..e87168b 100644 --- a/src/components/TenderDocuments.jsx +++ b/src/components/TenderDocuments.jsx @@ -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 }) => {

)} - {error && ( -
- -

{error}

-
- )} + )} @@ -334,15 +352,27 @@ export const TenderDocuments = ({ tender }) => { + {error && ( +
+ +

{error}

+
+ )} + {bilgi?.isleme &&

{bilgi.isleme}

} + {listeBekliyor &&

Belge listesi yükleniyor…

} + {listeHata &&
+ Belge listesi alınamadı: {listeHata} + +
} {belgeler.length > 0 && (

Bu ihaleye yüklenen belgeler ({belgeler.length})

{belgeler.map((b) => ( -
+
- {b.ad}{documentProcessingLabel(b.isleme)} +
{b.ad}{documentProcessingLabel(b.isleme)}
{(b.boyut / 1024).toLocaleString('tr-TR', { maximumFractionDigits: 0 })} KB @@ -361,7 +391,14 @@ export const TenderDocuments = ({ tender }) => { > Derin - + )} +
diff --git a/tests/http-state.test.mjs b/tests/http-state.test.mjs index 2b65a52..b0b848e 100644 --- a/tests/http-state.test.mjs +++ b/tests/http-state.test.mjs @@ -93,6 +93,10 @@ test('shared item extraction requires an owned tender document and matching tend assert.equal((await processing()).status,'no_items'); assert.equal((await processing()).count,0); assert.ok((await processing()).updatedAt); + assert.equal((await request('/api/me/documents')).data.belgeler.filter(d=>d.id===tender.data.id).length,1); + const retained=await fetch(base+'/api/me/documents/download?id='+tender.data.id,{headers:{Authorization:'Bearer '+token}}); + assert.equal(retained.status,200); + assert.equal(await retained.text(),Buffer.from(body.icerikBase64,'base64').toString()); assert.equal((await request('/api/kalem/cikar','POST',{ikn:body.ikn,metin:'test'})).status,403); await request('/api/me/documents?id='+company.data.id,'DELETE'); await request('/api/me/documents?id='+tender.data.id,'DELETE');