';
return rows;
}).join('');
}
lucide.createIcons();
}
/**
* syncDB() — Синхронизация БД с текущим состоянием файлов.
*
* Отправляет на бэкенд список doc_id, которые должны остаться в БД.
* Всё, чего нет в state.files, будет удалено из БД (cascade).
* Вызывается после каждого изменения состава файлов (removeFile, upload).
*/
async function syncDB() {
var keepIds = state.files.map(function(f) { return f.doc_id; }).filter(Boolean);
try {
await fetch(VM_API + '/api/sync', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({keep_ids: keepIds})
});
} catch(e) { /* сервер недоступен — не критично */ }
}
/**
* toggleClassifyDetail(i) — Раскрыть/скрыть результат классификации под строкой файла.
*
* Вызывается по клику на имя файла в таблице.
* Загружает данные с бэкенда (/api/documents/:id) и показывает:
* - Тип, номер, родительский номер, дату, контрагента
* - Текст отправленный в LLM (📤)
* - Сырой ответ LLM (📥)
* - Распарсенные элементы JSON (📄)
*/
window.toggleClassifyDetail = async function(i) {
var detailRow = document.getElementById('detail_' + i);
var expandIcon = document.getElementById('expand_' + i);
if (!detailRow) return;
if (detailRow.style.display !== 'none') {
detailRow.style.display = 'none';
if (expandIcon) expandIcon.textContent = '▸';
return;
}
var f = state.files[i];
if (!f || !f.doc_id) return;
detailRow.style.display = '';
if (expandIcon) expandIcon.textContent = '▾';
try {
var resp = await fetch(VM_API + '/api/documents/' + f.doc_id);
var qData = await resp.json();
if (!qData.ok) { detailRow.cells[0].innerHTML = 'Ошибка загрузки'; return; }
var html = '';
if (qData.classify_status === 'classified') {
html += '
🏷️ Результат классификации
';
html += '
';
if (qData.doc_type) html += 'Тип: ' + escHtml(qData.doc_type) + '';
if (qData.own_number) html += 'Номер: ' + escHtml(qData.own_number) + '';
if (qData.parent_number) html += 'Родительский: ' + escHtml(qData.parent_number) + '';
if (qData.doc_date) html += 'Дата: ' + escHtml(qData.doc_date) + '';
if (qData.counterparty) html += 'Контрагент: ' + escHtml(qData.counterparty) + '';
html += '
';
if (qData.classify_input) {
html += '📤 Текст отправленный в LLM';
html += '
' + escHtml(qData.classify_input) + '
';
html += '';
}
if (qData.classify_raw) {
html += '📥 Сырой ответ LLM';
html += '
' + escHtml(qData.classify_raw) + '
';
html += '';
}
if (qData.elements_json) {
var ej = qData.elements_json;
if (typeof ej === 'object' && ej.Value) ej = ej.Value;
var ejStr = typeof ej === 'string' ? ej : JSON.stringify(ej, null, 2);
html += '📄 Распарсенные элементы (JSON)';
html += '