51 lines
3.1 KiB
JavaScript
51 lines
3.1 KiB
JavaScript
export const analysisArrayFields = ['qualificationCriteria','criticalRequirements','risksAndPenalties','disqualificationRisks','criticalDetails','itemsAndSpecs','documentChecklist','deadlines','costDrivers','roadmap','questionsForAdministration'];
|
||
const stringLists = new Set(['qualificationCriteria','criticalRequirements','disqualificationRisks','costDrivers','questionsForAdministration']);
|
||
const shapes = {
|
||
itemsAndSpecs: ['name','quantity','note'], documentChecklist: ['document','note'],
|
||
risksAndPenalties: ['level','clause','text'], criticalDetails: ['level','title','where','detail','whyItMatters'],
|
||
deadlines: ['label','value','note'], roadmap: ['phase','action','owner','timing','output'],
|
||
};
|
||
const object = v => v && typeof v === 'object' && !Array.isArray(v);
|
||
const fail = path => { throw new Error(`Analiz alanının biçimi geçersiz: ${path}`); };
|
||
const text = (v,path) => { if (v != null && typeof v !== 'string') fail(path); };
|
||
const number = (v,path) => { if (v != null && (typeof v !== 'number' || !Number.isFinite(v) || v < 0)) fail(path); };
|
||
|
||
export function validateAnalysisShape(result) {
|
||
if (!object(result)) fail('result');
|
||
const issues = [];
|
||
for (const field of ['summary','scopeOfWork']) text(result[field],field);
|
||
for (const field of analysisArrayFields) {
|
||
if (result[field] == null) continue;
|
||
if (!Array.isArray(result[field])) fail(field);
|
||
result[field].forEach((row,i) => {
|
||
const path = `${field}.${i}`;
|
||
if (stringLists.has(field)) { if (typeof row !== 'string' || !row.trim()) fail(path); return; }
|
||
if (!object(row)) fail(path);
|
||
for (const key of shapes[field]) text(row[key],`${path}.${key}`);
|
||
if (field === 'itemsAndSpecs') {
|
||
if (!row.name?.trim() || !row.quantity?.trim()) issues.push(`${path}: kalem adı veya miktar/birim eksik.`);
|
||
if (row.keySpecs != null && (!Array.isArray(row.keySpecs) || row.keySpecs.some(v => typeof v !== 'string'))) fail(`${path}.keySpecs`);
|
||
for (const key of ['estimatedUnitPrice','estimatedTotal']) number(row[key],`${path}.${key}`);
|
||
}
|
||
if (field === 'documentChecklist') {
|
||
if (!row.document?.trim() || row.mandatory == null) issues.push(`${path}: belge adı veya zorunluluk bilgisi eksik.`);
|
||
if (row.mandatory != null && typeof row.mandatory !== 'boolean') fail(`${path}.mandatory`);
|
||
}
|
||
if (field === 'roadmap' && row.step != null && (!Number.isInteger(row.step) || row.step < 1)) fail(`${path}.step`);
|
||
});
|
||
}
|
||
if (result.bidAnalysis != null) {
|
||
if (!object(result.bidAnalysis)) fail('bidAnalysis');
|
||
for (const key of ['estimatedCost','recommendedBid','minBidLimit','maxBidLimit','winProbability']) number(result.bidAnalysis[key],`bidAnalysis.${key}`);
|
||
if (result.bidAnalysis.winProbability > 100) fail('bidAnalysis.winProbability');
|
||
for (const key of ['competitionLevel','notes']) text(result.bidAnalysis[key],`bidAnalysis.${key}`);
|
||
}
|
||
if (result.citations != null) {
|
||
if (!Array.isArray(result.citations)) fail('citations');
|
||
result.citations.forEach((c,i) => {
|
||
if (!object(c) || typeof c.path !== 'string' || typeof c.quote !== 'string') fail(`citations.${i}`);
|
||
});
|
||
}
|
||
return issues;
|
||
}
|