"use strict"; /* Экспертиза документов для советов — статичный прототип, состояние в localStorage. */ var LS_KEY = "edds-v1"; function $(id) { return document.getElementById(id); } function esc(s) { return String(s == null ? "" : s).replace(/[&<>"']/g, function (c) { return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]; }); } function escJs(s) { return esc(String(s == null ? "" : s).replace(/\\/g, "\\\\").replace(/'/g, "\\'")); } var ST = { wait: { label: "Ожидает проверки", tone: "warn" }, ready: { label: "Готово к анализу", tone: "ok" }, process: { label: "Процесс отбора", tone: "info" }, qual: { label: "Квалификация", tone: "info" }, contra: { label: "Противоречие", tone: "risk" }, checked: { label: "На проверке", tone: "info" }, conf: { label: "Подтверждено", tone: "ok" }, falsep: { label: "Ложное срабатывание", tone: "muted" }, draft: { label: "Черновик", tone: "warn" }, sent: { label: "Готово к согласованию", tone: "info" }, agree: { label: "Согласовано", tone: "ok" }, decl: { label: "Отклонено", tone: "risk" }, pending: { label: "Ожидает решения", tone: "warn" } }; function pill(key, label) { var st = ST[key] || { label: label || key, tone: "muted" }; return '' + esc(st.label) + ""; } function num(s) { return '' + s + ""; } function icon(name) { return ''; } /* ---------- Демо-данные ---------- */ function seed() { return { view: "upload", req: "Кандидат: А. Т. Нурмолдаев · SEO-1, управление сетевых сервисов · заявка от 14.09.2026", candidate: "А. Т. Нурмолдаев", sources: [ { n: "Трудовой кодекс Республики Казахстан", s: "Законодательство", d: 207, u: 1 }, { n: "Закон Республики Казахстан «Об акционерных обществах»", s: "Законодательство", d: 148, u: 1 }, { n: "Правила трудового распорядка", s: "Внутренние документы", d: 12, u: 1 }, { n: "Правила поиска и подбора персонала", s: "Внутренние документы", d: 8, u: 1 }, { n: "Корпоративный стандарт по управлению человеческими ресурсами фонда", s: "Внутренние документы", d: 96, u: 1 } ], files: [ { n: "Пояснительная записка на назначение — Нурмолдаев А.Т.docx", t: "Пояснительная записка", d: "14.09.2026, 10:24", s: "ready" }, { n: "План отбора 2026-09 — управление сетевых сервисов.docx", t: "Подтверждающий документ", d: "14.09.2026, 10:24", s: "ready" }, { n: "Протокол собеседования №18-09.pdf", t: "Подтверждающий документ", d: "14.09.2026, 10:25", s: "ready" }, { n: "Резюме кандидата — Нурмолдаев А.Т.pdf", t: "Подтверждающий документ", d: "14.09.2026, 10:28", s: "wait" }, { n: "Анкета о соответствии требованиям.docx", t: "Подтверждающий документ", d: "14.09.2026, 10:31", s: "ready" }, { n: "Проверка требований (внутренняя форма) — скан.pdf", t: "Подтверждающий документ", d: "14.09.2026, 10:32", s: "ready" }, { n: "Справка о стаже и опыте работы.docx", t: "Подтверждающий документ", d: "14.09.2026, 10:33", s: "ready" }, { n: "Диплом о высшем образовании (копия).pdf", t: "Подтверждающий документ", d: "14.09.2026, 10:35", s: "ready" } ], running: false, log: [ { t: "10:42", b: "Пакет документов принят, 8 файлов" }, { t: "10:42", b: "Извлечение текста: пояснительная записка, 12 разд." }, { t: "10:43", b: "Сверка с Правилами поиска и подбора персонала: раздел «Организация отбора», п. 4.2" }, { t: "10:43", b: "Найдено отклонение: дата объявления о вакансии" }, { t: "10:44", b: "Сверка с Правилами трудового распорядка, гл. 5 «Назначение руководителей»" }, { t: "10:44", b: "Сверка с Законом «Об АО»: компетенции совета директоров, ст. 36" }, { t: "10:45", b: "Скрытое голосование: противоречие в протоколе и правилах" }, { t: "10:45", b: "Проверка квалификации: стаж, образование, аттестация" }, { t: "10:46", b: "Обнаружено 4 несоответствия, 1 противоречие" }, { t: "10:46", b: "Журнал завершён — выводы переданы на верификацию эксперту" } ], found: 4, contra: true, violations: [ { d: "Объявление о вакансии опубликовано за 5 календарных дней до открытия отбора, норматив — 10", y: "process", r: "Правила поиска и подбора персонала, раздел 4.2", v: "conf" }, { d: "В пояснительной записке 3 кандидата, в протоколе собеседования фиксированы 2", y: "process", r: "План отбора 2026-09, п. 3", v: "conf" }, { d: "Срок согласования кандидатуры с материнской компанией — 2 дня при нормативе 5", y: "process", r: "Корпоративный стандарт по УХР, ред. 12", v: "conf" }, { d: "У кандидата 6 лет опыта в телекоммуникациях при требуемых 7 для уровня SEO-1", y: "qual", r: "Должностная инструкция SEO-1, п. 2 «Требования к квалификации»", v: "conf" }, { d: "Стаж в должности SEO-3: 3 года 4 месяца при нормативе не менее 4 лет", y: "qual", r: "Должностная инструкция SEO-2 и SEO-3, п. 2", v: "conf" }, { d: "Аттестация по управленческой программе в материалах не подтверждена", y: "qual", r: "Корпоративный стандарт по УХР, гл. 8", v: "conf" }, { d: "В протоколе — решение голосованием 4:1, в правилах назначений — единогласно", y: "contra", r: "Трудовой Кодекс РК, ст. 165, часть 3", v: "falsep" }, { d: "Внутренняя форма проверки заполнена не полностью — графа «Аттестация»", y: "process", r: "Правила трудового распорядка, приложение 2", v: "falsep" } ], verdict: "Заявка на назначение руководителя уровня SEO-1, управление сетевых сервисов (кандидат А. Т. Нурмолдаев) прошла проверку соответствия нормативной базе. По завершённой сверке зафиксированы следующие несоответствия:\n\n1. Объявление о вакансии опубликовано за 5 календарных дней до открытия отбора, норматив — 10 (Правила поиска и подбора персонала, раздел 4.2).\n2. В пояснительной записке указан круг из 3 кандидатов, в протоколе собеседования зафиксировано 2 (План отбора 2026-09, п. 3).\n3. Срок согласования кандидатуры с материнской компанией составил 2 дня при нормативе 5 (Корпоративный стандарт по УХР, ред. 12).\n4. Стаж в отрасли 6 лет при требуемых 7 для уровня SEO-1 (Должностная инструкция SEO-1, п. 2).\n5. Стаж в должности SEO-3 — 3 года 4 месяца при нормативе не менее 4 лет (Должностная инструкция SEO-2 и SEO-3, п. 2).\n6. Аттестация по управленческой программе в материалах не подтверждена (Корпоративный стандарт по УХР, гл. 8).\n\nПротиворечие в способе принятия решения (протокол — голосование 4:1, правила — единогласно) рассмотрено экспертом: позиция кандидата, поданная в установленном порядке, исключает применение требования единогласия; вывод системы отклонён как не относящийся к рассматриваемому пакету.\n\nРешение о назначении принимается с учётом перечисленных несоответствий. Материалы переданы на согласование руководителю.", checklist: [1, 2, 3, 4, 5, 6], status: "sent", rejected: null, stamp: null }; } var D = null; function load() { try { var raw = localStorage.getItem(LS_KEY); D = raw ? JSON.parse(raw) : seed(); } catch (e) { D = seed(); } if (!D.files || !D.violations) D = seed(); } function save() { try { localStorage.setItem(LS_KEY, JSON.stringify(D)); } catch (e) {} } /* ---------- Прогресс процесса ---------- */ function allFilesReady() { for (var i = 0; i < D.files.length; i++) if (D.files[i].s !== "ready") return false; return true; } function doneCount() { var n = 0, i; for (i = 0; i < D.violations.length; i++) if (D.violations[i].v === "conf") n++; return n; } function stepNum() { if (D.view === "upload") return D.running || allFilesReady() ? 2 : 1; if (D.view === "results") return D.contra && !D.falseDone ? 2 : 3; if (D.view === "verdict") return 3; if (D.view === "approve") return D.status === "agree" || D.status === "decl" ? 5 : 4; return 1; } var STEPS = ["Загрузка", "Анализ", "Заключение", "Согласование"]; /* ---------- UI: тосты, модалки ---------- */ var S = {}; function toast(msg, tone) { var box = $("toasts"); if (!box) return; var el = document.createElement("div"); el.className = "kt-ai-toast"; if (tone) el.dataset.tone = tone; el.textContent = msg; box.appendChild(el); setTimeout(function () { el.style.transition = "opacity .3s, transform .3s"; el.style.opacity = "0"; el.style.transform = "translateY(6px)"; setTimeout(function () { if (el.parentNode) el.parentNode.removeChild(el); }, 320); }, 2600); } function openModal(html) { var m = $("modal"); m.innerHTML = html; m.style.display = "flex"; } function closeModal() { var m = $("modal"); m.style.display = "none"; m.innerHTML = ""; } S.closeModal = closeModal; function openDrawer(html) { var d = $("drawer"); d.innerHTML = html; d.style.display = "block"; } function closeDrawer() { var d = $("drawer"); d.style.display = "none"; d.innerHTML = ""; } /* ---------- Навигация и рендер ---------- */ var NAV = [ { id: "upload", label: "Загрузка и обзор", icon: "upload" }, { id: "results", label: "Результаты анализа", icon: "sparkles" }, { id: "verdict", label: "Формирование заключения", icon: "pencil" }, { id: "approve", label: "Согласование", icon: "check-circle" }, { id: "about", label: "О решении", icon: "info", group: "Решение" } ]; var TITLES = { upload: "Загрузка и обзор материалов", results: "Результаты анализа ИИ", verdict: "Формирование заключения", approve: "Согласование руководителем", about: "О решении" }; var SUBS = { upload: "Пакет документов · первичное ознакомление", results: "Сверка процесса · проверка квалификации", verdict: "Верификация выводов · итоговый документ", approve: "Итоговое заключение · решение руководителя", about: "Паспорт ИИ-решения" }; function renderNav() { var nav = $("nav"); if (!nav) return; var group = null, html = ""; for (var i = 0; i < NAV.length; i++) { var n = NAV[i]; if (n.group && n.group !== group) { html += '
' + esc(n.group) + "
"; group = n.group; } if (!n.group) group = null; var active = D.view === n.id ? ' data-active="true"' : ""; html += '
' + '' + esc(n.label) + "
"; (function (id) { nav.lastChild.onclick = function () { S.go(id); }; })(n.id); } var sb = $("sbReq"); if (sb) sb.innerHTML = '
Текущая заявка
' + esc(D.req) + "
"; } function render() { var html; if (D.view === "upload") html = vUpload(); else if (D.view === "results") html = vResults(); else if (D.view === "verdict") html = vVerdict(); else if (D.view === "approve") html = vApprove(); else html = vAbout(); var view = $("view"); if (!view) return; view.innerHTML = html; $("pageTitle").textContent = TITLES[D.view] || ""; $("pageSub").textContent = SUBS[D.view] || ""; $("roleChip").textContent = (D.view === "approve") ? "Руководитель" : "Эксперт"; renderNav(); save(); } function vStepper() { var cur = stepNum(); var html = '
'; for (var i = 0; i < STEPS.length; i++) { var st = i + 1 < cur ? "done" : (i + 1 === cur ? "current" : ""); var n = i + 1 < cur ? "✓" : String(i + 1); html += '
' + n + "" + esc(STEPS[i]) + "
"; if (i < STEPS.length - 1) html += ''; } return html + "
"; } function kpiStrip(items) { return '
' + items.map(function (k) { return '
' + esc(k.l) + '' + k.v + "" + (k.h ? '' + esc(k.h) + "" : "") + "
"; }).join("") + "
"; } /* ---------- Экран 1. Загрузка и обзор материалов ---------- */ function vUpload() { var readyCount = 0; for (var i = 0; i < D.files.length; i++) if (D.files[i].s === "ready") readyCount++; var all = readyCount === D.files.length; var head = ""; if (all && !D.running) { head = '
' + 'Все материалы получены — пакет готов к запуску анализа' + '
'; } var rows = D.files.map(function (f, i) { return "" + '' + esc(f.n) + '' + '' + esc(f.t) + "" + '' + esc(f.d) + "" + '' + pill(f.s) + "" + '
" + ""; }).join(""); return '
Процесс' + '' + 'Заявка ' + num("ЭД-2026-118") + "
" + vStepper() + "
" + head + '
Материалы' + '' + '' + num(readyCount + " из " + D.files.length) + " готово
" + '
' + '' + "" + rows + "
ФайлТипЗагруженСтатус
" + '
Источники для сверки' + '' + '' + num(D.sources.length) + " источника
" + '
' + D.sources.map(function (s) { var pct = s.u ? 100 : 82; return '
' + esc(s.n) + "
" + '
Класс' + esc(s.s) + "" + 'Разделов' + num(s.d) + "" + 'Обновлён01.09.2026
' + '
' + '
' + (s.u ? pill("ok", "Синхронизирован") : pill("checked", "Синхронизация")) + "
"; }).join("") + "
" + '

Что на этом экране

' + "

Перед запуском анализа проверьте комплектность: для сверки нужны пояснительная записка и подтверждающие материалы. " + "Анализ идёт по пяти источникам — законодательству и внутренним правилам. Статус «Готово к анализу» появляется после проверки каждого файла.

"; } /* ---------- Экран 2. Результаты анализа ИИ ---------- */ function falseCount() { var n = 0; for (var i = 0; i < D.violations.length; i++) if (D.violations[i].v === "falsep") n++; return n; } function vResults() { var confN = doneCount(), falseN = falseCount(); var procN = 0, qualN = 0, contraN = 0; for (var i = 0; i < D.violations.length; i++) { var y = D.violations[i].y; if (y === "process") procN++; else if (y === "qual") qualN++; else if (y === "contra") contraN++; } var kpis = kpiStrip([ { l: "Несогласованностей", v: num(confN + falseN) }, { l: "Процесс отбора", v: num(procN) }, { l: "Квалификация", v: num(qualN) }, { l: "Противоречий", v: num(contraN) } ]); var banner = ""; if (D.running) return runScreen(); if (D.contra) { var contraRow = null; for (var ci = 0; ci < D.violations.length; ci++) if (D.violations[ci].y === "contra") contraRow = ci; banner = '
' + 'Найдено противоречие между документами' + 'Протокол — голосование 4:1, Правила назначений — единогласно. ' + (contraRow != null ? "Проверьте вывод №" + num(contraRow + 1) + " вручную." : "Противоречие разобрано.") + "
"; } var typeTone = { process: "blue", qual: "purple", contra: "red" }; var rows = D.violations.map(function (v, i) { return "" + '' + esc(v.r) + "" + '' + esc(v.d) + "" + '' + esc(ST[v.y].label) + "" + '' + pill(v.v) + "" + '
" + '
" + ""; }).join(""); var logRows = D.log.map(function (l) { return '
' + esc(l.t) + '' + esc(l.b) + "
"; }).join(""); var nextOk = true; for (var k = 0; k < D.violations.length; k++) if (D.violations[k].v === "checked") nextOk = false; var next = ""; if (nextOk) { next = '
' + '' + '
'; } return kpis + banner + '
Несогласованности
' + '
' + '' + "" + rows + "
Ссылка на пунктНарушениеТипСтатусДействие
" + next + "
" + '
Журнал действия ИИ' + '10:42 — 10:46
' + '
' + logRows + "
" + '

Что показывает анализ

' + "

Система сверяет процесс отбора с внутренними документами и законодательством, затем проверяет квалификацию кандидата по должностной инструкции. " + "Каждый вывод ссылается на пункт нормативного акта — эксперт проверяет ссылки и при необходимости отмечает ложные срабатывания.

"; } function runScreen() { var steps = [ "Извлечение текста из пакета документов", "Сверка с законодательством и внутренними документами", "Проверка квалификации кандидата", "Формирование вывода" ]; var rows = steps.map(function (s, i) { return '
' + esc(stepsTime[i] || "10:4" + i) + '' + esc(s) + "
"; }).join(""); return '
' + 'Идёт анализ пакета — ожидайте
' + '
Ход работы' + 'Идёт анализ
' + '
' + rows + "
" + "

Анализ занимает обычно 3–5 минут. Можно закрыть вкладку — прогресс сохранится.

"; } var stepsTime = ["10:46", "10:46", "10:46", "10:46"]; /* ---------- Экран 3. Формирование заключения ---------- */ function vVerdict() { var checks = D.checklist.map(function (idx) { var v = D.violations[idx]; if (!v) return ""; return '"; }).join(""); var sent = D.status === "sent"; var status = sent ? pill("sent") : pill("draft"); var docText = $("docText") && $("docText").textContent ? $("docText").textContent : D.verdict; if (docText !== D.verdict) D.verdict = docText; return vStepper() + '
Итоговое заключение' + '' + status + "
" + '
' + '
ЗАКЛЮЧЕНИЕпо заявке ' + esc("ЭД-2026-118") + " · SEO-1, управление сетевых сервисов
" + '
' + esc(docText) + "
" + '
' + '' + (sent ? '' : '') + "
" + '
Чек-лист подтвержденных нарушений' + '' + num(D.checklist.length + " из " + D.violations.length) + " включены в заключение
" + '
' + checks + "
" + '
' + '' + 'Без системы — вручную по действующим правилам
'; } /* ---------- Экран 4. Согласование руководителем ---------- */ function vApprove() { var st = D.status; var banner = ""; var btns = ""; var kpis = kpiStrip([ { l: "Заявка", v: num("ЭД-2026-118") }, { l: "Подтверждено", v: num(D.checklist.length) }, { l: "Исключено", v: num(D.violations.length - D.checklist.length) }, { l: "Статус", v: ST[st] ? esc(ST[st].label) : "—" } ]); if (st === "agree") { banner = '
Материал согласован' + 'Согласовано ' + num(D.stamp || "15.09.2026, 09:12") + " · руководитель
"; } else if (st === "decl") { banner = '
Материал отклонён' + '' + num(D.stamp || "15.09.2026") + ' · ' + esc(D.rejected || "Без комментария") + "
"; } else { banner = '
' + 'Ожидает решения руководителя' + 'Проверены материалы на назначение руководителя SEO-1 · ' + num("14.09.2026") + "
"; btns = '
' + '' + '
'; } var checks = D.checklist.map(function (idx) { var v = D.violations[idx]; if (!v) return ""; return '
✓' + esc(v.d) + ' ' + esc(v.r) + "
"; }).join(""); return kpis + banner + '
Итоговое заключение
' + '
' + '
ЗАКЛЮЧЕНИЕпо заявке ЭД-2026-118 · SEO-1, управление сетевых сервисов
' + '
' + esc(D.verdict) + "
" + '
Подтвержденные нарушения
' + '
' + checks + "
" + btns; } /* ---------- Экран 5. О решении ---------- */ function vAbout() { return '
О решении
' + '
' + 'НаименованиеЭкспертиза документов для советов
' + 'ИсполнительВладелец процесса — сборка на платформе Vibe AI
' + 'ПользовательЭксперт, руководитель
' + 'Дата внедрения—
' + '
' + passCard("Предпосылки", "") + passCard("Входные данные и источники", "") + passCard("Процессы для внедрения ИИ", "") + passCard("Результат", "") + passCard("Ожидаемый эффект", "") + passCard("Экономический эффект", "
Объём20 экспертиз в месяц во всех экспертах
Высвобождение50 часов в месяц (оценка Төре)
") + passCard("Контроль и риски", "") + passCard("Меры по снижению", "") + passCard("Формат реализации", "") + "
" + '

Путь сотрудника

' + "

Эксперт загружает документы на экране «Загрузка и обзор материалов», переходит на «Результаты анализа ИИ» и проверяет выводы, " + "формировает итоговое заключение на «Формировании заключения», руководитель просматривает и согласует документы на «Согласовании у руководителя».

"; } function passCard(title, body) { return '

' + esc(title) + "

" + body + "
"; } function nowTime() { var d = new Date(); function p(x) { return (x < 10 ? "0" : "") + x; } return p(d.getHours()) + ":" + p(d.getMinutes()); } /* ---------- Действия ---------- */ S.go = function (id) { D.view = id; render(); }; S.analyze = function () { if (!allFilesReady()) { toast("Дополните материалы — все файлы должны быть готовы", "warn"); return; } D.running = true; D.contra = false; D.falseDone = false; D.checklist = [0, 1, 2, 3, 4, 5, 6, 7]; D.status = "draft"; D.rejected = null; D.stamp = null; for (var i = 0; i < D.violations.length; i++) D.violations[i].v = D.violations[i].v === "conf" ? "checked" : "checked"; D.violations[6].v = "falsep"; D.violations[7].v = "falsep"; D.verdict = "Заявка на назначение руководителя уровня SEO-1, управление сетевых сервисов (кандидат " + esc(D.candidate) + ") прошла проверку соответствия нормативной базе. Итоговое заключение будет сформировано после экспертной проверки выводов."; D.log = [ { t: nowTime(), b: "Пакет принят, 8 файлов" }, { t: nowTime(), b: "Запущен анализ: " + D.sources.length + " источника для сверки" } ]; render(); setTimeout(function () { pushLogs(); }, 900); setTimeout(function () { pushLogs(); }, 2100); setTimeout(function () { D.running = false; D.violations[6].v = "falsep"; D.contra = true; D.log.push({ t: nowTime(), b: "Обнаружено несоответствий: " + (D.violations.length - 2) + ", противоречий: 1" }); D.log.push({ t: nowTime(), b: "Журнал завершён — выводы переданы эксперту" }); S.go("results"); }, 3600); }; function pushLogs() { var extra = [ { t: nowTime(), b: "Извлечение текста: пояснительная записка" }, { t: nowTime(), b: "Сверка с Правилами поиска и подбора персонала, п. 4.2" }, { t: nowTime(), b: "Найдено отклонение: дата объявления о вакансии" }, { t: nowTime(), b: "Сверка с Законом «Об АО», ст. 36" }, { t: nowTime(), b: "Проверка квалификации кандидата" } ]; if (D.log.length <= 2) { D.log.push(extra[0]); D.log.push(extra[1]); } else if (D.log.length <= 4) { D.log.push(extra[2]); D.log.push(extra[3]); } else { D.log.push(extra[4]); } save(); render(); } S.closeDrawer = closeDrawer; S.viewFile = function (i) { var f = D.files[i]; openDrawer( '
' + "" + esc(f.n) + "" + pill(f.s) + '
" + '
' + '
' + esc(f.t) + '' + esc(f.d) + "
" + '
' + "

Документ загружен во внутреннюю систему. Эксперт просматривает комплектность и при необходимости открывает исходник в корпоративном хранилище.

" + "

Статус: " + (ST[f.s] ? esc(ST[f.s].label) : esc(f.s)) + ".

" + "
" ); }; S.refute = function (i) { var v = D.violations[i]; v.v = v.v === "falsep" ? "checked" : "falsep"; if (v.y === "contra") { D.contra = v.v === "falsep"; D.falseDone = v.v === "falsep"; } D.checklist.forEach(function (idx, pos) { if (D.violations[idx].v === "falsep") { D.checklist.splice(pos, 1); pos--; } }); render(); toast(v.v === "falsep" ? "Вывод отмечен как ложное срабатывание" : "Вывод возвращён в работу", v.v === "falsep" ? "warn" : "ok"); }; S.ck = function (idx) { var pos = D.checklist.indexOf(idx); var el = $("ck" + idx); if (el && el.checked && pos < 0) D.checklist.push(idx); if (el && !el.checked && pos >= 0) D.checklist.splice(pos, 1); D.checklist.sort(function (a, b) { return a - b; }); save(); // обновить счётчик без перерисовки чек-бокса render(); }; function rebuildVerdict() { var parts = D.checklist.map(function (idx) { var v = D.violations[idx]; return "\n" + (parts.indexOf(idx) + 1) + ". " + v.d + " (" + v.r + ")."; }); var base = "Заявка на назначение руководителя уровня SEO-1, управление сетевых сервисов (кандидат " + D.candidate + ") прошла проверку соответствия нормативной базе."; if (D.checklist.length > 0) { base += " По завершённой сверке зафиксированы следующие несоответствия:" + parts.join("") + "\n\nРешение о назначении принимается с учётом перечисленных несоответствий. Материалы переданы на согласование руководителю."; } else { base += " Несоответствий не выявлено."; } return base; } S.sendForApproval = function () { var n = 0; for (var i = 0; i < D.violations.length; i++) if (D.violations[i].v === "checked") n++; if (n > 0) { toast("Проверьте все выводы: " + n + " на проверке", "warn"); return; } D.status = "sent"; D.rejected = null; D.stamp = null; D.verdict = rebuildVerdict(); render(); toast("Заключение отправлено на согласование руководителю", "ok"); }; S.approve = function () { D.status = "agree"; D.stamp = nowTime(); render(); toast("Материал согласован", "ok"); }; S.declare = function () { var m = prompt("Комментарий к отклонению:"); if (m === null) return; D.status = "decl"; D.stamp = nowTime(); D.rejected = m || "Без комментария"; render(); toast("Материал отклонён", "warn"); }; S.manualMode = function () { if (confirm("Вернуться в ручной режим? Текущий черновик заключения сохранится.")) { toast("Ручной режим: работа ведётся без системы", "info"); closeModal(); } }; S.openRef = function (i) { var v = D.violations[i]; openModal( '
' + '
' + icon("link") + "Ссылка на нормативный акт" + '
" + '
Пункт' + esc(v.r) + "
" + "
Вывод системы" + esc(v.d) + "
" + '
' ); }; S.burger = function () { var sb = $("sidebar"); if (!sb) return; if (sb.dataset.open === "true") sb.removeAttribute("data-open"); else sb.setAttribute("data-open", "true"); }; /* ---------- Инициализация ---------- */ load(); render();