soglasovanie-zayavok-na-zaku/script.js

850 lines
50 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Согласование заявок на закупку — статический интерфейс.
Состояние (статусы заявок, выбор экрана, роль) — в localStorage. */
"use strict";
var LS_KEY = "sgl-zakupki-v1";
// ---------- справочники ----------
var SOURCES = ["1С", "Почта"];
var STATUS = {
new: { label: "Новая", tone: "blue" },
checking: { label: "На проверке", tone: "orange" },
toLegal: { label: "На согласовании", tone: "orange" },
toSign: { label: "На подписании", tone: "purple" },
sent: { label: "Отправлена в 1С", tone: "green" },
rejected: { label: "Возвращена", tone: "red" },
disputed: { label: "Спорный случай", tone: "red" }
};
function statusChip(st) {
var s = STATUS[st] || STATUS.checking;
return '<span class="kt-ai-chip" data-tone="' + s.tone + '">' + s.label + "</span>";
}
// ИИ-подсказки по кодам ТРУ: для каждой заявки — 2 кандидата
var TRU_HINTS = {
"ЗК-0918": [
{ c: "ТРУ-442.01", n: "Оптические модули, передача данных", conf: 96 },
{ c: "ТРУ-442.05", n: "Кабель оптический, внешняя прокладка", conf: 54 }
],
"ЗК-0919": [
{ c: "ТРУ-101.22", n: "Оборудование для обслуживания АТС", conf: 91 },
{ c: "ТРУ-101.27", n: "Монтажный инструмент для линий", conf: 47 }
],
"ЗК-0920": [
{ c: "ТРУ-310.14", n: "Провод медный, ВВГнг 3х25", conf: 98 },
{ c: "ТРУ-310.11", n: "Провод медный, ВВГнг 3х16", conf: 61 }
],
"ЗК-0912": [
{ c: "ТРУ-501.03", n: "Сухие строительные смеси, оптом", conf: 88 },
{ c: "ТРУ-501.07", n: "Краски и лаки, фасовка 10 кг", conf: 40 }
],
"ЗК-0913": [
{ c: "ТРУ-071.19", n: "Услуги связи, прокат линии", conf: 94 },
{ c: "ТРУ-071.11", n: "Аренда коммутаторов", conf: 38 }
],
"ЗК-0914": [
{ c: "ТРУ-442.01", n: "Оптические модули, передача данных", conf: 52 },
{ c: "ТРУ-440.02", n: "Сетевые коммутаторы", conf: 49 }
],
"ЗК-0915": [
{ c: "ТРУ-610.05", n: "Хостинг вычислительных мощностей", conf: 93 },
{ c: "ТРУ-610.09", n: "Облачные услуги, хранилище", conf: 58 }
]
};
// ---------- заявки ----------
var BASE_REQUESTS = [
{ id: "ЗК-0918", init: "Ахметов С. Т.", sum: 2480000, src: "1С", in: "22.09.2026", done: "", st: "checking", trr: "ТРУ-440.12", plan: "Да", codeTru: false, dispute: false,
subj: "Оптические модули для узла связи", justif: "Замена изношенных модулей на магистральном узле, срок годности запаса истёк." },
{ id: "ЗК-0919", init: "Байжанова Ш. К.", sum: 1120000, src: "Почта", in: "22.09.2026", done: "", st: "checking", trr: "ТРУ-101.20", plan: "Да", codeTru: false, dispute: false,
subj: "Монтажный инструмент", justif: "Дополнение оснастки бригады по заявке на обслуживание линий." },
{ id: "ЗК-0920", init: "Дюсембаев Н. Б.", sum: 560000, src: "1С", in: "21.09.2026", done: "", st: "new", trr: "ТРУ-310.09", plan: "Да", codeTru: false, dispute: false,
subj: "Провод ВВГнг 3х25, 1 200 м", justif: "Стройка внутреннего контура в техническом помещении." },
{ id: "ЗК-0915", init: "Ермек А. Д.", sum: 1490000, src: "1С", in: "20.09.2026", done: "21.09.2026", st: "toSign", trr: "ТРУ-610.05", plan: "Да", codeTru: true, dispute: false,
subj: "Хостинг дополнительных мощностей", justif: "Временное расширение хранилища до завершения строительства стойки." },
{ id: "ЗК-0916", init: "Исаев Г. М.", sum: 3840000, src: "Почта", in: "20.09.2026", done: "21.09.2026", st: "toLegal", trr: "ТРУ-220.31", plan: "Нет", codeTru: true, dispute: false,
subj: "Панели управления для ЩУ-2", justif: "Панели не включены в план закупок: потребность подтверждена актом осмотра." },
{ id: "ЗК-0917", init: "Кадырова Ж. С.", sum: 2960000, src: "1С", in: "19.09.2026", done: "22.09.2026", st: "toSign", trr: "ТРУ-442.01", plan: "Да", codeTru: true, dispute: false,
subj: "Оборудование передачи данных", justif: "Поставка по утверждённому плану на вторую половину года." },
{ id: "ЗК-0914", init: "Мухитов Р. О.", sum: 1730000, src: "1С", in: "18.09.2026", done: "", st: "disputed", trr: "ТРУ-440.12", plan: "Да", codeTru: true, dispute: true,
subj: "Коммутаторы сетевые, 8 шт", justif: "В заявке указано оборудование, не совпадающее с артикулом плана закупки." },
{ id: "ЗК-0913", init: "Нурлан С. А.", sum: 640000, src: "Почта", in: "17.09.2026", done: "17.09.2026", st: "rejected", trr: "ТРУ-071.19", plan: "Нет", codeTru: true, dispute: false,
subj: "Прокат телефонной линии", justif: "Возвращена: услуга уже приобретена по договору № 4542/2026." },
{ id: "ЗК-0912", init: "Оспанова Л. Т.", sum: 1250000, src: "1С", in: "16.09.2026", done: "18.09.2026", st: "sent", trr: "ТРУ-501.03", plan: "Да", codeTru: true, dispute: false,
subj: "Стройматериалы для ремонта серверной", justif: "Ремонт по смете, согласованной в плановом периоде." },
{ id: "ЗК-0911", init: "Павлов К. И.", sum: 480000, src: "1С", in: "15.09.2026", done: "16.09.2026", st: "sent", trr: "ТРУ-118.40", plan: "Да", codeTru: true, dispute: true,
subj: "Комплект ЗИП для линии связи", justif: "Подтверждён вручную: ИИ предложил неверный код (ТРУ-118.44), закупщик исправил." },
{ id: "ЗК-0910", init: "Сафин Д. Р.", sum: 910000, src: "Почта", in: "14.09.2026", done: "15.09.2026", st: "sent", trr: "ТРУ-330.08", plan: "Да", codeTru: true, dispute: false,
subj: "Эксплуатационные материалы", justif: "Запас на третий квартал, остатки ниже минимального уровня." },
{ id: "ЗК-0909", init: "Тлеуберди А. М.", sum: 3200000, src: "1С", in: "12.09.2026", done: "16.09.2026", st: "sent", trr: "ТРУ-443.02", plan: "Да", codeTru: true, dispute: false,
subj: "Кабель оптический, 48 волокон", justif: "Резервирующая линия между узлами, включена в план." }
];
// ---------- состояние ----------
function today() { return new Date().toISOString().slice(0, 10); }
function loadState() {
try {
var raw = localStorage.getItem(LS_KEY);
if (raw) {
var st = JSON.parse(raw);
if (st && st.v === 1) return st;
}
} catch (e) { /* повреждённые данные — начинаем заново */ }
return { v: 1, screen: "work", approverRole: "legal", selected: "ЗК-0918", regStatus: "all", regSrc: "all", regQuery: "", regRole: "legal",
modified: {}, daySent: "ЗК-0912", dayNew: "ЗК-0920", created: today() };
}
var S;
function saveState() {
try { localStorage.setItem(LS_KEY, JSON.stringify(S)); } catch (e) {}
}
function daysIn(id) {
var d = { "ЗК-0918": 2, "ЗК-0919": 1, "ЗК-0920": 1, "ЗК-0916": 2, "ЗК-0915": 2, "ЗК-0917": 4, "ЗК-0914": 3, "ЗК-0909": 8 };
return d[id] || 1;
}
function byId(id) {
for (var i = 0; i < BASE_REQUESTS.length; i++) {
if (BASE_REQUESTS[i].id === id) return BASE_REQUESTS[i];
}
return null;
}
function live(id) {
var b = byId(id);
var r = {};
for (var k in b) { if (Object.prototype.hasOwnProperty.call(b, k)) r[k] = b[k]; }
var m = S.modified[id];
if (m) {
if (m.st) r.st = m.st;
if (m.codeTru) r.codeTru = true;
if (m.dispute) r.dispute = true;
if (m.disputeReset) r.dispute = false;
if (m.trr) r.trr = m.trr;
if (m.done) r.done = m.done;
if (m.doneReset) r.done = "";
}
return r;
}
function allLive() {
var out = [];
for (var i = 0; i < BASE_REQUESTS.length; i++) out.push(live(BASE_REQUESTS[i].id));
return out;
}
// ---------- утилиты ----------
function fmtSum(x) {
return x.toLocaleString("ru-RU") + " ₸";
}
function esc(s) {
return String(s == null ? "" : s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
function toast(msg, tone) {
var stack = document.getElementById("toasts");
if (!stack) return;
var el = document.createElement("div");
el.className = "kt-ai-toast";
el.setAttribute("data-tone", tone || "");
el.textContent = msg;
stack.appendChild(el);
setTimeout(function () {
if (el.parentNode) el.parentNode.removeChild(el);
}, 4600);
}
function modalOpen(title, body) {
var root = document.getElementById("modalRoot");
if (!root) return;
root.innerHTML = '<div class="kt-ai-modal-overlay"><div class="kt-ai-modal">' +
'<div class="modal-title">' + esc(title) + "</div>" + esc(body) +
'<div class="modal-actions"><button class="kt-ai-btn" id="mClose">Отмена</button></div></div></div>';
var btn = document.getElementById("mClose");
if (btn) btn.addEventListener("click", modalClose);
var ov = root.querySelector(".kt-ai-modal-overlay");
if (ov) ov.addEventListener("click", function (e) { if (e.target === ov) modalClose(); });
}
function modalClose() {
var root = document.getElementById("modalRoot");
if (root) root.innerHTML = "";
}
// ================= РАБОЧИЙ СТОЛ ЗАКУПЩИКА =================
function renderWork() {
var list = allLive();
var queue = list.filter(function (r) { return r.st === "new" || r.st === "checking"; });
// свежие первыми, затем по дате поступления
queue.sort(function (a, b) {
if ((a.st === "new") !== (b.st === "new")) return a.st === "new" ? -1 : 1;
return a.in < b.in ? 1 : -1;
});
var sel = null;
for (var i = 0; i < queue.length; i++) { if (queue[i].id === S.selected) { sel = queue[i]; break; } }
if (!sel && queue.length) { sel = queue[0]; S.selected = sel.id; }
var rows = queue.map(function (r) {
var selAttr = r.id === S.selected ? ' data-selected="true"' : "";
var srcIc = r.src === "1С" ? "spreadsheet" : "mail";
return '<tr data-id="' + r.id + '"' + selAttr + ">" +
"<td class=\"kt-ai-t-medium\">" + r.id + "</td>" +
"<td>" + esc(r.init) + "</td>" +
'<td class="num kt-ai-num">' + fmtSum(r.sum) + "</td>" +
"<td>" + statusChip(r.st) + "</td>" +
'<td><span class="src-cell"><svg class="kt-icon kt-icon-sm" aria-hidden="true"><use href="#' + srcIc + '"></use></svg>' + r.src + "</span></td>" +
"</tr>";
}).join("");
var k1 = queue.length;
var k2 = queue.filter(function (r) { return r.st === "new"; }).length;
var k3 = queue.filter(function (r) { return r.st === "checking" && !r.codeTru; }).length;
var k4 = list.filter(function (r) { return r.st === "toSign" || r.st === "toLegal"; }).length;
var k5 = list.filter(function (r) { return r.st === "sent"; }).length;
var html = "<h1 class=\"kt-ai-h1\">Рабочий стол</h1>" +
'<p class="kt-ai-t-md" style="color:var(--kt-ai-fg-muted)">Проверка поступивших заявок на закупку: код ТРУ, план, сумма.</p>' +
'<div class="kt-ai-kpi-strip">' +
kpi(k1, "В очереди проверки", "новых заявок: " + k2) +
kpi(k3, "Без подтверждённого кода", "из ожидает проверки") +
kpi(k4, "На согласовании", "у юриста и руководителя") +
kpi(k5, "Отправлено в 1С", "с начала месяца") +
"</div>" +
'<div class="wd-grid"><div class="kt-ai-v" data-gap="tight">' +
'<div class="kt-ai-table-wrap" data-framed="true"><div class="kt-ai-table-toolbar">' +
'<span class="kt-ai-t-md kt-ai-t-semibold">Очередь</span><span class="kt-ai-meta">обновлено ИИ: 22.09, 09:14</span>' +
'</div><table class="kt-ai-table"><thead><tr><th>Номер</th><th>Инициатор</th><th class="num">Сумма</th><th>Статус</th><th>Источник</th></tr></thead>' +
"<tbody>" + (rows || emptyRow("Очередь пуста", "Все поступившие заявки проверены.")) + "</tbody></table></div>" +
"</div>" +
workDetail(sel) +
"</div></div>";
document.getElementById("view").innerHTML = html;
bindWork();
}
function kpi(v, label, hint) {
return '<div class="kt-ai-kpi"><span class="value">' + v + '</span><span class="label">' + esc(label) + "</span>" +
(hint ? '<span class="hint">' + esc(hint) + "</span>" : "") + "</div>";
}
function emptyRow(title, sub) {
return '<tr><td colspan="5"><div class="kt-ai-empty"><div class="title">' + esc(title) + "</div>" +
'<div class="sub" style="font-size:var(--kt-ai-text-xs);color:var(--kt-ai-fg-muted)">' + esc(sub) + "</div></div></td></tr>";
}
function workDetail(r) {
if (!r) {
return '<aside class="wd-card"><div class="kt-ai-empty"><div class="title">В очереди нет заявок</div></div></aside>';
}
var hint = TRU_HINTS[r.id];
var codeErr = !r.codeTru || (r.id === "ЗК-0919");
var sumMismatch = r.id === "ЗК-0919";
var planIssue = r.plan !== "Да";
var isErr = codeErr || sumMismatch || planIssue;
var errReason = codeErr ? (r.id === "ЗК-0919" ? "Сумма не сходится с ценой в плане" : (r.dispute ? "Код несовпадает с оборудованием в заявке" : "ИИ не подтвердил код")) : (sumMismatch ? "Сумма не сходится с ценой в плане" : "Нет обоснования сверх плана");
var checks =
checkItem(planIssue ? "warn" : "ok", "В плане закупок", planIssue ? "В плане отсутствует, нужно обоснование" : "Строка плана найдена") +
checkItem(codeErr ? "err" : "ok", "Код ТРУ", codeErr ? errReason : "Подтверждён по справочнику") +
checkItem(sumMismatch ? "err" : "ok", "Сумма в пределах лимита", sumMismatch ? "Не сходится с ценой в плане" : (r.src === "1С" ? "Сверено со строкой 1С" : "В письмо от инициатора"));
var okCount = isErr ? 2 : 3;
return '<aside class="kt-ai-v" data-gap="block">' +
'<div class="wd-card"><div class="wd-card-h"><strong class="kt-ai-t-lg kt-ai-t-semibold">' + r.id + "</strong>" +
'<span class="sub">' + esc(r.subj) + "</span></div>" +
'<div class="wd-card-kpis">' +
kpiCell("Инициатор", esc(r.init)) + kpiCell("Сумма", fmtSum(r.sum)) +
kpiCell("План", r.plan === "Да" ? "В плане" : "Сверх плана") +
"</div>" +
'<div class="kt-ai-v" data-gap="tight">' +
'<span class="kt-ai-label">Поля заявки</span>' +
'<div class="drv"><span class="k">Код ТРУ в заявке</span><span class="v mono">' + r.trr + "</span></div>" +
'<div class="drv"><span class="k">Наличие в плане</span><span class="v">' + (r.plan === "Да" ? "Да, в плане закупок" : "Нет, сверх плана") + "</span></div>" +
'<div class="drv"><span class="k">Дата поступления</span><span class="v">' + r.in + " · " + r.src + "</span></div>" +
"</div>" +
'<div class="kt-ai-v" data-gap="tight">' +
"<span class=\"kt-ai-label\">Индикатор проверки</span>" +
'<div class="ai-hint" style="padding:0">' +
(isErr
? '<div class="kt-ai-banner" data-tone="risk" style="border:none;border-radius:var(--kt-ai-radius-xl)"><svg class="kt-icon" aria-hidden="true"><use href="#warning"></use></svg><div>Проверка не пройдена: ' + (okCount) + " из 3 пунктов. " + errReason + ". Верните заявку инициатору либо передайте в спорные случаи.</div></div>"
: '<div class="kt-ai-banner" data-tone="ok" style="border:none;border-radius:var(--kt-ai-radius-xl)"><svg class="kt-icon" aria-hidden="true"><use href="#check"></use></svg><div>Проверка пройдена: 3 из 3 пунктов. Заявка готова к передаче на согласование.</div></div>') +
"</div>" +
"</div>" +
'<div class="kt-ai-v" data-gap="tight">' +
'<span class="kt-ai-label">Проверки</span>' +
'<div class="check-list">' + checks + "</div>" +
"</div>" +
"</div>" +
aiPanel(r) +
'<div class="actions-row">' +
'<button class="kt-ai-btn" data-act="legal" ' + (isErr ? "disabled" : "") + '><svg class="kt-icon" aria-hidden="true"><use href="#send"></use></svg>Передать юристу</button>' +
'<button class="kt-ai-btn" data-act="return"><svg class="kt-icon" aria-hidden="true"><use href="#arrowLeft"></use></svg>Вернуть инициатору</button>' +
'<button class="kt-ai-btn" data-variant="ghost" data-act="dispute"><svg class="kt-icon" aria-hidden="true"><use href="#warning"></use></svg>В спорные</button>' +
"</div>" +
"</aside>";
}
function kpiCell(k, v) {
return '<div class="wd-kpi"><span class="v">' + v + '</span><span class="k">' + k + "</span></div>";
}
function checkItem(tone, title, why) {
var ic = tone === "ok" ? "check" : (tone === "err" ? "x" : "warning");
return '<div class="check-item ' + tone + '"><svg class="kt-icon" aria-hidden="true"><use href="#' + ic + '"></use></svg>' +
"<div>" + esc(title) + (why ? '<div class="why">' + esc(why) + "</div>" : "") + "</div></div>";
}
function aiPanel(r) {
var hint = TRU_HINTS[r.id];
var head = '<div class="ai-hint-t"><svg class="kt-icon" aria-hidden="true"><use href="#sparkles"></use></svg>Подсказка ИИ · код ТРУ</div>';
if (r.codeTru) {
return '<div class="ai-hint">' + head +
'<div class="ai-done"><svg class="kt-icon" aria-hidden="true"><use href="#check"></use></svg><div>Код <b>' + r.trr + "</b> подтверждён " +
(S.modified[r.id] && S.modified[r.id].at ? S.modified[r.id].at : "(ранее)") + ". Реестр обновлён автоматически.</div></div></div>";
}
if (!hint) return '<div class="ai-hint"><div class="kt-ai-empty"><div class="title">Подсказка не найдена</div></div></div>';
var best = hint[0];
var cands = hint.map(function (h, idx) {
return '<div class="cand-row' + (idx === 0 ? " sel" : "") + ' data-cand="' + idx + '"><div><div class="code">' + h.c + "</div>" +
'<div class="nm">' + esc(h.n) + "</div></div>" +
'<span class="kt-ai-chip" data-tone="' + (h.conf >= 80 ? "green" : "orange") + '">' + h.conf + "%</span></div>";
}).join("");
return '<div class="ai-hint">' + head +
'<div class="ai-conf"><span>Уверенность по первой подсказке</span><span class="kt-ai-progress"><div style="width:' + best.conf + '%"></div></span><span class="kt-ai-num">' + best.conf + "%</span></div>" +
'<div class="kt-ai-v" data-gap="tight">' + cands + "</div>" +
'<div class="ai-actions"><button class="kt-ai-btn" data-variant="primary" data-act="confirm"><svg class="kt-icon" aria-hidden="true"><use href="#check"></use></svg>Подтвердить код</button>' +
'<button class="kt-ai-btn" data-variant="ghost" data-act="other">Другой код</button></div></div>';
}
function bindWork() {
var trs = document.querySelectorAll("#view .kt-ai-table tbody tr[data-id]");
for (var i = 0; i < trs.length; i++) {
trs[i].addEventListener("click", function () {
S.selected = this.getAttribute("data-id");
saveState(); renderWork();
});
}
var btns = document.querySelectorAll('#view [data-act]');
for (var j = 0; j < btns.length; j++) {
btns[j].addEventListener("click", onWorkAction);
}
var cands = document.querySelectorAll("#view .cand-row[data-cand]");
for (var c = 0; c < cands.length; c++) {
cands[c].addEventListener("click", function () {
var rows = document.querySelectorAll("#view .cand-row[data-cand]");
for (var k = 0; k < rows.length; k++) rows[k].classList.remove("sel");
this.classList.add("sel");
});
}
}
function onWorkAction(ev) {
var act = ev.currentTarget.getAttribute("data-act");
var r = live(S.selected);
if (!r) return;
var now = "22.09.2026, " + new Date().toTimeString().slice(0, 5);
var m = S.modified[r.id] || {};
if (act === "confirm") {
var code = r.trr;
var hintArr = TRU_HINTS[r.id];
if (hintArr && hintArr[0]) {
var selCand = document.querySelector("#view .cand-row.sel");
var ci = 0;
if (selCand) {
var ciRaw = parseInt(selCand.getAttribute("data-cand"), 10);
ci = !isNaN(ciRaw) ? ciRaw : 0;
if (ci >= hintArr.length) ci = 0;
}
var picked = hintArr[ci];
if (picked) code = picked.c;
}
m.codeTru = true; m.at = now; m.trr = code;
S.modified[r.id] = m; saveState();
toast("Код " + code + " подтверждён, реестр обновлён", "ok");
renderWork();
} else if (act === "return") {
m.st = "rejected"; m.done = "22.09.2026";
S.modified[r.id] = m; saveState();
S.dayNew = r.id === S.dayNew ? "" : S.dayNew;
S.selected = nextOf(S.selected, ["new", "checking"]);
saveState();
toast(r.id + " возвращена инициатору с комментарием", "warn");
renderWork();
} else if (act === "dispute") {
m.dispute = true; S.disputeTarget = r.id; saveState();
modalOpen("Спорный случай", "<div style=\"font-size:var(--kt-ai-text-sm);line-height:1.6\">Заявка " + r.id + " будет передана руководителю для разбора: ИИ предложил код, который не совпадает с версией в заявке. Решение по ней примет руководитель.</div><button class=\"kt-ai-btn\" data-variant=\"primary\" id=\"dOk\" style=\"width:100%;margin-top:var(--kt-ai-space-5)\">Передать руководителю</button>");
var ok = document.getElementById("dOk");
if (ok) ok.addEventListener("click", function () {
modalClose();
S.modified[r.id] = S.modified[r.id] || {};
S.modified[r.id].st = "disputed";
delete S.modified[r.id].disputeReset;
S.selected = nextOf(S.selected, ["new", "checking"]);
saveState();
toast(r.id + " передана руководителю на разбор", "warn");
renderWork();
});
return;
} else if (act === "other") {
toast("Подскажи код — выберите из списка или верните заявку инициатору", "warn");
return;
} else if (act === "legal") {
m.st = "toLegal"; m.done = "22.09.2026";
S.modified[r.id] = m; saveState();
S.selected = nextOf(S.selected, ["new", "checking"]);
saveState();
toast(r.id + " передана юристу на согласование", "ok");
renderWork();
}
}
function nextOf(skip, stList) {
var q = allLive().filter(function (r) { return stList.indexOf(r.st) >= 0 && r.id !== skip; });
q.sort(function (a, b) { return a.in < b.in ? 1 : -1; });
return q.length ? q[0].id : "";
}
// ================= РЕЕСТР ЗАЯВОК =================
var MONTHS = [["04.09", 7, "ok"], ["05.09", 9, "ok"], ["08.09", 8, "ok"], ["09.09", 11, "ok"], ["10.09", 8, "ok"],
["11.09", 13, "orange"], ["14.09", 7, "ok"], ["15.09", 10, "ok"], ["16.09", 9, "ok"],
["17.09", 12, "orange"], ["18.09", 8, "ok"], ["19.09", 11, "ok"]];
function renderRegistry() {
var list = allLive();
var st = S.regStatus, src = S.regSrc, q = (S.regQuery || "").toLowerCase();
var filtered = list.filter(function (r) {
if (st !== "all" && r.st !== st) return false;
if (src !== "all" && r.src !== src) return false;
if (q && (r.id.toLowerCase().indexOf(q) < 0) && (r.init.toLowerCase().indexOf(q) < 0) && (r.subj.toLowerCase().indexOf(q) < 0)) return false;
return true;
});
// сортировка: сначала новые, затем по дате
filtered.sort(function (a, b) { return a.in < b.in ? 1 : -1; });
var inWork = list.filter(function (r) { return ["new", "checking", "toLegal", "toSign", "disputed"].indexOf(r.st) >= 0; }).length;
var done = list.filter(function (r) { return r.st === "sent"; }).length;
var rej = list.filter(function (r) { return r.st === "rejected"; }).length;
var sumWork = 0;
list.forEach(function (r) { if (["new", "checking"].indexOf(r.st) >= 0) sumWork += r.sum; });
var rows = filtered.map(function (r) {
var srcIc = r.src === "1С" ? "spreadsheet" : "mail";
var disp = r.done ? r.done : "—";
var di = daysIn(r.id);
return '<tr data-id="' + r.id + '"><td class="kt-ai-t-medium">' + r.id + "</td>" +
"<td>" + esc(r.init) + "</td>" +
'<td class="num kt-ai-num">' + fmtSum(r.sum) + "</td>" +
"<td>" + statusChip(r.st) + "</td>" +
'<td class="num kt-ai-num' + (di > 2 ? " sl-amber" : "") + '">' + di + "</td>" +
"<td class=\"kt-ai-num\">" + r.in + "</td>" +
'<td class="kt-ai-num">' + disp + "</td>" +
'<td><span class="src-cell"><svg class="kt-icon kt-icon-sm" aria-hidden="true"><use href="#' + srcIc + '"></use></svg>' + r.src + "</span></td></tr>";
}).join("");
var bars = MONTHS.map(function (m) {
var max = 13, h = Math.round((m[1] / max) * 100);
return '<div class="cc-bar" data-tone="' + m[2] + '" tabindex="0" style="height:' + h + '%" ' +
'data-kt-tip=\'{"title":"' + m[0] + '","rows":[{"label":"Заявок","value":"' + m[1] + '"},{"label":"Возвращено","value":"' + (m[2] === "orange" ? 2 : 1) + '"}]}\'></div>';
}).join("");
var html = "<h1 class=\"kt-ai-h1\">Реестр заявок</h1>" +
'<p class="kt-ai-t-md" style="color:var(--kt-ai-fg-muted)">Все заявки филиала с текущим статусом; реестр ведёт ИИ, статусы меняет человек.</p>' +
'<div class="kt-ai-kpi-strip">' +
kpi(inWork, "В работе", "новые + проверка + согласование") +
kpi(done, "Обработано", "отправлено в 1С в сентябре") +
kpi(rej, "Возвращено", "инициатору на доработку") +
kpi(Math.round(sumWork / 1000000), "млн ₸ в очереди", "сумма новых и проверяемых") +
"</div>" +
'<div class="kt-ai-v" data-gap="block">' +
'<div class="kt-ai-toolbar"><div class="kt-ai-filterbar">' +
inputSearch() +
'<span class="kt-ai-spacer"></span>' +
selStatus() +
selSrc() +
'<button class="kt-ai-btn" data-act="csv"><svg class="kt-icon" aria-hidden="true"><use href="#download"></use></svg>Экспорт</button>' +
"</div></div>" +
'<div class="kt-ai-table-wrap"><table class="kt-ai-table"><thead><tr>' +
"<th>Номер</th><th>Инициатор</th><th class=\"num\">Сумма</th><th>Статус</th><th class=\"num\">Срок, дней</th><th>Поступила</th><th>Обработана</th><th>Источник</th>" +
"</tr></thead><tbody>" + (rows || emptyRow("Ничего не найдено", "Снимите фильтры — реестр за месяц заполнен.")) + "</tbody></table></div>" +
'<div class="chart-card"><div class="kt-ai-h3">Заявки по неделям сентября</div>' +
'<div class="cc-bars">' + bars + "</div>" +
'<div class="chart-foot" style="display:flex;gap:var(--kt-ai-space-5);margin-top:var(--kt-ai-space-3)">' +
'<span><span class="dot" style="background:var(--kt-ai-chart-blue)"></span>входящие</span>' +
'<span><span class="dot" style="background:var(--kt-ai-chart-orange)"></span>высокая нагрузка</span></div></div>' +
"<div class=\"kt-ai-conclusion\"><span class=\"title\">Что показывает реестр</span>" +
"<span class=\"text\">Пик нагрузки — 2–7 сентября: 10–13 заявок в день совпали с закрытием планового цикла. Возвратов в сентябре немного, большинство возвратов — дубли уже приобретённых услуг.</span></div>" +
"</div></div>";
document.getElementById("view").innerHTML = html;
bindRegistry();
}
function inputSearch() {
return '<input class="kt-ai-input reg-q" type="text" placeholder="Номер, инициатор, предмет…" value="' + esc(S.regQuery || "") + '" style="max-width:240px;height:32px">';
}
function selStatus() {
var opts = "";
for (var k in STATUS) {
if (!Object.prototype.hasOwnProperty.call(STATUS, k)) continue;
opts += '<option value="' + k + '"' + (S.regStatus === k ? " selected" : "") + ">" + STATUS[k].label + "</option>";
}
return '<div class="kt-ai-select-wrap" style="width:auto"><select class="kt-ai-select reg-status" style="height:32px">' +
'<option value="all"' + (S.regStatus === "all" ? " selected" : "") + ">Все статусы</option>" +
opts +
"</select></div>";
}
function selSrc() {
return '<div class="kt-ai-select-wrap" style="width:auto"><select class="kt-ai-select reg-src" style="height:32px">' +
'<option value="all"' + (S.regSrc === "all" ? " selected" : "") + ">Все источники</option>" +
"<option value=\"1С\"" + (S.regSrc === "1С" ? " selected" : "") + ">1С</option>" +
"<option value=\"Почта\"" + (S.regSrc === "Почта" ? " selected" : "") + ">Почта</option></select></div>";
}
function bindRegistry() {
var trs = document.querySelectorAll("#view .kt-ai-table tbody tr[data-id]");
for (var i = 0; i < trs.length; i++) {
trs[i].addEventListener("click", function () {
S.selected = this.getAttribute("data-id");
S.screen = "approve"; S.regRole = "legal";
saveState(); render();
});
}
var q = document.querySelector(".reg-q");
if (q) q.addEventListener("input", function () { S.regQuery = this.value; saveState(); renderRegistry(); });
var status = document.querySelector(".reg-status");
if (status) status.addEventListener("change", function () { S.regStatus = this.value; saveState(); renderRegistry(); });
var src = document.querySelector(".reg-src");
if (src) src.addEventListener("change", function () { S.regSrc = this.value; saveState(); renderRegistry(); });
var exp = document.querySelector('#view [data-act="csv"]');
if (exp) exp.addEventListener("click", exportCsv);
}
function exportCsv() {
var list = allLive();
var head = "Номер;Инициатор;Сумма;Статус;Срок (дней);Поступила;Обработана;Источник;Предмет\n";
var body = list.map(function (r) {
var parts = [r.id, r.init, r.sum, STATUS[r.st].label, daysIn(r.id), r.in, r.done || "", r.src, '"' + esc(r.subj) + '"'];
return parts.join(";");
}).join("\n");
var blob = new Blob(["\uFEFF" + head + body], { type: "text/csv;charset=utf-8" });
var a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "reestr-zayavok-sentyabr.csv";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
toast("Реестр выгружен в CSV", "ok");
}
// ================= СОГЛАСОВАНИЕ =================
function renderApprove() {
var role = S.regRole || "legal";
var list = allLive();
var pool = list.filter(function (r) { return role === "legal" ? r.st === "toLegal" : r.st === "toSign"; });
pool.sort(function (a, b) { return a.in < b.in ? 1 : -1; });
var sel = null;
for (var i = 0; i < pool.length; i++) { if (pool[i].id === S.selected) { sel = pool[i]; break; } }
var isDisputed = role === "boss" && sel && sel.st === "disputed";
if (!sel && pool.length) { sel = pool[0]; }
var rows = pool.map(function (r) {
var sAttr = r.id === S.selected ? ' data-selected="true"' : "";
return '<tr data-id="' + r.id + '"' + sAttr + '><td class="kt-ai-t-medium">' + r.id + "</td>" +
"<td>" + esc(r.init) + "</td>" +
'<td class="num kt-ai-num">' + fmtSum(r.sum) + "</td>" +
"<td class=\"kt-ai-num\">" + r.in + "</td>" +
"<td>" + statusChip(role === "legal" ? "toLegal" : "toSign") + "</td></tr>";
}).join("");
// disputed-строки для руководителя
var dis = list.filter(function (r) { return r.st === "disputed"; });
var disRows = dis.map(function (r) {
var sAttr = isDisputed && r.id === S.selected ? ' data-selected="true"' : "";
return '<tr data-id="' + r.id + '" data-dispute="1"' + sAttr + '><td class="kt-ai-t-medium">' + r.id + "</td>" +
"<td>" + esc(r.init) + "</td>" +
'<td class="num kt-ai-num">' + fmtSum(r.sum) + "</td>" +
"<td class=\"kt-ai-num\">" + r.in + "</td>" +
"<td>" + statusChip("disputed") + "</td></tr>";
}).join("");
var roleTitle = role === "legal" ? "Согласование юриста" : "Подписание руководителя";
var poolLabel = role === "legal" ? "ждут согласования" : "ждут подписания";
var nToAct = pool.length + dis.length;
var k1 = pool.length, k2 = dis.length,
k3 = list.filter(function (r) { return r.st === "sent"; }).length,
k4 = list.filter(function (r) { return r.st === "rejected"; }).length;
var html = "<h1 class=\"kt-ai-h1\">" + roleTitle + "</h1>" +
'<p class="kt-ai-t-md" style="color:var(--kt-ai-fg-muted)">Заявки, ' + poolLabel + " после проверки закупщиком.</p>" +
'<div class="kt-ai-kpi-strip">' +
kpi(k1, "Ждут моего действия", "в очереди сейчас") +
kpi(k2, "Спорные случаи", "ошибки ИИ по кодам ТРУ") +
kpi(k3, "Отправлено в 1С", "сентябрь") +
kpi(k4, "Возвращено", "на доработку") +
"</div>" +
'<div class="kt-ai-toolbar"><div class="kt-ai-filterbar">' +
'<span class="kt-ai-label">Роль</span>' +
'<div class="kt-ai-select-wrap" style="width:auto"><select class="kt-ai-select sel-role" style="height:32px">' +
'<option value="boss"' + (role === "boss" ? " selected" : "") + ">Руководитель</option>" +
"<option value=\"legal\"" + (role === "legal" ? " selected" : "") + ">Юрист</option></select></div>" +
'<span class="kt-ai-spacer"></span><span class="kt-ai-meta">на рассмотрении: ' + nToAct + "</span></div></div>" +
'<div class="appr-grid" style="margin-top:var(--kt-ai-space-6)"><div class="kt-ai-v" data-gap="tight">' +
'<div class="kt-ai-table-wrap"' + (disRows ? " data-framed=\"true\"" : "") + '><table class="kt-ai-table"><thead><tr>' +
"<th>Номер</th><th>Инициатор</th><th class=\"num\">Сумма</th><th>Поступила</th><th>Статус</th>" +
"</tr></thead><tbody>" + (rows || emptyRow("Очередь пуста", "Все заявки переданы на следующий этап.")) + disRows + "</tbody></table></div>" +
"</div>" +
apprDetail(sel, role, isDisputed) +
"</div></div>";
document.getElementById("view").innerHTML = html;
bindApprove(role);
}
function apprDetail(r, role, isDisputed) {
if (!r) {
return '<aside class="wd-card"><div class="kt-ai-empty"><div class="title">Выберите заявку из очереди</div></div></aside>';
}
var hint = TRU_HINTS[r.id];
var trrNote = r.dispute
? 'ИИ предложил «' + r.trr + '», но версия оборудования в заявке не сходится — на разборе.'
: 'Подтверждён закупщиком' + (hint ? ' (уверенность ИИ: ' + hint[0].conf + "%)" : "") + ".";
var subj = isDisputed ? "Разбор спорного случая" : "Детали заявки";
var tone = isDisputed ? "risk" : "ok";
return '<aside class="kt-ai-v" data-gap="block">' +
'<div class="wd-card"><div class="wd-card-h"><strong class="kt-ai-t-lg kt-ai-t-semibold">' + r.id + "</strong>" +
'<span class="sub">' + esc(r.subj) + "</span></div>" +
"<div style=\"margin-top:var(--kt-ai-space-2)\">" + statusChip(isDisputed ? "disputed" : (role === "legal" ? "toLegal" : "toSign")) + "</div>" +
'<div class="wd-card-kpis">' +
kpiCell("Инициатор", esc(r.init)) + kpiCell("Сумма", fmtSum(r.sum)) +
kpiCell("План", r.plan === "Да" ? "В плане" : "Сверх плана") +
"</div>" +
'<div class="kt-ai-v" data-gap="tight">' +
'<span class="kt-ai-label">Код ТРУ</span>' +
'<div class="drv"><span class="k">Подтверждённый код</span><span class="v mono">' + r.trr + "</span></div>" +
'<div class="check-item ' + (r.dispute ? "warn" : "ok") + ' style="margin-top:var(--kt-ai-space-2)"><svg class="kt-icon" aria-hidden="true"><use href="#' + (r.dispute ? "warning" : "check") + '"></use></svg><div class="why">' + trrNote + "</div></div>" +
"</div>" +
'<div class="kt-ai-v" data-gap="tight">' +
'<span class="kt-ai-label">Обоснование</span>' +
'<div class="justif"><span class="k">' + esc(r.init) + "</span><span>" + esc(r.justif) + "</span></div>" +
"</div>" +
"</div>" +
(isDisputed
? '<div class="actions-row">' +
'<button class="kt-ai-btn" data-variant="primary" data-act="approve"><svg class="kt-icon" aria-hidden="true"><use href="#check"></use></svg>Подтвердить решение</button>' +
'<button class="kt-ai-btn" data-variant="danger" data-act="reject"><svg class="kt-icon" aria-hidden="true"><use href="#x"></use></svg>Вернуть инициатору</button>' +
"</div>"
: '<div class="actions-row">' +
'<button class="kt-ai-btn" data-variant="primary" data-act="approve"><svg class="kt-icon" aria-hidden="true"><use href="#check"></use></svg>' + (role === "legal" ? "Согласовать" : "Подписать") + "</button>" +
'<button class="kt-ai-btn" data-variant="danger" data-act="reject-draft"><svg class="kt-icon" aria-hidden="true"><use href="#x"></use></svg>Не согласовать</button>' +
"</div>") +
"</aside>";
}
function bindApprove(role) {
var trs = document.querySelectorAll("#view .kt-ai-table tbody tr[data-id]");
for (var i = 0; i < trs.length; i++) {
trs[i].addEventListener("click", function () {
S.selected = this.getAttribute("data-id");
saveState(); renderApprove();
});
}
var roleSel = document.querySelector("#view .sel-role");
if (roleSel) roleSel.addEventListener("change", function () {
S.regRole = this.value; S.selected = ""; saveState(); renderApprove();
});
var acts = document.querySelectorAll("#view [data-act]");
for (var a = 0; a < acts.length; a++) acts[a].addEventListener("click", onApproveAction);
}
function onApproveAction(ev) {
var act = ev.currentTarget.getAttribute("data-act");
var r = live(S.selected);
if (!r) return;
var m = S.modified[r.id] || {};
var isDisputed = r.st === "disputed";
if (act === "approve") {
var role = S.regRole;
if (role === "legal") { m.st = "toSign"; m.done = "22.09.2026"; }
else { m.st = "sent"; m.disputeReset = true; m.at = nowStr(); m.done = "22.09.2026"; }
S.modified[r.id] = m; saveState();
var msg = isDisputed ? r.id + ": спорный случай разобран, отправлена в 1С" : (role === "legal" ? r.id + " согласована и передана руководителю" : r.id + " подписана и отправлена в 1С");
toast(msg, "ok");
S.selected = ""; saveState(); renderApprove();
} else if (act === "reject" || act === "reject-draft") {
m.st = "rejected"; m.done = "22.09.2026";
if (act === "reject") m.dispute = false;
S.modified[r.id] = m; saveState();
toast(r.id + " возвращена инициатору", "warn");
S.selected = ""; saveState(); renderApprove();
}
}
function nowStr() {
return "22.09.2026, " + new Date().toTimeString().slice(0, 5);
}
// ================= О РЕШЕНИИ =================
function renderAbout() {
var html = "<h1 class=\"kt-ai-h1\">О решении</h1>" +
'<p class="kt-ai-t-md" style="color:var(--kt-ai-fg-muted)">Согласование заявок на закупку филиала — паспорт ИИ-решения.</p>' +
'<div class="kt-ai-v" data-gap="block">' +
'<div class="kt-ai-card" style="padding:var(--kt-ai-space-6)">' +
"<table class=\"pas-table\"><tbody>" +
tr("Наименование", "Согласование заявок на закупку филиала") +
tr("Исполнитель", "Владелец процесса — сборка на платформе Vibe AI") +
tr("Пользователи", "Закупщик · Юрист · Руководитель") +
tr("Дата внедрения", "—") +
"</tbody></table></div>" +
'<div class="kt-ai-card" style="padding:var(--kt-ai-space-6)"><div class="kt-ai-h3" style="margin-bottom:var(--kt-ai-space-4)">Предпосылки</div>' +
'<div class="kt-ai-v" data-gap="tight">' +
p("Заявки на закупку поступают из 1С и письмами, около 150 в месяц.") +
p("Процесс включает ручную проверку данных (код ТРУ, план, сумма) и последовательную передачу между участниками.") +
"</div></div>" +
'<div class="kt-ai-card" style="padding:var(--kt-ai-space-6)"><div class="kt-ai-h3" style="margin-bottom:var(--kt-ai-space-4)">Входные данные и источники</div>' +
'<div class="kt-ai-v" data-gap="tight">' +
p("Заявки из 1С и электронной почты.") +
p("Реестр в Excel: номер, инициатор, сумма, статус, даты.") +
"</div></div>" +
'<div class="kt-ai-card" style="padding:var(--kt-ai-space-6)"><div class="kt-ai-h3" style="margin-bottom:var(--kt-ai-space-4)">Процессы для внедрения ИИ</div>' +
'<div class="kt-ai-v" data-gap="tight">' +
p("Проверка по чек-листу.") +
p("Подсказка кода ТРУ.") +
p("Ведение реестра и напоминания.") +
"</div></div>" +
'<div class="kt-ai-card" style="padding:var(--kt-ai-space-6)"><div class="kt-ai-h3" style="margin-bottom:var(--kt-ai-space-4)">Шаги процесса</div>' +
steps() + "</div>" +
passSteps() +
risks() +
"</div></div>";
document.getElementById("view").innerHTML = html;
}
function tr(k, v) { return "<tr><td>" + esc(k) + "</td><td>" + esc(v) + "</td></tr>"; }
function p(t) { return '<p class="kt-ai-t-md" style="color:var(--kt-ai-fg)">' + esc(t) + "</p>"; }
function steps() {
var steps = [
"Поступление заявки из 1С или по электронной почте",
"Закупщик проверяет код ТРУ, наличие в плане и сумму",
"ИИ подсказывает код ТРУ и ведёт реестр",
"Закупщик возвращает заявку инициатору при ошибке",
"Передача заявки юристу для согласования",
"Руководитель подписывает заявку",
"Закупщик отправляет заявку в 1С и уведомляет инициатора"
];
return '<div class="kt-ai-v" data-gap="tight">' + steps.map(function (s, i) {
return '<div class="pas-step"><span class="kt-ai-num-badge" data-state="' + (i === 1 ? "current" : "done") + '">' + (i + 1) + '</span><div class="b">' + esc(s) + "</div></div>";
}).join("") + "</div>";
}
function passSteps() {
var ai = ["ИИ подсказывает код ТРУ", "ИИ ведёт реестр", "ИИ обновляет статусы и даты"];
var hum = ["Закупщик проверяет код ТРУ, план и сумму", "Закупщик решает, вернуть ли заявку", "Юрист согласовывает", "Руководитель подписывает", "Руководитель разбирает спорные случаи"];
return '<div class="kt-ai-card" style="padding:var(--kt-ai-space-6)"><div class="kt-ai-h3" style="margin-bottom:var(--kt-ai-space-4)">Кто делает что</div>' +
'<div class="wd-grid">' +
'<div class="kt-ai-v" data-gap="tight">' +
'<div class="kt-ai-chip" data-tone="blue">ИИ</div>' +
ai.map(function (s) { return '<div class="pas-step"><span class="kt-ai-num-badge" data-tone="warn">·</span><div class="b">' + esc(s) + "</div></div>"; }).join("") +
"</div>" +
'<div class="kt-ai-v" data-gap="tight">' +
'<div class="kt-ai-chip" data-tone="green">Человек</div>' +
hum.map(function (s) { return '<div class="pas-step"><span class="kt-ai-num-badge">·</span><div class="b">' + esc(s) + "</div></div>"; }).join("") +
"</div></div></div>";
}
function pass() {}
function risks() {
return '<div class="kt-ai-card" style="padding:var(--kt-ai-space-6)"><div class="kt-ai-h3" style="margin-bottom:var(--kt-ai-space-4)">Контроль и риски</div>' +
'<div class="risk-grid">' +
riskItem("Ошибка ИИ в коде ТРУ", "ИИ может ошибаться в коде ТРУ — все коды подтверждает закупщик.") +
riskItem("Дубли", "ИИ может пропустить дубли — закупщик проверяет дубли перед отправкой в 1С.") +
riskItem("Спорные случаи", "Спорные случаи разбирает руководитель, решение фиксируется в реестре.") +
"</div>" +
'<div class="kt-ai-v" data-gap="tight" style="margin-top:var(--kt-ai-space-5)">' +
'<div class="kt-ai-kpi-strip" data-cards="true" style="margin-top:var(--kt-ai-space-5);padding:0">' +
'<div class="kt-ai-kpi"><span class="value" data-long="true">~3 000 ч</span><span class="label">экономия в месяц</span><span class="hint">150 заявок × 20 ч — оценка владельца процесса</span></div>' +
'<div class="kt-ai-kpi"><span class="value" data-long="true">~150</span><span class="label">заявок в месяц</span><span class="hint">из 1С и по почте</span></div>' +
'<div class="kt-ai-kpi"><span class="value" data-long="true">2 роли</span><span class="label">юрист и руководитель</span><span class="hint">проверяют после закупщика</span></div>' +
"</div>" +
'<div class="kt-ai-v" data-gap="tight" style="margin-top:var(--kt-ai-space-2)">' +
'<span class="kt-ai-label">Результат</span>' +
p("Реестр и статус заявки для закупщика и руководителя: меньше времени на обработку и меньше ошибок в кодах ТРУ.") +
'<span class="kt-ai-label">Ожидаемый эффект</span>' +
p("Быстрая обработка заявок, меньше ошибок в кодах ТРУ.") +
'<span class="kt-ai-label">Экономический эффект</span>' +
p("Около 3 000 ч в месяц (150 заявок × 20 ч) — оценка владельца процесса.") +
'<span class="kt-ai-label">Формат реализации</span>' +
p("Веб-интерфейс на Vibe AI: сначала статичный макет, после согласования — рабочее решение.") +
"</div></div>";
}
function riskItem(t, d) {
return '<div class="risk-item"><div class="t"><svg class="kt-icon" aria-hidden="true"><use href="#warning"></use></svg>' + esc(t) + "</div><p>" + esc(d) + "</p></div>";
}
// ================= НАВИГАЦИЯ + СЕССИЯ =================
var CRUMBS = { work: "Филиал · Закупки · Рабочий стол", registry: "Филиал · Закупки · Реестр", approve: "Филиал · Закупки · Согласование", about: "Филиал · Закупки · О решении" };
function render() {
// сбросить выбранную заявку при смене экрана
S.selected = ""; saveState();
if (S.screen === "work") renderWork();
else if (S.screen === "registry") renderRegistry();
else if (S.screen === "approve") renderApprove();
else renderAbout();
// навигация
var items = document.querySelectorAll(".kt-ai-nav-item");
for (var i = 0; i < items.length; i++) {
items[i].setAttribute("data-active", items[i].getAttribute("data-screen") === S.screen ? "true" : "false");
}
// счётчики
var list = allLive();
setCount("work", list.filter(function (r) { return r.st === "new" || r.st === "checking"; }).length);
setCount("approve", list.filter(function (r) { return r.st === "toLegal" || r.st === "toSign" || r.st === "disputed"; }).length);
var crumb = document.getElementById("crumb");
if (crumb) crumb.textContent = CRUMBS[S.screen] || CRUMBS.work;
}
function setCount(id, n) {
document.querySelectorAll('[data-count="' + id + '"]').forEach(function (el) {
el.textContent = n;
});
}
document.addEventListener("DOMContentLoaded", function () {
S = loadState();
var items = document.querySelectorAll(".kt-ai-nav-item");
for (var i = 0; i < items.length; i++) {
(function (a) {
a.addEventListener("click", function (e) {
e.preventDefault();
S.screen = a.getAttribute("data-screen");
S.selected = "";
S.selectedIsDisputed = false;
saveState(); render();
});
})(items[i]);
}
render();
});