79 lines
5.4 KiB
React
79 lines
5.4 KiB
React
import React,{useEffect,useRef,useState} from 'react';
|
||
import {getDocumentTextReview,saveDocumentTextReview} from '../services/account';
|
||
import {getToken} from '../services/auth';
|
||
|
||
export function DocumentTextReview({documentId,extractedText,onAnalyze}) {
|
||
const [source,setSource]=useState(null);
|
||
const [text,setText]=useState(extractedText || '');
|
||
const [note,setNote]=useState('');
|
||
const [compared,setCompared]=useState(false);
|
||
const [busy,setBusy]=useState(false);
|
||
const [error,setError]=useState(null);
|
||
const [saved,setSaved]=useState(null);
|
||
const [analyzing,setAnalyzing]=useState(false);
|
||
const live=useRef(true);
|
||
const sequence=useRef(0);
|
||
const load=async()=>{
|
||
const request=++sequence.current, token=getToken();
|
||
setBusy(true);setError(null);
|
||
try {
|
||
const data=await getDocumentTextReview(documentId);
|
||
if(!live.current || request!==sequence.current || token!==getToken())return;
|
||
setSource(data);
|
||
// A stale source must be compared afresh; do not seed from an old correction.
|
||
setText(!data.stale && data.review ? data.review.corrected_text : extractedText || '');
|
||
setNote(!data.stale && data.review ? data.review.review_note : '');
|
||
setCompared(false);setSaved(null);
|
||
} catch(e) {if(live.current && request===sequence.current && token===getToken())setError(e.message);}
|
||
finally {if(live.current && request===sequence.current && token===getToken())setBusy(false);}
|
||
};
|
||
useEffect(()=>{live.current=true;load();return()=>{live.current=false;sequence.current++;};},[documentId]);
|
||
const save=async()=>{
|
||
if(!source || busy)return;
|
||
const token=getToken();setBusy(true);setError(null);setSaved(null);
|
||
try {
|
||
const result=await saveDocumentTextReview(documentId,{fileHash:source.fileHash,
|
||
expectedRevision:source.review?.revision || 0,correctedText:text,note,allPagesCompared:compared});
|
||
if(!live.current || token!==getToken())return;
|
||
setSource(result);setSaved(result.review);setCompared(false);
|
||
} catch(e) {if(live.current && token===getToken())setError(e.message);}
|
||
finally {if(live.current && token===getToken())setBusy(false);}
|
||
};
|
||
const analyze=async()=>{
|
||
if(!source?.review || source.stale || busy || analyzing)return;
|
||
const token=getToken();setAnalyzing(true);setError(null);
|
||
try {await onAnalyze({documentId,revision:source.review.revision});}
|
||
catch(e) {if(live.current && token===getToken())setError(e.message);}
|
||
finally {if(live.current && token===getToken())setAnalyzing(false);}
|
||
};
|
||
return <section aria-label="Belge metni incelemesi" className="border-t pt-3 space-y-2">
|
||
<h5 className="font-bold text-sm">Metni karşılaştır ve düzelt</h5>
|
||
<p className="text-xs text-slate-600">Belgenin tüm sayfalarını açıp metin, miktar ve birimleri karşılaştırın. Bu kayıt kullanıcı incelemesidir; resmî onay veya e-imza değildir. Kaydetmek tek başına teklif dosyasını hazır yapmaz.</p>
|
||
{source?.stale && <p role="alert" className="text-xs text-amber-800">Kaynak dosya değişmiş. Önceki inceleme geçerli değil; tüm sayfaları yeniden karşılaştırın.</p>}
|
||
{source?.review && <p className="text-xs text-slate-500">Son kayıt: sürüm {source.review.revision} · {new Date(source.review.reviewed_at).toLocaleString('tr-TR')}</p>}
|
||
<label className="block text-xs font-semibold">Kontrol edilen metin
|
||
<textarea value={text} onChange={e=>{setText(e.target.value);setCompared(false);setSaved(null);}} disabled={busy || analyzing}
|
||
className="block w-full h-64 border rounded p-2 font-mono font-normal mt-1" />
|
||
</label>
|
||
<label className="block text-xs font-semibold">Karşılaştırılan sayfalar ve düzeltmeler
|
||
<textarea value={note} onChange={e=>{setNote(e.target.value);setCompared(false);setSaved(null);}} disabled={busy || analyzing}
|
||
placeholder="Örnek: Sayfa 1–3 karşılaştırıldı; 2. sayfadaki miktar 100 olarak düzeltildi."
|
||
className="block w-full border rounded p-2 font-normal mt-1" />
|
||
</label>
|
||
<label className="flex gap-2 text-xs"><input type="checkbox" checked={compared} onChange={e=>setCompared(e.target.checked)} disabled={busy || analyzing}/>
|
||
Bu metni kaynak dosyanın tüm sayfalarıyla karşılaştırdım.</label>
|
||
<button onClick={save} disabled={busy || analyzing || !source || !compared || text.trim().length<40 || note.trim().length<20}
|
||
className="text-xs bg-blue-700 text-white px-3 py-2 rounded disabled:opacity-40">{busy ? 'İşleniyor…' : 'İncelemeyi kaydet'}</button>
|
||
{onAnalyze && source?.review && !source.stale && <button onClick={analyze}
|
||
disabled={busy || analyzing || text.trim()!==source.review.corrected_text || note.trim()!==source.review.review_note}
|
||
className="ml-2 text-xs bg-amber-500 px-3 py-2 rounded disabled:opacity-40">
|
||
{analyzing ? 'İncelenmiş metin analiz ediliyor…' : 'Kaydedilmiş incelemeyi analiz et'}
|
||
</button>}
|
||
{error && <div role="alert" className="text-xs text-red-700">{error}
|
||
<p>Metin taslağınız bu ekranda korunuyor. Sürüm değiştiyse taslağı kopyalayıp güncel kaydı yükleyin.</p>
|
||
<button onClick={load} disabled={busy || analyzing} className="underline">Güncel kaydı yükle (taslağı değiştirir)</button>
|
||
</div>}
|
||
{saved && <p role="status" className="text-xs text-green-800">İnceleme sürüm {saved.revision} sunucuda kaydedildi. Analiz ve teklif kontrolleri ayrıca tamamlanmalıdır.</p>}
|
||
</section>;
|
||
}
|