ii-reshenie-dlya-protsessa-2/script.js

417 lines
25 KiB
JavaScript
Raw Permalink 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 с seed-набором. */
(function () {
"use strict";
var KEY = "ii-swot-state-v1";
var WEEK = ["08.09", "15.09", "22.09", "29.09", "06.10", "13.10", "20.10", "27.10"];
var SEED = {
orgs: [
{ id: "o1", name: "ДО «Север»", files: 5, kpi: 14, volume: 128.4, date: "16.09.2026", status: "ready" },
{ id: "o2", name: "ДО «Восток»", files: 4, kpi: 11, volume: 86.2, date: "16.09.2026", status: "ready" },
{ id: "o3", name: "ДО «Юг-Транс»",files: 5, kpi: 13, volume: 112.7, date: "17.09.2026", status: "mismatch" },
{ id: "o4", name: "ДО «Запад»", files: 3, kpi: 9, volume: 64.1, date: "17.09.2026", status: "ready" },
{ id: "o5", name: "ДО «Центр»", files: 5, kpi: 14, volume: 143.9, date: "18.09.2026", status: "checked" },
{ id: "o6", name: "ДО «Приозерье»",files: 4, kpi: 10, volume: 71.5, date: "18.09.2026", status: "ready" },
{ id: "o7", name: "ДО «Степь»", files: 4, kpi: 12, volume: 95.3, date: "19.09.2026", status: "mismatch" },
{ id: "o8", name: "ДО «Речной»", files: 3, kpi: 8, volume: 58.6, date: "19.09.2026", status: "pending" }
],
week: [612.0, 648.5, 631.2, 670.8, 655.4, 689.1, 672.6, 760.7],
weekDates: ["10.02.2026", "16.03.2026", "22.04.2026", "29.05.2026", "26.06.2026", "24.07.2026", "21.08.2026", "27.09.2026"],
insights: [
{ org: "ДО «Центр»", tone: "ok", text: "Самый стабильный показатель за период: отклонение от среднего −3,1%." },
{ org: "ДО «Север»", tone: "ok", text: "Объём вырос на 4,2% к предыдущей неделе — выше среднего по своду." },
{ org: "ДО «Речной»", tone: "warn", text: "Файлы получены в 18:42, свод не завершён — организация в статусе «ожидаемо»." },
{ org: "ДО «Юг-Транс»", tone: "warn", text: "Расхождение: показатель за период в файле № 3 не сходится с итоговым на 2,3%." },
{ org: "ДО «Степь»", tone: "warn", text: "Расхождение: в выгрузке отсутствует столбец «итого», требуется уточнение у организации." },
{ org: "ДО «Восток»", tone: "info", text: "Динамика в пределах нормы, изменений нет." }
]
};
var MAIL_SUBJECTS = ["Отчёт за неделю №37", "Выгрузка показателей", "Еженедельный отчёт", "Письмо со вложениями"];
var MAIL_TIMES = ["08:12", "09:47", "11:03", "14:26", "16:58", "18:42"];
var state = load();
var view = { page: "passport", mailFilter: "all", mailSearch: "", mailSel: {}, sumFilter: "all", insightFilter: "all" };
function load() {
try {
var raw = localStorage.getItem(KEY);
if (raw) { var s = JSON.parse(raw); if (s && s.orgs) return s; }
} catch (e) { /* повреждённые данные — пересоздаём */ }
return JSON.parse(JSON.stringify(SEED));
}
function save() { try { localStorage.setItem(KEY, JSON.stringify(state)); } catch (e) { /* квота — просто не сохраняем */ } }
function mailFor(org) {
if (org.mail) return org.mail;
var i = org.id.charCodeAt(1) % MAIL_TIMES.length;
org.mail = {
time: MAIL_TIMES[i],
subject: MAIL_SUBJECTS[org.id.charCodeAt(1) % MAIL_SUBJECTS.length],
size: (org.files * 1.4 + 0.3).toFixed(1)
};
for (var f = 1; f < org.files; f++) { /* имена вложения в drawer */ }
return org.mail;
}
var STATUS = {
ready: { label: "свёл", tone: "green" },
checked: { label: "проверено", tone: "blue" },
mismatch: { label: "расхождение", tone: "orange" },
pending: { label: "ожидаемо", tone: "gray" }
};
var STATUS_ORDER = ["ready", "checked", "mismatch", "pending"];
function $(id) { return document.getElementById(id); }
function el(tag, cls, html) { var e = document.createElement(tag); if (cls) e.className = cls; if (html != null) e.innerHTML = html; return e; }
function fmt(n) { return n.toLocaleString("ru-RU", { minimumFractionDigits: 1, maximumFractionDigits: 1 }); }
function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]; }); }
/* ---------- навигация ---------- */
function setPage(p) {
view.page = p;
document.querySelectorAll("#appNav button").forEach(function (b) {
b.setAttribute("data-active", b.getAttribute("data-page") === p ? "true" : "false");
});
document.querySelectorAll(".page").forEach(function (s) {
s.hidden = s.id !== "page-" + p;
});
if (p === "inbox") renderInbox();
if (p === "summary") renderSummary();
if (p === "analysis") renderAnalysis();
window.scrollTo(0, 0);
}
/* ---------- тосты ---------- */
function toast(text, tone, action, cb) {
var t = el("div", "kt-ai-toast", esc(text));
if (tone) t.dataset.tone = tone;
if (action) {
var b = el("button", "kt-ai-btn", action);
b.dataset.size = "sm";
b.style.marginLeft = "var(--kt-ai-space-3)";
b.onclick = function () { cb && cb(); t.remove(); };
t.appendChild(b);
}
$("toasts").appendChild(t);
setTimeout(function () { t.style.transition = "opacity .3s"; t.style.opacity = "0"; setTimeout(function () { t.remove(); }, 320); }, 4200);
}
/* ---------- модалка ---------- */
function modal(title, bodyHtml, actions) {
var box = $("modalBox");
box.innerHTML = '<div class="modal-title">' + esc(title) + "</div>" + bodyHtml +
'<div class="modal-actions">' + (actions || "") + "</div>";
$("modalOvl").style.display = "flex";
box.querySelectorAll("button[data-close]").forEach(function (b) { b.onclick = closeModal; });
}
function closeModal() { $("modalOvl").style.display = "none"; }
$("modalOvl").addEventListener("click", function (e) { if (e.target === this) closeModal(); });
document.addEventListener("keydown", function (e) { if (e.key === "Escape") { closeModal(); closeDrawer(); } });
/* ---------- drawer ---------- */
function openDrawer(html) { $("drawer").innerHTML = html; $("ovl").style.display = "block"; }
function closeDrawer() { $("ovl").style.display = "none"; }
$("ovlBg").addEventListener("click", closeDrawer);
function orgDrawer(org) {
var st = STATUS[org.status];
var files = "";
for (var i = 1; i <= org.files; i++) {
files += '<div class="kt-ai-doc"><div class="doc-ic"><svg class="kt-icon"><use href="#spreadsheet"></use></svg></div>' +
'<div class="doc-meta"><span class="doc-name">' + esc(org.name) + " — файл " + i + ".xlsx</span>" +
'<span class="doc-sub">' + i + ".xlsx · " + (0.4 + i * 0.3).toFixed(1) + " МБ</span></div>" +
'<button class="kt-ai-btn" data-size="sm"><svg class="kt-icon"><use href="#download"></use></svg></button></div>';
}
var issues = org.status === "mismatch"
? '<div class="insight" data-tone="warn" style="margin-top: var(--kt-ai-space-5)"><svg class="kt-icon"><use href="#warning"></use></svg><div><div class="t">Нужна сверка</div><div class="d">ИИ обнаружил расхождение показателей между файлами. Строки подсвечены в сводном файле, пока статус не станет «свёл».</div></div></div>'
: "";
openDrawer(
'<div style="display:flex; align-items:center; gap: var(--kt-ai-space-4); margin-bottom: var(--kt-ai-space-6)">' +
'<h2 style="margin:0; font-size: var(--kt-ai-text-lg); font-weight: var(--kt-ai-weight-semibold)">' + esc(org.name) + "</h2>" +
'<span class="kt-ai-chip" data-tone="' + st.tone + '">' + st.label + "</span></div>" +
'<div class="kv" style="display:flex; gap: var(--kt-ai-space-5)"><div class="kv"><span class="k">Файлов получено</span><span class="v">' + org.files + "</span></div>" +
'<div class="kv"><span class="k">Показателей</span><span class="v">' + org.kpi + "</span></div>" +
'<div class="kv"><span class="k">Объём</span><span class="v">' + fmt(org.volume) + " млн</span></div></div>" +
'<h3 style="margin: var(--kt-ai-space-7) 0 var(--kt-ai-space-4); font-size: var(--kt-ai-text-sm); font-weight: var(--kt-ai-weight-semibold)">Файлы из письма</h3>' +
'<div class="kt-ai-doc-list" style="display:flex; flex-direction:column; gap: var(--kt-ai-space-3)">' + files + "</div>" +
issues +
'<div class="kt-ai-drawer-actions" style="display:flex; justify-content:flex-end; gap: var(--kt-ai-space-3); margin-top: var(--kt-ai-space-8)">' +
(org.status === "pending" ? '<button class="kt-ai-btn" data-act="open">Открыть письмо</button>' : "") +
'<button class="kt-ai-btn" data-variant="primary" onclick="App.goSummary(\'' + org.id + '\')">К своду</button></div>'
);
}
/* ================= ВХОДЯЩИЕ ================= */
function filteredMails() {
var q = view.mailSearch.toLowerCase();
return state.orgs.filter(function (o) {
if (view.mailFilter === "new" && o.status !== "pending" && o.status !== "ready") return false;
if (view.mailFilter === "attention" && o.status !== "mismatch" && o.status !== "pending") return false;
if (view.mailFilter === "done" && o.status !== "checked") return false;
if (q && o.name.toLowerCase().indexOf(q) === -1 && mailFor(o).subject.toLowerCase().indexOf(q) === -1) return false;
return true;
});
}
function renderInbox() {
var orgs = filteredMails();
var totalFiles = state.orgs.reduce(function (a, o) { return a + o.files; }, 0);
var filesIn = orgs.reduce(function (a, o) { return a + o.files; }, 0);
var pending = state.orgs.filter(function (o) { return o.status === "pending"; }).length;
var mismatch = state.orgs.filter(function (o) { return o.status === "mismatch"; }).length;
var checked = state.orgs.filter(function (o) { return o.status === "checked"; }).length;
$("kpiInbox").innerHTML = kpi(fmtCount(state.orgs.length), "организаций", "пришлют отчёты за неделю") +
kpi(totalFiles, "файлов", "из писем за период") +
kpi(pending + mismatch, "требуют внимания", "ожидаемо или с расхождением") +
kpi(checked, "проверено", "человеком, готово в отчёт");
var chips = [
["all", "Все", state.orgs.length],
["new", "Новые", state.orgs.filter(function (o) { return o.status === "pending" || o.status === "ready"; }).length],
["attention", "Требуют внимания", pending + mismatch],
["done", "Завершено", checked]
];
$("mailFilters").innerHTML = chips.map(function (c) {
return '<button class="kt-ai-filter-chip" data-f="' + c[0] + '" data-active="' + (view.mailFilter === c[0]) + '"><span class="k">' + c[1] + "</span> " + c[2] + "</button>";
}).join("");
document.querySelectorAll("#mailFilters .kt-ai-filter-chip").forEach(function (b) {
b.onclick = function () { view.mailFilter = b.dataset.f; renderInbox(); };
});
var rows = orgs.map(function (o) {
var m = mailFor(o), st = STATUS[o.status];
var sel = !!view.mailSel[o.id];
return '<tr class="row-click" data-id="' + o.id + '" style="' + (sel ? "background: var(--kt-ai-bg-active)" : "") + '">' +
'<td><input type="checkbox"' + (sel ? " checked" : "") + ' aria-label="Выбрать ' + esc(o.name) + '"></td>' +
'<td>' + (o.status === "pending" ? '<svg class="kt-icon" style="width:16px;height:16px;color:var(--kt-ai-status-warn-fg)"><use href="#warning"></use></svg>' : '<svg class="kt-icon" style="width:16px;height:16px;color:var(--kt-ai-fg-faint)"><use href="#spreadsheet"></use></svg>') + "</td>" +
'<td><div style="font-size: var(--kt-ai-text-sm); font-weight: var(--kt-ai-weight-medium)">' + esc(o.name) + "</div></td>" +
'<td style="color: var(--kt-ai-fg-muted); font-size: var(--kt-ai-text-xs)">' + esc(m.subject) + "</td>" +
'<td class="num">' + o.files + "</td>" +
'<td style="font-size: var(--kt-ai-text-xs); font-family: var(--kt-ai-font-mono); color: var(--kt-ai-fg-muted)">' + esc(o.date) + " " + m.time + (o.status === "pending" ? ' <span style="color:var(--kt-ai-status-warn-fg)">· недавно</span>' : "") + "</td>" +
'<td><span class="kt-ai-chip" data-tone="' + st.tone + '">' + st.label + "</span></td></tr>";
}).join("");
$("mailRows").innerHTML = rows;
$("mailEmpty").hidden = orgs.length > 0;
$("mailCount").textContent = "Показано " + orgs.length + " из " + state.orgs.length + " · файлов: " + filesIn;
$("ctInbox").textContent = state.orgs.length;
document.querySelectorAll("#mailTable tr.row-click").forEach(function (tr) {
tr.onclick = function (e) {
if (e.target.closest("button")) return;
if (e.target.tagName === "INPUT") {
var id = tr.dataset.id;
view.mailSel[id] = e.target.checked;
renderInbox();
return;
}
orgDrawer(byId(tr.dataset.id));
};
});
}
function kpi(v, l, h) { return '<div class="kt-ai-kpi"><span class="value" data-long="true">' + v + "</span><span class="label\">" + l + '</span><span class="hint">' + h + "</span></div>"; }
function fmtCount(n) { return String(n); }
function byId(id) { return state.orgs.filter(function (o) { return o.id === id; })[0]; }
function processMails() {
var pending = state.orgs.filter(function (o) { return o.status === "pending"; });
var mismatch = state.orgs.filter(function (o) { return o.status === "mismatch"; });
if (!pending.length && !mismatch.length) {
toast("Все письма уже обработаны", "ok");
return;
}
var names = pending.concat(mismatch).map(byId).map(function (o) { return o.name; });
modal("Свести файлы",
"<p style='margin:0 0 var(--kt-ai-space-4); color: var(--kt-ai-fg-muted); font-size: var(--kt-ai-text-sm)'>ИИ разберёт файлы из " + names.length + " писем, обновит сводные показатели и отметит расхождения:</p>" +
"<div style='display:flex; flex-direction:column; gap: var(--kt-ai-space-2);'>" +
names.map(function (n) { return '<div style="display:flex; gap: var(--kt-ai-space-3); align-items:center; font-size: var(--kt-ai-text-sm)"><svg class="kt-icon" style="width:15px;height:15px;color:var(--kt-ai-primary)"><use href="#sparkles"></use></svg>' + esc(n) + "</div>"; }).join("") +
"</div>",
'<button class="kt-ai-btn" data-close>Отмена</button><button class="kt-ai-btn" data-variant="primary" id="btnConfirmProcess">Свести</button>');
$("btnConfirmProcess").onclick = function () {
state.orgs.forEach(function (o) {
if (o.status === "pending" || o.status === "mismatch") o.status = "ready";
});
save(); closeModal();
toast("Файлы сведены. Показатели обновлены", "ok", null, null);
renderInbox(); refreshKpiSummary();
setPage("summary");
};
}
/* ================= СВОД ================= */
function renderSummary() {
var orgs = state.orgs.filter(function (o) { return view.sumFilter === "all" || o.status === view.sumFilter; });
var tot = orgs.reduce(function (a, o) { return { f: a.f + o.files, k: a.k + o.kpi, v: a.v + o.volume }; }, { f: 0, k: 0, v: 0 });
var mismatch = orgs.filter(function (o) { return o.status === "mismatch"; }).length;
var checked = orgs.filter(function (o) { return o.status === "checked"; }).length;
$("kpiSum").innerHTML = kpi(state.orgs.length, "организаций в своде", "отчётная неделя 14–20.09") +
kpi(fmt(tot.v), "млн, всего", "сумма показателей") +
kpi(mismatch, "с расхождениями", "нужны перед проверкой") +
kpi(checked, "проверено", "подтверждены человеком");
var filters = [
["all", "Все", state.orgs.length],
["ready", "Сведёно", state.orgs.filter(function (o) { return o.status === "ready"; }).length],
["checked", "Проверено", checked],
["mismatch", "Расхождение", mismatch],
["pending", "Ожидаемо", state.orgs.filter(function (o) { return o.status === "pending"; }).length]
];
$("sumFilters").innerHTML = filters.map(function (c) {
return '<button class="kt-ai-filter-chip" data-f="' + c[0] + '" data-active="' + (view.sumFilter === c[0]) + '"><span class="k">' + c[1] + "</span> " + c[2] + "</button>";
}).join("");
document.querySelectorAll("#sumFilters .kt-ai-filter-chip").forEach(function (b) {
b.onclick = function () { view.sumFilter = b.dataset.f; renderSummary(); };
});
var rows = orgs.map(function (o) {
var st = STATUS[o.status];
var hl = o.status === "mismatch" ? ' <span style="color:var(--kt-ai-status-warn-fg); font-size: var(--kt-ai-text-xs)">' + "проверить" + "</span>" : "";
return '<tr class="row-click" data-id="' + o.id + '"><td style="font-weight: var(--kt-ai-weight-medium)">' + esc(o.name) + "</td>" +
'<td class="num">' + o.files + "</td>" +
'<td class="num">' + o.kpi + "</td>" +
'<td class="num">' + fmt(o.volume) + hl + "</td>" +
'<td style="font-size: var(--kt-ai-text-xs); font-family: var(--kt-ai-font-mono); color: var(--kt-ai-fg-muted)">' + esc(o.date) + "</td>" +
'<td><span class="kt-ai-chip" data-tone="' + st.tone + '">' + st.label + "</span></td></tr>";
}).join("");
rows += "<tr style='background: var(--kt-ai-bg-soft)'><td style='font-weight: var(--kt-ai-weight-semibold)'>Всего</td>" +
'<td class="num" style="font-weight: var(--kt-ai-weight-semibold)">' + tot.f + "</td>" +
'<td class="num" style="font-weight: var(--kt-ai-weight-semibold)">' + tot.k + "</td>" +
'<td class="num" style="font-weight: var(--kt-ai-weight-semibold); color: var(--kt-ai-primary)">' + fmt(tot.v) + "</td>" +
"<td></td><td></td></tr>";
$("sumRows").innerHTML = rows;
$("sumEmpty").hidden = orgs.length > 0;
document.querySelectorAll("#sumTable tr.row-click").forEach(function (tr) {
tr.onclick = function () { orgDrawer(byId(tr.dataset.id)); };
});
}
function refreshKpiSummary() { if (view.page === "summary") renderSummary(); }
/* ================= АНАЛИЗ ================= */
function renderAnalysis() {
var w = state.week;
var last = w[w.length - 1], prev = w[w.length - 2];
var max = Math.max.apply(null, w), maxI = w.indexOf(max);
var avg = w.reduce(function (a, b) { return a + b; }, 0) / w.length;
var delta = (last - prev) / prev * 100;
$("kpiAna").innerHTML = kpi(fmt(last), "млн, текущая неделя", state.weekDates[w.length - 1]) +
kpi(fmt(avg), "млн, среднее", "за 8 недель") +
kpi((delta >= 0 ? "+" : "") + fmt(delta) + "%", "к предыдущей", "неделя vs неделя") +
kpi(state.orgs.length, "организаций", "в сводном файле");
drawChart(w, max);
$("chMaxV").textContent = fmt(max);
$("chMaxD").textContent = state.weekDates[maxI];
$("chAvgV").textContent = fmt(avg);
$("chDeltaV").textContent = (delta >= 0 ? "+" : "") + fmt(delta) + "%";
var ins = state.insights.filter(function (i) {
if (view.insightFilter === "attention") return i.tone === "warn";
if (view.insightFilter === "ok") return i.tone === "ok";
return true;
});
$("insFilter").innerHTML = [
["all", "Все", state.insights.length],
["attention", "Требуют внимания", state.insights.filter(function (i) { return i.tone === "warn"; }).length],
["ok", "В норме", state.insights.filter(function (i) { return i.tone === "ok"; }).length]
].map(function (c) {
return '<button class="kt-ai-filter-chip" data-f="' + c[0] + '" data-active="' + (view.insightFilter === c[0]) + '"><span class="k">' + c[1] + "</span> " + c[2] + "</button>";
}).join("");
document.querySelectorAll("#insFilter .kt-ai-filter-chip").forEach(function (b) {
b.onclick = function () { view.insightFilter = b.dataset.f; renderAnalysis(); };
});
$("insightsList").innerHTML = ins.map(function (i) {
var icon = i.tone === "warn" ? "warning" : i.tone === "ok" ? "check-circle" : "info";
return '<div class="insight" data-tone="' + i.tone + '"><svg class="kt-icon"><use href="#' + icon + '"></use></svg>' +
'<div><div class="t">' + esc(i.org) + "</div><div class=\"d\">" + esc(i.text) + "</div></div></div>";
}).join("");
}
function drawChart(vals, max) {
var svg = $("chartSvg");
var W = 640, H = 210, padL = 8, padB = 24, padT = 18;
var n = vals.length;
var cw = (W - padL * 2) / n;
var barW = 26;
var out = [];
vals.forEach(function (v, i) {
var h = (v / max) * (H - padB - padT);
var x = padL + i * cw + (cw - barW) / 2;
var y = H - padB - h;
var label = WEEK[i];
var tip = state.weekDates[i] + ": " + fmt(v) + " млн";
out.push('<rect class="cc-bar" x="' + x + '" y="' + y + '" width="' + barW + '" height="' + h + '" rx="3" tabindex="0" data-tip="' + tip + '"></rect>');
out.push('<text class="cc-lbl" x="' + (x + barW / 2) + '" y="' + (H - 8) + '" text-anchor="middle">' + label + "</text>");
out.push('<text class="cc-val" x="' + (x + barW / 2) + '" y="' + (y - 6) + '" text-anchor="middle">' + Math.round(v) + "</text>");
});
svg.innerHTML = out.join("");
svg.querySelectorAll(".cc-bar").forEach(function (r) {
r.addEventListener("mouseenter", function () { r.setAttribute("data-hover", "true"); });
r.addEventListener("mouseleave", function () { r.removeAttribute("data-hover"); });
r.addEventListener("focus", function () { r.setAttribute("data-hover", "true"); });
r.addEventListener("blur", function () { r.removeAttribute("data-hover"); });
});
}
/* ================= действия ================= */
function downloadSelected() {
var sel = state.orgs.filter(function (o) { return view.mailSel[o.id]; });
if (!sel.length) { toast("Сначала выберите письма в таблице (клик по строке)", "warn"); return; }
toast("Подготовка " + sel.length + " файлов…", "info");
setTimeout(function () {
toast("Готово: " + sel.reduce(function (a, o) { return a + o.files; }, 0) + " файлов в одну папку", "ok");
}, 900);
}
function exportSwat() {
toast("Формируем единый Excel SWAT…", "info");
setTimeout(function () {
modal("Excel SWAT готов",
"<div class=\"kt-ai-doc\" style=\"margin-bottom: var(--kt-ai-space-5)\"><div class=\"doc-ic\"><svg class=\"kt-icon\"><use href=\"#spreadsheet\"></use></svg></div>" +
"<div class=\"doc-meta\"><span class=\"doc-name\">SWAT_дочки_14-20.09.2026.xlsx</span>" +
'<span class="doc-sub">' + state.orgs.length + " организаций · " + state.orgs.reduce(function (a, o) { return a + o.kpi; }, 0) + " показателей</span></div></div>" +
"<p style='margin:0; font-size: var(--kt-ai-text-sm); color: var(--kt-ai-fg-muted)'>Свод по всем дочерним организациям с анализом. Остаток: " +
state.orgs.filter(function (o) { return o.status === "mismatch" || o.status === "pending"; }).length + " позиций требуют внимания.</p>",
'<button class="kt-ai-btn" data-close>Закрыть</button><button class="kt-ai-btn" data-variant="primary" data-act="dl">Скачать файл</button>');
var dl = $("modalBox").querySelector("[data-act=dl]");
if (dl) dl.onclick = function () { closeModal(); toast("Файл сохранён в загрузки", "ok"); };
}, 1100);
}
function refreshMail() {
state.orgs.filter(function (o) { return o.status === "pending"; }).forEach(function (o) {
o.status = "ready";
});
save();
toast("Почта проверена: новые письма не найдены", "ok");
renderInbox();
}
/* ---------- init ---------- */
document.querySelectorAll("#appNav button").forEach(function (b) {
b.onclick = function () { setPage(b.getAttribute("data-page")); };
});
$("btnProcess").onclick = processMails;
$("btnDownload").onclick = downloadSelected;
$("btnRefresh").onclick = refreshMail;
$("btnExport").onclick = exportSwat;
$("mailSearch").addEventListener("input", function (e) {
view.mailSearch = e.target.value;
renderInbox();
});
window.App = {
goSummary: function (id) { closeDrawer(); view.sumFilter = "all"; renderSummary(); setPage("summary"); orgDrawer(byId(id)); },
resetMailFilters: function () {
view.mailFilter = "all"; view.mailSearch = ""; $("mailSearch").value = "";
renderInbox();
}
};
setPage("passport");
})();