fix: withhold print action until bid validation finishes

This commit is contained in:
2026-09-06 15:08:56 +03:00
parent 1e36c17487
commit fb2c2b120e
6 changed files with 72 additions and 8 deletions
+19
View File
@@ -0,0 +1,19 @@
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {belgeYazdir} from '../src/services/bidDocs.js';
test('validation window has no print action until replaced by the prepared document',()=>{
let html='',focused=0;
const popup={closed:false,document:{open(){html='';},write(value){html+=value;},close(){}},focus(){focused++;}};
assert.equal(belgeYazdir('Dosya kontrolü','<p role="status">Belgeler kontrol ediliyor…</p>',popup,{pending:true}),popup);
assert.match(html,/Belgeler kontrol ediliyor/);
assert.doesNotMatch(html,/<button|window\.print\(/);
assert.equal(belgeYazdir('Teklif <Cetveli>','<h1>Hazır cetvel</h1>',popup),popup);
assert.match(html,/<title>Teklif &lt;Cetveli&gt;<\/title>/);
assert.match(html,/Hazır cetvel/);
assert.match(html,/<button onclick="window\.print\(\)"/);
assert.doesNotMatch(html,/Belgeler kontrol ediliyor/);
assert.equal(focused,2);
popup.closed=true;
assert.throws(()=>belgeYazdir('Teklif','',popup),/penceresi kapatıldı/);
});
+19 -3
View File
@@ -6,6 +6,8 @@ import {tmpdir} from 'node:os';
import {join} from 'node:path';
import {randomBytes} from 'node:crypto';
import {createServer as netServer} from 'node:net';
import {readyBid} from './fixtures/ready-bid.mjs';
import {preparationSummary} from '../src/services/preparationSummary.js';
const dir=mkdtempSync(join(tmpdir(),'ihaleist-browser-'));
process.env.AUTH_SECRET=randomBytes(32).toString('hex');
const {signToken}=await import('../server/auth.mjs');
@@ -15,6 +17,8 @@ const keyUser={email:'keys-browser-qa@example.test',name:'API QA',isAdmin:true};
const keyToken=signToken(keyUser);
let apiKeyListFailure=false;
let briefFailure='none';
let holdDocuments=false;
const pendingDocuments=new Set();
process.env.IHALEIST_USER_DATA_DIR=dir;
const {createBriefOutbox}=await import('../server/brief-outbox.mjs');
const qaBriefOutbox=createBriefOutbox(dir);
@@ -96,17 +100,29 @@ const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{targ
res.setHeader('Content-Type','application/json');res.end(JSON.stringify({success:true,job:qaJobs.getAiJob(user.email,created.id)}));
}catch(error){res.writeHead(400,{'Content-Type':'application/json'});res.end(JSON.stringify({success:false,error:error.message}));}});return;
}
if(req.url==='/qa-source-update') {
if(req.url?.startsWith('/qa-output-documents?')&&req.method==='POST') {
const mode=new URL(req.url,'http://localhost').searchParams.get('mode');
if(mode==='hold')holdDocuments=true;
if(mode==='release'){holdDocuments=false;for(const pending of pendingDocuments){pending.writeHead(200,{'Content-Type':'application/json'});pending.end(JSON.stringify({success:true,belgeler:[]}));}pendingDocuments.clear();}
res.end('Belge yanıtı: '+mode);return;
}
if(req.url==='/api/me/documents'&&req.method==='GET'&&holdDocuments){pendingDocuments.add(res);res.on('close',()=>pendingDocuments.delete(res));return;}
if(req.url==='/qa-source-update'||req.url==='/qa-source-update?ready=1') {
const selected=req.url.endsWith('?ready=1')?readyBid(tenders[0]):tenders[0];
if(selected.bidDraft){const summary=preparationSummary(selected,{loading:false,error:null,documents:[]});if(summary.issues.length)throw new Error('QA ready fixture: '+JSON.stringify(summary.issues));}
const html=`<html lang="tr"><head><meta name="viewport" content="width=device-width,initial-scale=1"></head><body><div id="root"></div><script type="module">
import React from '/node_modules/.vite/deps/react.js';
import ReactDOM from '/node_modules/.vite/deps/react-dom_client.js';
import {AppProvider,useApp} from '/src/context/AppContext.jsx';
import {BidPackageBuilder} from '/src/components/BidPackageBuilder.jsx';
import '/src/index.css';
const selected=${JSON.stringify(tenders[0])};
function Probe(){const {setTenders}=useApp();return React.createElement('main',{},
const selected=${JSON.stringify(selected)};
function Probe(){const {setTenders}=useApp();const [status,setStatus]=React.useState('');const control=async mode=>setStatus(await (await fetch('/qa-output-documents?mode='+mode,{method:'POST'})).text());return React.createElement('main',{},
React.createElement('h1',{},'Yerel kaynak güncelleme testi'),
React.createElement('p',{},'Yalnız sentetik veri. Açık dosya korunurken güncel kaynak değişir.'),
React.createElement('button',{onClick:()=>control('hold')},'Belge yanıtını beklet'),
React.createElement('button',{onClick:()=>control('release')},'Belge yanıtını serbest bırak'),
React.createElement('p',{role:'status'},status),
React.createElement('button',{onClick:()=>setTenders([{...selected,id:'qa-rotated-source',baslik:'QA Güncellenmiş alım',kazananFirma:'QA Kazanan'}])},'Aynı İKN için yeni kayıt ID ve kazanan yayımla'),
React.createElement(BidPackageBuilder,{tender:selected}));}
ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(AppProvider,{},React.createElement(Probe)));
+21
View File
@@ -0,0 +1,21 @@
// Synthetic UI fixture, never a claim of official tender eligibility.
import {sourceRows,bidSourceOptions,bidSourceSnapshot} from '../../src/services/bidSource.js';
import {documentKey,HEALTH_CHECKS} from '../../src/services/preparationChecks.js';
export function readyBid(tender) {
const document={document:'QA isteğe bağlı belge',mandatory:false,note:'Yalnız sentetik senaryo'};
const analysis={documentChecklist:[document]},itemSource={};
const partialBid={enabled:false,source:'',selected:[],assignments:{}};
const options=bidSourceOptions(tender,analysis,itemSource);
const rows=sourceRows(tender,analysis,options.selected || 'tender');
const sourceSnapshot=JSON.stringify([bidSourceSnapshot(tender,analysis,rows),partialBid,itemSource,options.signature]);
return {...tender,bidDraft:{
firma:{firmaUnvan:'QA Sentetik Laboratuvar',adres:'QA Test Adresi',vergiDairesi:'QA',vergiNo:'0000000000',yetkiliAd:'QA Test Kullanıcısı',yetkiliGorev:'Test',telefon:'0000000000',eposta:'qa@example.test'},
teklifTuru:'birim',gecerlilikGun:'120',kisim:'',goturuBedel:'',priceSchema:1,
pricesBySource:Object.fromEntries(rows.map(row=>[row.priceKey,'125.50'])),
sourceSnapshot,reviewedSource:sourceSnapshot,manualDocuments:[],partialBid,itemSource,analysisSource:analysis,
checks:{documents:{[documentKey(document)]:{status:'notApplicable',evidence:'QA senaryosunda istenmiyor.'}},
products:Object.fromEntries(rows.map(row=>[row.priceKey,{status:'suitable',product:'QA sentetik ürün',source:'QA teknik madde 1',
...Object.fromEntries(HEALTH_CHECKS.map(([key])=>[key,{status:'notApplicable',evidence:'QA senaryosu.'}]))}]))},
}};
}