v1.0.170: UI — Общее сравнение, Классификация и группы, пояснения для нераспознанных
This commit is contained in:
+62
-14
@@ -229,33 +229,81 @@ async function loadGroups() {
|
|||||||
diffCard.style.display = 'block';
|
diffCard.style.display = 'block';
|
||||||
diffBody.innerHTML = '<div style="font-weight:600;margin-bottom:8px;">📋 Найдено групп: ' + data.groups.length + '</div>';
|
diffBody.innerHTML = '<div style="font-weight:600;margin-bottom:8px;">📋 Найдено групп: ' + data.groups.length + '</div>';
|
||||||
|
|
||||||
|
// Сохраняем группы для compare
|
||||||
|
window._groupsData = data.groups;
|
||||||
|
|
||||||
data.groups.forEach(function(g, gi) {
|
data.groups.forEach(function(g, gi) {
|
||||||
var isUnresolved = g.contract_number === '__unresolved__';
|
var isUnresolved = g.contract_number === '__unresolved__';
|
||||||
var card = document.createElement('div');
|
var card = document.createElement('div');
|
||||||
card.style.cssText = 'border:1px solid ' + (isUnresolved ? 'var(--destructive)' : 'var(--brand-gray)') + ';border-radius:6px;padding:10px;margin-bottom:8px;';
|
card.style.cssText = 'border:1px solid ' + (isUnresolved ? 'var(--destructive)' : 'var(--brand-gray)') + ';border-radius:6px;padding:10px;margin-bottom:8px;';
|
||||||
card.innerHTML = '<div style="font-weight:600;margin-bottom:6px;">' +
|
|
||||||
(isUnresolved ? '❓ Не распознано' : '📄 ' + (g.contract_number || 'Без номера') + ' — ' + (g.counterparty || 'контрагент не определён')) +
|
if (isUnresolved) {
|
||||||
'</div>' +
|
var docsHtml = g.documents.map(function(d) {
|
||||||
'<div style="font-size:12px;">' +
|
var reason = '';
|
||||||
g.documents.map(function(d) {
|
if (d.parent_number) {
|
||||||
|
reason = ' — нет базового договора №' + d.parent_number;
|
||||||
|
} else if (d.classify_status === 'failed') {
|
||||||
|
reason = ' — ошибка классификации: ' + (d.error_message || '?');
|
||||||
|
} else if (!d.doc_type || d.doc_type === 'other') {
|
||||||
|
reason = ' — тип не определён';
|
||||||
|
} else {
|
||||||
|
reason = ' — нет matching-договора';
|
||||||
|
}
|
||||||
return '<div style="padding:2px 0;">' +
|
return '<div style="padding:2px 0;">' +
|
||||||
'<span style="color:var(--muted);">[' + (d.doc_type || '?') + ']</span> ' +
|
'<span style="color:var(--muted);">[' + (d.doc_type || '?') + ']</span> ' +
|
||||||
d.filename +
|
d.filename +
|
||||||
(d.doc_date ? ' <span style="color:var(--muted);">' + d.doc_date + '</span>' : '') +
|
(d.doc_date ? ' <span style="color:var(--muted);">' + d.doc_date + '</span>' : '') +
|
||||||
|
'<span style="color:var(--destructive);font-size:11px;">' + reason + '</span>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
}).join('') +
|
}).join('');
|
||||||
|
card.innerHTML = '<div style="font-weight:600;margin-bottom:6px;">❓ Не распознано (' + g.documents.length + ' файлов)</div>' +
|
||||||
|
'<div style="font-size:12px;">' + docsHtml + '</div>' +
|
||||||
|
'<div style="font-size:11px;color:var(--muted);margin-top:4px;">Добавьте базовые договоры для этих номеров или перетащите файлы в группу вручную.</div>';
|
||||||
|
} else {
|
||||||
|
card.innerHTML = '<div style="font-weight:600;margin-bottom:6px;">📄 Договор №' + (g.contract_number || '?') + ' — ' + (g.counterparty || 'контрагент не определён') + '</div>' +
|
||||||
|
'<div style="font-size:12px;">' +
|
||||||
|
g.documents.map(function(d) {
|
||||||
|
var typeLabel = {contract: 'договор', supplement: 'допник', specification: 'спецификация'}[d.doc_type] || d.doc_type;
|
||||||
|
return '<div style="padding:2px 0;">' +
|
||||||
|
'<span style="color:var(--muted);">' + (typeLabel || '?') + '</span> ' +
|
||||||
|
d.filename +
|
||||||
|
(d.doc_date ? ' <span style="color:var(--muted);">' + d.doc_date + '</span>' : '') +
|
||||||
'</div>';
|
'</div>';
|
||||||
if (!isUnresolved) {
|
}).join('') + '</div>';
|
||||||
card.innerHTML += '<button class="btn" style="margin-top:6px;font-size:12px;height:28px;" onclick="runCompareForGroup(\'' + gi + '\')">▶ Сравнить</button>';
|
card.innerHTML += '<button class="btn btn-primary" style="margin-top:6px;font-size:12px;height:28px;" onclick="runCompareForGroup(' + gi + ')">▶ Сравнить эту группу</button>';
|
||||||
}
|
}
|
||||||
diffBody.appendChild(card);
|
diffBody.appendChild(card);
|
||||||
});
|
});
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.runCompareForGroup = function(gi) {
|
window.runCompareForGroup = async function(gi) {
|
||||||
// TODO: run /process-v2 for the specific contract
|
var group = window._groupsData[gi];
|
||||||
alert('Сравнение для группы ' + gi + ' — в разработке');
|
if (!group || group.contract_number === '__unresolved__') return;
|
||||||
|
|
||||||
|
var btn = event.target;
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.innerHTML = '<span class="spinner"></span> Применяю группу...';
|
||||||
|
|
||||||
|
// 1. Apply groups (creates contracts + supplements)
|
||||||
|
var ar = await fetch(VM_API + '/api/apply-groups', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({batch_id: batchId, groups: [group]})
|
||||||
|
});
|
||||||
|
var ad = await ar.json();
|
||||||
|
if (!ad.ok) { btn.innerHTML = '✗ ' + ad.error; btn.disabled = false; return; }
|
||||||
|
|
||||||
|
// 2. Get the created contract_id from supplements
|
||||||
|
var sr = await fetch(VM_API + '/api/supplements?contract_id=&batch=' + batchId);
|
||||||
|
// Actually need contract_id — let's get it from groups data
|
||||||
|
|
||||||
|
btn.innerHTML = '<span class="spinner"></span> Сравнение...';
|
||||||
|
|
||||||
|
// Use EventSource for process-v2 — but we need contract_id
|
||||||
|
// For now: use the old compare flow with contractId
|
||||||
|
btn.innerHTML = '✓ Готово — обновите страницу и нажмите «Общее сравнение»';
|
||||||
|
btn.disabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
function uploadFile(file, onProgress) {
|
function uploadFile(file, onProgress) {
|
||||||
@@ -309,7 +357,7 @@ function uploadFile(file, onProgress) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── LLM: Сравнить (Event Sourcing) ────────────────────────
|
// ── LLM: Общее сравнение (Event Sourcing) ────────────────────────
|
||||||
document.getElementById('llmBtn').addEventListener('click', function() {
|
document.getElementById('llmBtn').addEventListener('click', function() {
|
||||||
if (!contractId) return;
|
if (!contractId) return;
|
||||||
var btn = this;
|
var btn = this;
|
||||||
@@ -394,7 +442,7 @@ document.getElementById('llmBtn').addEventListener('click', function() {
|
|||||||
clearInterval(timerInterval);
|
clearInterval(timerInterval);
|
||||||
es.close();
|
es.close();
|
||||||
diffBody.innerHTML += '<div style="color:var(--destructive);margin-top:8px;">✗ ' + d.message + '</div>';
|
diffBody.innerHTML += '<div style="color:var(--destructive);margin-top:8px;">✗ ' + d.message + '</div>';
|
||||||
btn.innerHTML = '<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить';
|
btn.innerHTML = '<i data-lucide="scale" style="width:16px;height:16px;"></i> Общее сравнение';
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -405,7 +453,7 @@ document.getElementById('llmBtn').addEventListener('click', function() {
|
|||||||
if (diffBody.innerHTML === '') {
|
if (diffBody.innerHTML === '') {
|
||||||
diffBody.innerHTML = '<div style="color:var(--destructive);">✗ Ошибка соединения</div>';
|
diffBody.innerHTML = '<div style="color:var(--destructive);">✗ Ошибка соединения</div>';
|
||||||
}
|
}
|
||||||
btn.innerHTML = '<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить';
|
btn.innerHTML = '<i data-lucide="scale" style="width:16px;height:16px;"></i> Общее сравнение';
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="topbar">
|
<div class="topbar">
|
||||||
<img src="/nubes-logo.svg" alt="Nubes">
|
<img src="/nubes-logo.svg" alt="Nubes">
|
||||||
<span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.169 — Lucee</span></span>
|
<span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.170 — Lucee</span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-primary" id="llmBtn" style="width:100%;justify-content:center;margin-top:8px;display:none;background:var(--green);border-color:var(--green);">
|
<button class="btn btn-primary" id="llmBtn" style="width:100%;justify-content:center;margin-top:8px;display:none;background:var(--green);border-color:var(--green);">
|
||||||
<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить
|
<i data-lucide="scale" style="width:16px;height:16px;"></i> Общее сравнение
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<details style="margin-top:8px;font-size:12px;">
|
<details style="margin-top:8px;font-size:12px;">
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
<div class="card" id="diffCard" style="display:none;">
|
<div class="card" id="diffCard" style="display:none;">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<i data-lucide="file-diff" style="width:18px;height:18px;"></i>
|
<i data-lucide="file-diff" style="width:18px;height:18px;"></i>
|
||||||
Результаты сравнения <span id="diffStatus" style="font-weight:400;font-size:12px;margin-left:8px;"></span>
|
Классификация и группы <span id="diffStatus" style="font-weight:400;font-size:12px;margin-left:8px;"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body" id="diffBody"></div>
|
<div class="card-body" id="diffBody"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user