Feature: API PostgreSQL — upsert по «Название», загрузка из БД при открытии

Made-with: Cursor
This commit is contained in:
cursor-agent
2026-04-06 08:59:29 +00:00
parent 5c0028c308
commit f506563cdb
11 changed files with 1272 additions and 28 deletions

View File

@@ -965,7 +965,7 @@
<span class="section-head-label">Подсистема</span>
<span class="section-head-title">Мониторинг инцидентов</span>
<span class="section-head-rule"></span>
<span class="section-head-info">Формат загрузки: CSV · JSON · Excel</span>
<span class="section-head-info">Файл: CSV · JSON · Excel · сохранение в PostgreSQL по ключу «Название»</span>
</div>
<main class="main">
@@ -2022,6 +2022,36 @@
applyFilters();
}
function syncRowsToServer(rows) {
return fetch("/api/incidents/sync", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ rows: rows })
}).then(function (r) {
if (!r.ok) {
return r.text().then(function (t) {
throw new Error(t || "HTTP " + r.status);
});
}
return r.json();
});
}
function tryLoadFromDb() {
fetch("/api/incidents")
.then(function (r) {
if (!r.ok) return null;
return r.json();
})
.then(function (j) {
if (!j || !j.rows || !j.rows.length) return;
processData(j.rows);
el("success").classList.add("show");
el("success").textContent = "✓ Загружено из базы: " + j.rows.length + " записей";
})
.catch(function () { /* API недоступен — работа только с файлом */ });
}
function parseExcelToRows(buffer) {
if (typeof XLSX === "undefined") {
throw new Error("Библиотека Excel не загружена (XLSX). Проверьте сеть и CDN.");
@@ -2052,10 +2082,26 @@
function finish(rows) {
if (!rows.length) throw new Error("Нет строк данных");
processData(rows);
el("loading").classList.remove("show");
el("success").classList.add("show");
el("success").textContent = "✓ Загружено записей: " + rows.length;
el("loading").textContent = "Сохранение в базу данных…";
syncRowsToServer(rows)
.then(function (j) {
processData(rows);
el("loading").classList.remove("show");
el("error").classList.remove("show");
el("success").classList.add("show");
var msg = "✓ В базе сохранено: " + j.applied + " из " + rows.length;
if (j.skipped) msg += " (без ключа «Название»: " + j.skipped + ")";
el("success").textContent = msg;
})
.catch(function (err) {
processData(rows);
el("loading").classList.remove("show");
el("success").classList.remove("show");
el("error").classList.add("show");
el("error").textContent =
"✗ Данные отображены из файла. Ошибка БД: " +
(err && err.message ? err.message : String(err));
});
}
function onFail(err) {
@@ -2141,6 +2187,7 @@
controlDate = startOfDay(new Date());
setControlInput();
setupTabs();
tryLoadFromDb();
})();
</script>
</body>