Expose document text and OCR page provenance for review
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { readableDocument, readStoredDocument, retryDocumentExtraction } from '../services/storedDocument';
|
||||
import { readableDocument, readStoredDocument, inspectStoredDocument, retryDocumentExtraction } from '../services/storedDocument';
|
||||
import { documentProcessingLabel } from '../services/documentProcessing';
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { useApp } from '../context/AppContext';
|
||||
@@ -18,6 +18,7 @@ export const TenderDocuments = ({ tender }) => {
|
||||
const [error, setError] = useState(null);
|
||||
const [bilgi, setBilgi] = useState(null);
|
||||
const [ilerleme, setIlerleme] = useState('');
|
||||
const [metinOnizleme, setMetinOnizleme] = useState(null);
|
||||
// Son üretilen EKAP doküman adresi — yeni sekme engellenirse kullanıcı
|
||||
// gerçek bir bağlantıya tıklayarak açabilsin (bağlantı tıklaması engellenmez).
|
||||
const [ekapUrl, setEkapUrl] = useState(null);
|
||||
@@ -30,6 +31,7 @@ export const TenderDocuments = ({ tender }) => {
|
||||
const [listeHata, setListeHata] = useState(null);
|
||||
const [listeBekliyor, setListeBekliyor] = useState(false);
|
||||
const listeIstegi = useRef(0);
|
||||
const onizlemeIstegi = useRef(0);
|
||||
const dosyaRef = useRef(null);
|
||||
|
||||
const belgeleriYukle = useCallback(async () => {
|
||||
@@ -45,7 +47,7 @@ export const TenderDocuments = ({ tender }) => {
|
||||
if (request === listeIstegi.current) setListeBekliyor(false);
|
||||
}
|
||||
}, [ikn]);
|
||||
useEffect(() => { setBelgeler([]); belgeleriYukle(); return () => { listeIstegi.current++; }; }, [belgeleriYukle]);
|
||||
useEffect(() => { setBelgeler([]); setMetinOnizleme(null); belgeleriYukle(); return () => { listeIstegi.current++; onizlemeIstegi.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.
|
||||
@@ -92,6 +94,18 @@ export const TenderDocuments = ({ tender }) => {
|
||||
const okunabilirMi = readableDocument;
|
||||
const belgeMetni = readStoredDocument;
|
||||
|
||||
const metniIncele = async (b) => {
|
||||
setBusy('metin'); setError(null); setMetinOnizleme(null);
|
||||
const token = getToken();
|
||||
const request = ++onizlemeIstegi.current;
|
||||
try {
|
||||
const result = await inspectStoredDocument(b);
|
||||
if (token !== getToken() || request !== onizlemeIstegi.current) return;
|
||||
setMetinOnizleme({...result,name:b.ad});
|
||||
} catch (e) { if (token === getToken() && request === onizlemeIstegi.current) setError(e.message); }
|
||||
finally { if (token === getToken() && request === onizlemeIstegi.current) setBusy(null); }
|
||||
};
|
||||
|
||||
const kalemleriYenidenCikar = async (b) => {
|
||||
setBusy('kalem'); setError(null); setBilgi(null);
|
||||
try {
|
||||
@@ -373,6 +387,8 @@ export const TenderDocuments = ({ tender }) => {
|
||||
>
|
||||
Derin
|
||||
</button>
|
||||
{okunabilirMi(b.ad) && <button onClick={() => metniIncele(b)} disabled={busy !== null || !b.erisilebilir}
|
||||
aria-label={`${b.ad} metnini incele`} className="text-[10px] text-blue-700 disabled:opacity-40">Metni incele</button>}
|
||||
{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`}
|
||||
@@ -388,6 +404,17 @@ export const TenderDocuments = ({ tender }) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{metinOnizleme && <section aria-label="Okunan belge metni" className="border rounded-xl p-3 space-y-2">
|
||||
<h5 className="font-bold text-xs">{metinOnizleme.name} — okunan metin</h5>
|
||||
<p className="text-xs text-slate-600">
|
||||
{metinOnizleme.pageCount != null ? `${metinOnizleme.usedPages}/${metinOnizleme.pageCount} sayfa okundu. ` : 'Sunucuda okunan belge metni. '}
|
||||
{metinOnizleme.ocr ? `OCR kullanılan sayfalar: ${metinOnizleme.pages.filter(p=>p.method === 'ocr').map(p=>p.page).join(', ')}. ` : ''}
|
||||
Metni resmi dokümanla karşılaştırın; okunmuş olması doğruluğunun onaylandığı anlamına gelmez.
|
||||
</p>
|
||||
<pre className="max-h-80 overflow-auto whitespace-pre-wrap break-words text-xs">{metinOnizleme.text || 'Okunabilir metin bulunamadı.'}</pre>
|
||||
<button onClick={()=>setMetinOnizleme(null)} className="text-xs underline">Metin önizlemesini kapat</button>
|
||||
</section>}
|
||||
|
||||
{(ekapIhalesi || belgeler.length > 0) && (
|
||||
<div className="bg-linear-to-r from-slate-900 via-indigo-950 to-slate-900 rounded-xl p-4 space-y-2.5">
|
||||
<div className="flex items-start gap-2.5">
|
||||
|
||||
@@ -3,7 +3,7 @@ import { reportDocumentReadError } from './account.js';
|
||||
|
||||
export const readableDocument = (name) => /\.(pdf|txt|md|docx|xlsx|zip)$/i.test(String(name || ''));
|
||||
|
||||
export async function readStoredDocument(document) {
|
||||
export async function inspectStoredDocument(document) {
|
||||
if (!readableDocument(document.ad)) throw new Error('Bu dosya biçiminden metin çıkarımı desteklenmiyor.');
|
||||
const office = /\.(docx|xlsx|zip)$/i.test(document.ad);
|
||||
const response = await fetch(`/api/me/documents/${office ? 'text' : 'download'}?id=${encodeURIComponent(document.id)}`, {
|
||||
@@ -12,14 +12,16 @@ export async function readStoredDocument(document) {
|
||||
if (office) {
|
||||
const result = await response.json().catch(() => ({}));
|
||||
if (!response.ok || !result.success) throw new Error(result.error || 'Belge metni alınamadı.');
|
||||
return result.metin;
|
||||
return {text:result.metin,format:document.ad.split('.').pop()};
|
||||
}
|
||||
if (!response.ok) throw new Error('Belge indirilemedi.');
|
||||
const blob = await response.blob();
|
||||
const { extractText } = await import('./pdfText');
|
||||
return (await extractText(new File([blob],document.ad,{type:document.mime || 'application/octet-stream'}))).text;
|
||||
return extractText(new File([blob],document.ad,{type:document.mime || 'application/octet-stream'}));
|
||||
}
|
||||
|
||||
export async function readStoredDocument(document) { return (await inspectStoredDocument(document)).text; }
|
||||
|
||||
export async function retryDocumentExtraction(document) {
|
||||
if (document.tur !== 'ihale' || !document.ikn) throw new Error('İhaleye bağlı ihale dokümanı gerekli.');
|
||||
const token = getToken();
|
||||
|
||||
Reference in New Issue
Block a user