v1.0.170: UI — Общее сравнение, Классификация и группы, пояснения для нераспознанных

This commit is contained in:
2026-06-24 13:17:50 +04:00
parent abba9c4f1d
commit d848caacb2
2 changed files with 66 additions and 18 deletions
+62 -14
View File
@@ -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();
}; };
+3 -3
View File
@@ -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>