Honor explicitly supplied sources and disclose draft source changes

This commit is contained in:
2026-09-06 10:18:28 +03:00
parent 2b9e1953e4
commit c3ec8c8ba8
4 changed files with 42 additions and 7 deletions
+9 -4
View File
@@ -32,6 +32,7 @@ const OfferAssistantSession = ({ tender }) => {
const post = (url, body, documents) => requestOfferAssistant({url, body, readToken:getToken, isCurrent:current, listDocuments:documents});
const [arac, setArac] = useState('sor');
const [metin, setMetin] = useState(''); // isteğe bağlı: yapıştırılan doküman metni
const [sourceName, setSourceName] = useState('');
const [soru, setSoru] = useState('');
const [cevap, setCevap] = useState(null);
const [taslak, setTaslak] = useState('');
@@ -39,6 +40,7 @@ const OfferAssistantSession = ({ tender }) => {
const [tur, setTur] = useState('tam');
const [bolumler, setBolumler] = useState([]);
const [draftReview, setDraftReview] = useState(null);
const [draftSource, setDraftSource] = useState(null);
const [mesgul, setMesgul] = useState(false);
const [hata, setHata] = useState(null);
@@ -59,7 +61,7 @@ const OfferAssistantSession = ({ tender }) => {
});
const uret = () => calistir(async () => {
const result = await post('/api/ai/teklif-taslak', {ikn:tender.ikn, tur, tender, text:metin || undefined}, listDocuments);
if (current()) { setBolumler(result.bolumler || []); setDraftReview(result.draftReview || null); }
if (current()) { setBolumler(result.bolumler || []); setDraftReview(result.draftReview || null); setDraftSource(metin); }
});
const kopyala = (t) => { if (current()) navigator.clipboard?.writeText(t).catch(() => {}); };
const dosyaOku = (f) => {
@@ -68,7 +70,8 @@ const OfferAssistantSession = ({ tender }) => {
const { extractText } = await import('../services/pdfText');
if (!current()) return;
const r = await extractText(f);
if (current()) { setMetin(r.text || ''); setCevap(null); setDenetim(null); }
if (typeof r.text !== 'string' || !r.text.trim()) throw new Error('Dosyada okunabilir metin bulunamadı. Önceki kaynak değiştirilmedi. Başka bir dosya seçin.');
if (current()) { setMetin(r.text); setSourceName(f.name); setCevap(null); setDenetim(null); }
});
};
@@ -86,10 +89,11 @@ const OfferAssistantSession = ({ tender }) => {
<Sekme id="denet" icon={ClipboardCheck}>Taslağı Denetle</Sekme>
<Sekme id="uret" icon={PenLine}>Taslak Üret</Sekme>
<label className="ml-auto inline-flex items-center gap-1.5 text-[11px] text-slate-600 cursor-pointer border border-slate-200 rounded-lg px-2 py-1 hover:bg-slate-50">
<Upload className="w-3.5 h-3.5" /> {metin ? `Doküman metni yüklendi (${metin.length.toLocaleString('tr-TR')} kr.)` : 'Doküman yükle (.pdf/.txt) — isteğe bağlı'}
<input type="file" disabled={mesgul} accept=".pdf,.txt,.md" className="hidden" onChange={(e) => dosyaOku(e.target.files?.[0])} />
<Upload className="w-3.5 h-3.5" /> {metin ? `Kaynak: ${sourceName} (${metin.length.toLocaleString('tr-TR')} kr.)` : 'Doküman yükle (.pdf/.txt) — isteğe bağlı'}
<input type="file" disabled={mesgul} accept=".pdf,.txt,.md" className="hidden" onChange={(e) => {const file=e.target.files?.[0];e.target.value='';dosyaOku(file);}} />
</label>
</div>
{metin && <button type="button" disabled={mesgul} className="text-xs text-blue-700 underline" onClick={() => {setMetin('');setSourceName('');setCevap(null);setDenetim(null);}}>Yüklenen kaynağı kaldır ve ilan kaynağını kullan</button>}
<p className="text-[11px] text-slate-500">Metin sağlanmazsa sunucu EKAP dokümanını / ilan metnini kullanır. Yanıtların kaynak parçalarını kontrol edin; AI yanıtları hata veya eksik bilgi içerebilir.</p>
{hata && <p role="alert" className="text-xs text-red-700 bg-red-50 border border-red-200 rounded-lg p-2">{hata}</p>}
@@ -155,6 +159,7 @@ const OfferAssistantSession = ({ tender }) => {
</button>
{bolumler.length > 0 && <button type="button" onClick={() => kopyala(bolumler.map((b) => `${b.baslik}\n\n${b.icerik}`).join('\n\n\n'))} className="text-xs text-slate-600 border border-slate-200 rounded-lg px-2 py-1.5 flex items-center gap-1 hover:bg-slate-50"><Copy className="w-3.5 h-3.5" /> Tümünü kopyala</button>}
</div>
{bolumler.length > 0 && draftSource !== metin && <p role="alert" className="text-xs text-amber-800">Kaynak değişti. Aşağıdaki taslak önceki kaynakla üretildi; düzenlemeleriniz korundu. Yeni kaynakla karşılaştırın veya yeniden üretin.</p>}
{draftReview && <div role="status" className="rounded-lg border border-amber-200 bg-amber-50 p-3 text-xs text-amber-900">
<p className="font-semibold">{draftReview.status === 'partial' ? 'Kısmi taslak — bölüm kontrolü gerekli' : 'Taslak — kullanıcı kontrolü gerekli'}</p>
<p>{draftReview.note}</p>