"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 + "
";
}
/* ---------- Экран 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 = headReady;
if (all && !D.running) {
head = '
' +
'Все материалы получены — пакет готов к запуску анализа' +
'
';
}
var rows = D.files.map(function (f, i) {
var statusCell = pill(f.s);
if (f.s === "wait") statusCell += ' ждёт загрузки';
return "
" +
'
' + esc(f.n) + "
" +
'
' + esc(f.t) + "
" +
'
' + esc(f.d) + "
" +
'
' + statusCell + "
" +
'
' +
"
";
}).join("");
var headReady = hasWaiting
? '
' +
"Пакет не собран" +
'Не все файлы получены от кандидата — анализ станет доступен после загрузки остальных материалов.
Строками идёт по заявке и пакету документов. Статусы файлов: «Ожидает проверки» — файл ещё получен не был, «Готово к анализу» — файл проверен и получен. " +
"Когда собран весь пакет, вверху экрана появляется кнопка «Запустить анализ ИИ».
";
}
/* ---------- Экран 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 = '
";
}).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 '
Анализ занимает обычно 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() +
'
' +
'Ожидает решения руководителя' +
'Проверены материалы на назначение руководителя 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("Формат реализации", "
ИИ-агент на платформе Alem; при необходимости — простой веб-экран на Vibe AI
Экраны макета: Загрузка и обзор; Результаты анализа; Формирование заключения; Согласование руководителем
") +
"
" +
'
Путь сотрудника
' +
"
Эксперт загружает документы на экране «Загрузка и обзор материалов», переходит на «Результаты анализа ИИ» и проверяет выводы, " +
"формировает итоговое заключение на «Формировании заключения», руководитель просматривает и согласует документы на «Согласовании у руководителя».
";
}
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.uploadFiles = function () {
var input = document.createElement("input");
input.type = "file";
input.multiple = true;
input.accept = ".pdf,.docx,.xlsx,.csv,.txt,.doc";
input.onchange = function () {
var files = input.files || [];
if (files.length === 0) return;
var added = 0;
for (var i = 0; i < files.length; i++) {
var f = files[i];
var exists = false;
for (var j = 0; j < D.files.length; j++) {
if (D.files[j].s === "wait" && D.files[j].n.toLowerCase().indexOf(f.name.toLowerCase().slice(0, 20)) >= 0) {
D.files[j].s = "ready";
D.files[j].d = nowTime();
exists = true;
added++;
break;
}
}
if (!exists) {
var ext = (f.name.lastIndexOf(".") >= 0) ? f.name.slice(f.name.lastIndexOf(".")) : "";
var typeLabel = "Подтверждающий документ";
if (f.name.toLowerCase().indexOf("записк") >= 0) typeLabel = "Пояснительная записка";
D.files.push({ n: f.name, t: typeLabel, d: nowTime() + " (загрузка)", s: "ready" });
added++;
}
}
save();
render();
toast(added + " файл(ов) добавлено в пакет", "ok");
var allNow = D.files.every(function (x) { return x.s === "ready"; });
if (allNow) toast("Пакет собран — анализ стал доступен", "ok");
};
input.click();
};
S.fileReady = function (i) {
D.files[i].s = "ready";
render();
toast("Файл отмечен как готовый к анализу", "ok");
};
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) + "
" +
'
' +
"
Документ загружен во внутреннюю систему. Эксперт просматривает комплектность и при необходимости открывает исходник в корпоративном хранилище.