v1.0.163: classify UI — batch_id, Классифицировать button, group cards
This commit is contained in:
+84
-1
@@ -11,6 +11,7 @@ var fileInput = document.getElementById('fileInput');
|
||||
var fileTable = document.getElementById('fileTable');
|
||||
var fileQueue = [];
|
||||
var contractId = null;
|
||||
var batchId = crypto.randomUUID(); // классификация: привязка всех файлов сессии
|
||||
|
||||
function formatSize(bytes) {
|
||||
if (!bytes || bytes === 0) return '—';
|
||||
@@ -207,13 +208,94 @@ fileInput.addEventListener('change', async function() {
|
||||
|
||||
fileInput.value = '';
|
||||
fileInput.disabled = false;
|
||||
// Показать кнопки LLM после авто-парсинга
|
||||
// Показать кнопки после авто-парсинга
|
||||
var llmBtn = document.getElementById('llmBtn');
|
||||
llmBtn.style.display = 'flex';
|
||||
llmBtn.disabled = false;
|
||||
showClassifyBtn();
|
||||
lucide.createIcons();
|
||||
});
|
||||
|
||||
// ── Классификация ──────────────────────────────────────────
|
||||
function showClassifyBtn() {
|
||||
var btn = document.getElementById('classifyBtn');
|
||||
if (!btn) {
|
||||
btn = document.createElement('button');
|
||||
btn.id = 'classifyBtn';
|
||||
btn.className = 'btn btn-primary';
|
||||
btn.style.cssText = 'width:100%;justify-content:center;margin-top:8px;background:var(--brand-primary);border-color:var(--brand-primary);';
|
||||
btn.innerHTML = '<i data-lucide="tags" style="width:16px;height:16px;"></i> Классифицировать';
|
||||
btn.addEventListener('click', runClassify);
|
||||
document.getElementById('llmBtn').parentNode.insertBefore(btn, document.getElementById('llmBtn'));
|
||||
}
|
||||
btn.style.display = 'flex';
|
||||
btn.disabled = false;
|
||||
}
|
||||
|
||||
async function runClassify() {
|
||||
var btn = document.getElementById('classifyBtn');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner"></span> Классификация...';
|
||||
|
||||
try {
|
||||
var resp = await fetch(VM_API + '/api/classify-batch', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({batch_id: batchId})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
btn.innerHTML = '✓ ' + data.done + '/' + data.total + ' классифицировано';
|
||||
loadGroups();
|
||||
} else {
|
||||
btn.innerHTML = '✗ ' + (data.error || 'ошибка');
|
||||
btn.disabled = false;
|
||||
}
|
||||
} catch(e) {
|
||||
btn.innerHTML = '✗ ' + e.message;
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadGroups() {
|
||||
var resp = await fetch(VM_API + '/api/groups?batch=' + batchId);
|
||||
var data = await resp.json();
|
||||
if (!data.ok || !data.groups) return;
|
||||
|
||||
var diffBody = document.getElementById('diffBody');
|
||||
var diffCard = document.getElementById('diffCard');
|
||||
diffCard.style.display = 'block';
|
||||
diffBody.innerHTML = '<div style="font-weight:600;margin-bottom:8px;">📋 Найдено групп: ' + data.groups.length + '</div>';
|
||||
|
||||
data.groups.forEach(function(g, gi) {
|
||||
var isUnresolved = g.contract_number === '__unresolved__';
|
||||
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.innerHTML = '<div style="font-weight:600;margin-bottom:6px;">' +
|
||||
(isUnresolved ? '❓ Не распознано' : '📄 ' + (g.contract_number || 'Без номера') + ' — ' + (g.counterparty || 'контрагент не определён')) +
|
||||
'</div>' +
|
||||
'<div style="font-size:12px;">' +
|
||||
g.documents.map(function(d) {
|
||||
return '<div style="padding:2px 0;">' +
|
||||
'<span style="color:var(--muted);">[' + (d.doc_type || '?') + ']</span> ' +
|
||||
d.filename +
|
||||
(d.doc_date ? ' <span style="color:var(--muted);">' + d.doc_date + '</span>' : '') +
|
||||
'</div>';
|
||||
}).join('') +
|
||||
'</div>';
|
||||
if (!isUnresolved) {
|
||||
card.innerHTML += '<button class="btn" style="margin-top:6px;font-size:12px;height:28px;" onclick="runCompareForGroup(\'' + gi + '\')">▶ Сравнить</button>';
|
||||
}
|
||||
diffBody.appendChild(card);
|
||||
});
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
window.runCompareForGroup = function(gi) {
|
||||
// TODO: run /process-v2 for the specific contract
|
||||
alert('Сравнение для группы ' + gi + ' — в разработке');
|
||||
};
|
||||
|
||||
function uploadFile(file, onProgress) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
// .doc → конвертация в docx
|
||||
@@ -226,6 +308,7 @@ function uploadFile(file, onProgress) {
|
||||
var fd = new FormData();
|
||||
fd.append('files', uploadFile, uploadName);
|
||||
if (contractId) fd.append('contract_id', contractId);
|
||||
fd.append('batch_id', batchId);
|
||||
xhr.open('POST', UPLOAD_URL);
|
||||
xhr.upload.onprogress = function(e) {
|
||||
if (e.lengthComputable && onProgress) onProgress(Math.round(e.loaded / e.total * 100));
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<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.162 — Lucee</span></span>
|
||||
<span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.163 — Lucee</span></span>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
Reference in New Issue
Block a user