diff --git a/deploy/app.js b/deploy/app.js index 319a9e8..095d321 100644 --- a/deploy/app.js +++ b/deploy/app.js @@ -197,7 +197,28 @@ function showClassifyBtn() { async function runClassify() { var btn = document.getElementById('classifyBtn'); btn.disabled = true; - btn.innerHTML = ' Классификация...'; + btn.innerHTML = ' Классификация... 0с'; + + var start = Date.now(); + var timer = setInterval(function() { + btn.innerHTML = ' Классификация... ' + Math.round((Date.now() - start) / 1000) + 'с'; + }, 1000); + + // Poll progress каждые 2с + var pollTimer = setInterval(async function() { + try { + var r = await fetch(VM_API + '/api/batch-progress?batch=' + batchId); + var d = await r.json(); + if (d.ok && d.counts) { + var done = (d.counts.classified || 0) + (d.counts.failed || 0); + var total = d.total || 0; + btn.innerHTML = ' Классификация... ' + done + '/' + total + ' (' + Math.round((Date.now() - start) / 1000) + 'с)'; + if (done >= total && total > 0) { + clearInterval(pollTimer); + } + } + } catch(e) {} + }, 2000); try { var resp = await fetch(VM_API + '/api/classify-batch', { @@ -206,14 +227,18 @@ async function runClassify() { body: JSON.stringify({batch_id: batchId}) }); var data = await resp.json(); + clearInterval(timer); + clearInterval(pollTimer); if (data.ok) { - btn.innerHTML = '✓ ' + data.done + '/' + data.total + ' классифицировано'; + btn.innerHTML = '✓ ' + data.done + '/' + data.total + ' классифицировано (' + Math.round((Date.now() - start) / 1000) + 'с)'; loadGroups(); } else { btn.innerHTML = '✗ ' + (data.error || 'ошибка'); btn.disabled = false; } } catch(e) { + clearInterval(timer); + clearInterval(pollTimer); btn.innerHTML = '✗ ' + e.message; btn.disabled = false; } @@ -283,27 +308,49 @@ window.runCompareForGroup = async function(gi) { var btn = event.target; btn.disabled = true; - btn.innerHTML = ' Применяю группу...'; + btn.innerHTML = ' Применяю...'; - // 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; } + if (!ad.ok || !ad.contract_ids || !ad.contract_ids.length) { + btn.innerHTML = '✗ ошибка'; 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 - + var cid = ad.contract_ids[0]; btn.innerHTML = ' Сравнение...'; - // Use EventSource for process-v2 — but we need contract_id - // For now: use the old compare flow with contractId - btn.innerHTML = '✓ Готово — обновите страницу и нажмите «Общее сравнение»'; - btn.disabled = false; + var diffCard = document.getElementById('diffCard'); + var diffBody = document.getElementById('diffBody'); + var diffStatus = document.getElementById('diffStatus'); + diffCard.style.display = 'block'; + diffBody.innerHTML = ''; + diffStatus.textContent = '⏳ 0.0с'; + + var start = Date.now(); + var timer = setInterval(function() { + diffStatus.textContent = '⏳ ' + ((Date.now() - start) / 1000).toFixed(1) + 'с'; + }, 200); + + var es = new EventSource(VM_API + '/process-v2?contract_id=' + cid); + es.onmessage = function(e) { + var d = JSON.parse(e.data); + if (d.type === 'done') { + clearInterval(timer); es.close(); + diffStatus.textContent = '✓ ' + d.total_time_s + 'с'; + btn.innerHTML = '✓ Готово'; btn.disabled = false; + } else if (d.type === 'applied') { + diffBody.innerHTML += '