eksik teklif taslagini acikca etiketle
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { AiJobHistory } from './AiJobHistory';
|
import { AiJobHistory } from './AiJobHistory';
|
||||||
import { validateOfferResponse } from '../services/offerResponse';
|
import { draftReviewLabel, validateOfferResponse } from '../services/offerResponse';
|
||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { MessageSquareText, ClipboardCheck, PenLine, Loader2, Copy, Upload } from 'lucide-react';
|
import { MessageSquareText, ClipboardCheck, PenLine, Loader2, Copy, Upload } from 'lucide-react';
|
||||||
import { getToken } from '../services/auth';
|
import { getToken } from '../services/auth';
|
||||||
@@ -180,8 +180,8 @@ const OfferAssistantSession = ({ tender }) => {
|
|||||||
{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>}
|
{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>
|
</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>}
|
{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">
|
{draftReview && <div role={draftReview.status === 'partial' ? 'alert' : '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 className="font-semibold">{draftReviewLabel(draftReview)}</p>
|
||||||
<p>{draftReview.note}</p>
|
<p>{draftReview.note}</p>
|
||||||
{draftReview.missingSections?.length > 0 && <p>Modelin tamamlamadığı bölümler: {draftReview.missingSections.join(', ')}. Aşağıdaki DOLDURUN alanlarını düzenleyin.</p>}
|
{draftReview.missingSections?.length > 0 && <p>Modelin tamamlamadığı bölümler: {draftReview.missingSections.join(', ')}. Aşağıdaki DOLDURUN alanlarını düzenleyin.</p>}
|
||||||
{draftReview.extraSections?.length > 0 && <p>İstenen başlıklara eşleşmeyen ek bölümler: {draftReview.extraSections.join(', ')}.</p>}
|
{draftReview.extraSections?.length > 0 && <p>İstenen başlıklara eşleşmeyen ek bölümler: {draftReview.extraSections.join(', ')}.</p>}
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ const object = value => value !== null && typeof value === 'object' && !Array.is
|
|||||||
const text = value => typeof value === 'string';
|
const text = value => typeof value === 'string';
|
||||||
const texts = value => Array.isArray(value) && value.every(text);
|
const texts = value => Array.isArray(value) && value.every(text);
|
||||||
const optionalText = value => value == null || text(value);
|
const optionalText = value => value == null || text(value);
|
||||||
|
export function draftReviewLabel(review) {
|
||||||
|
return review?.status === 'partial'
|
||||||
|
? 'EKSİK TASLAK — TEKLİF OLARAK KULLANILAMAZ'
|
||||||
|
: 'Taslak — kullanıcı kontrolü gerekli';
|
||||||
|
}
|
||||||
export function validateOfferResponse(url, data) {
|
export function validateOfferResponse(url, data) {
|
||||||
let valid = object(data) && data.success === true;
|
let valid = object(data) && data.success === true;
|
||||||
if (valid && url === '/api/ai/belge-sor') {
|
if (valid && url === '/api/ai/belge-sor') {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {test} from 'node:test';
|
import {test} from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import {validateOfferResponse} from '../src/services/offerResponse.js';
|
import {draftReviewLabel,validateOfferResponse} from '../src/services/offerResponse.js';
|
||||||
import {qualifyOfferDraft} from '../server/offer-draft.mjs';
|
import {qualifyOfferDraft} from '../server/offer-draft.mjs';
|
||||||
import {qualifyOfferAudit} from '../server/offer-audit.mjs';
|
import {qualifyOfferAudit} from '../server/offer-audit.mjs';
|
||||||
import {qualifyQuestionAnswer} from '../server/document-question.mjs';
|
import {qualifyQuestionAnswer} from '../server/document-question.mjs';
|
||||||
@@ -16,3 +16,7 @@ test('nested fields that would crash the view are rejected at the boundary',()=>
|
|||||||
['teklif-denetim',{maddeler:[{gereklilik:'Numune',durum:'unexpected'}],eksikler:[],uyum_yuzde:100,belirsizSayisi:0}],
|
['teklif-denetim',{maddeler:[{gereklilik:'Numune',durum:'unexpected'}],eksikler:[],uyum_yuzde:100,belirsizSayisi:0}],
|
||||||
['teklif-taslak',{bolumler:[{baslik:'Kapak',icerik:{text:'invalid'}}]}]]) assert.throws(()=>validateOfferResponse('/api/ai/'+url,{success:true,...data}),/beklenen biçimde/);
|
['teklif-taslak',{bolumler:[{baslik:'Kapak',icerik:{text:'invalid'}}]}]]) assert.throws(()=>validateOfferResponse('/api/ai/'+url,{success:true,...data}),/beklenen biçimde/);
|
||||||
});
|
});
|
||||||
|
test('partial draft output carries an explicit non-official offer warning',()=>{
|
||||||
|
assert.equal(draftReviewLabel({status:'partial'}),'EKSİK TASLAK — TEKLİF OLARAK KULLANILAMAZ');
|
||||||
|
assert.equal(draftReviewLabel({status:'draft'}),'Taslak — kullanıcı kontrolü gerekli');
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user