samruk-ai-agent/index.html

536 lines
48 KiB
HTML
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.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>ИИ-агент мониторинга ПБ — АО «Казахтелеком»</title>
<style>
:root{--ink:#0F1218;--cyan:#00E5FF;--cyan-50:#E8FCFF;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7;--gray-200:#E5E7EB;--gray-700:#374151;--green:#10B981;--red:#EF4444;--amber:#F59E0B;--blue:#3B82F6;--sidebar-w:260px}
*{box-sizing:border-box;margin:0;padding:0}
body{font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--gray-100);display:flex;min-height:100vh}
.sidebar{width:var(--sidebar-w);background:var(--ink);color:var(--white);position:fixed;top:0;left:0;bottom:0;z-index:100;display:flex;flex-direction:column;overflow-y:auto}
.sidebar .logo{padding:20px 24px;font-size:17px;font-weight:800;border-bottom:1px solid rgba(255,255,255,.1);line-height:1.3}
.sidebar .logo span{color:var(--cyan)}
.sidebar nav{padding:16px 0;flex:1}
.sidebar nav a{display:flex;align-items:center;gap:12px;padding:12px 24px;color:#9aa3b2;font-weight:500;transition:.15s;font-size:15px;cursor:pointer}
.sidebar nav a:hover,.sidebar nav a.active{color:var(--white);background:rgba(0,229,255,.08)}
.sidebar nav a.active{border-right:3px solid var(--cyan)}
.sidebar nav a .ico{font-size:18px;width:24px;text-align:center}
.sidebar .user{padding:20px 24px;border-top:1px solid rgba(255,255,255,.1);font-size:13px;color:#6b7280}
.main{margin-left:var(--sidebar-w);flex:1;padding:24px 32px;max-width:calc(100vw - var(--sidebar-w))}
.page{display:none}
.page.active{display:block}
.page h2{font-size:24px;font-weight:700;margin-bottom:20px}
.stats-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:16px;margin-bottom:24px}
.stat-card{background:var(--white);border-radius:12px;padding:20px 24px;border:1px solid var(--gray-200)}
.stat-card .lbl{font-size:13px;color:var(--gray-500);margin-bottom:4px}
.stat-card .num{font-size:32px;font-weight:800;line-height:1}
.stat-card .sub{font-size:13px;color:var(--gray-500);margin-top:4px}
.stat-card.red .num{color:var(--red)}.stat-card.green .num{color:var(--green)}.stat-card.amber .num{color:var(--amber)}.stat-card.blue .num{color:var(--blue)}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-bottom:24px}
.grid3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:24px;margin-bottom:24px}
.panel{background:var(--white);border-radius:12px;border:1px solid var(--gray-200);padding:24px}
.panel h3{font-size:17px;font-weight:700;margin-bottom:16px}
.chart-stub{height:200px;background:var(--gray-100);border-radius:8px;display:flex;align-items:flex-end;gap:8px;padding:16px 16px 8px}
.chart-stub .bar{flex:1;background:var(--cyan);border-radius:4px 4px 0 0;min-height:4px}
.chart-stub .bar.red{background:var(--red)}.chart-stub .bar.amber{background:var(--amber)}.chart-stub .bar.green{background:var(--green)}
.chart-labels{display:flex;gap:8px;padding:8px 16px 0;font-size:11px;color:var(--gray-500);text-align:center}
.chart-labels span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
table{width:100%;border-collapse:collapse}
th,td{padding:10px 14px;text-align:left;font-size:13px}
th{font-weight:600;color:var(--gray-500);font-size:11px;text-transform:uppercase;letter-spacing:.5px;border-bottom:2px solid var(--gray-200)}
td{border-bottom:1px solid var(--gray-200)}
tr:hover td{background:var(--cyan-50)}
.badge{display:inline-block;padding:4px 10px;border-radius:100px;font-size:12px;font-weight:600}
.badge.green{background:#D1FAE5;color:#065F46}.badge.amber{background:#FEF3C7;color:#92400E}.badge.red{background:#FEE2E2;color:#991B1B}.badge.blue{background:#DBEAFE;color:#1E40AF}.badge.gray{background:var(--gray-100);color:var(--gray-700)}
.filters{display:flex;gap:12px;margin-bottom:20px;flex-wrap:wrap;align-items:center}
.filters select,.filters input{padding:10px 14px;border:1px solid var(--gray-200);border-radius:8px;font-size:14px;background:var(--white);min-width:160px}
.filters input{min-width:280px}
.filters .count{font-size:13px;color:var(--gray-500);margin-left:auto}
.ai-tag{display:inline-flex;align-items:center;gap:6px;background:var(--cyan-50);color:var(--ink);padding:4px 10px;border-radius:6px;font-size:12px;font-weight:600}
.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:200;display:none;align-items:center;justify-content:center}
.modal-overlay.open{display:flex}
.modal{background:var(--white);border-radius:16px;max-width:860px;width:92vw;max-height:85vh;overflow-y:auto;padding:32px}
.modal h2{font-size:21px;margin-bottom:8px;padding-right:30px}
.modal .close{float:right;background:none;border:none;font-size:28px;cursor:pointer;line-height:1;color:var(--gray-500);margin:-8px -8px 0 0}
.modal .meta{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:20px 0}
.modal .meta .fld{font-size:13px;color:var(--gray-500)}
.modal .meta .fld strong{display:block;font-size:15px;color:var(--ink);margin-top:2px}
.modal .docs{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0}
.modal .docs span{background:var(--gray-100);padding:6px 12px;border-radius:6px;font-size:13px}
.modal .ai-block{background:var(--cyan-50);border-radius:8px;padding:16px;margin:16px 0;font-size:14px}
.modal .ai-block h4{font-size:14px;margin-bottom:6px}
.modal .timeline{font-size:13px;color:var(--gray-500);line-height:2}
.modal .timeline strong{color:var(--ink)}
.rating-bar{display:flex;align-items:center;gap:12px;padding:8px 0}
.rating-bar .name{width:200px;font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rating-bar .track{flex:1;height:10px;background:var(--gray-200);border-radius:10px;overflow:hidden}
.rating-bar .fill{height:100%;border-radius:10px}
.timeline-item{display:flex;gap:8px;font-size:13px;padding:3px 0;color:var(--gray-500);align-items:baseline}
.timeline-item .dot{width:8px;height:8px;border-radius:50%;background:var(--cyan);flex-shrink:0;margin-top:6px}
.section-tag{display:inline-block;padding:4px 8px;border-radius:4px;font-size:11px;font-weight:600;margin-right:6px;background:var(--gray-100);color:var(--gray-700)}
@media(max-width:900px){
.sidebar{width:60px}.sidebar .logo,.sidebar nav a span,.sidebar .user{display:none}
.sidebar nav a{justify-content:center;padding:12px 0;font-size:20px}
.main{margin-left:60px;max-width:calc(100vw - 60px);padding:16px}
.grid2,.grid3{grid-template-columns:1fr}
.stats-row{grid-template-columns:1fr 1fr}
}
</style>
</head>
<body>
<aside class="sidebar">
<div class="logo"><span>ИИ-Агент</span><br>Казахтелеком</div>
<nav>
<a class="active" data-page="dashboard"><span class="ico">📊</span> <span>Дашборд</span></a>
<a data-page="register"><span class="ico">📋</span> <span>Реестр (35)</span></a>
<a data-page="analytics"><span class="ico">📈</span> <span>Аналитика</span></a>
<a data-page="reports"><span class="ico">📑</span> <span>Отчёты</span></a>
<a data-page="risks"><span class="ico">⚠️</span> <span>Карта рисков</span></a>
</nav>
<div class="user">Директор ДПБ • АО «Казахтелеком»</div>
</aside>
<main class="main" id="mainContent"></main>
<div class="modal-overlay" id="modalOverlay"><div class="modal" id="modalContent"></div></div>
<script>
// ===== 35 МЕРОПРИЯТИЙ ПЛАНА ПБ КАЗАХТЕЛЕКОМ НА 2026 =====
const sections = [
'I. Люди. Повышение культуры безопасности',
'II. Безопасность при эксплуатации оборудования',
'III. Предупреждение и готовность к ликвидации аварий и ЧС',
'IV. Информационно-разъяснительная работа',
'V. Внедрение ИИ и цифровизации'
]
const branches = [
'Дирекция производственной безопасности',
'Объединение «Дивизион «Сеть»»',
'Дивизион по корпоративному бизнесу',
'Дивизион по розничному бизнесу',
'Сервисная фабрика',
'Дирекция «Телеком Комплект»',
'Корпоративный университет',
'Дирекция управления проектами',
'Дивизион цифрового бизнеса'
]
const statusMap = {done:'Исполнено',warn:'На контроле',late:'Просрочено',wait:'Не начато'}
// real plan data — 35 events
const E = [
// I. ЛЮДИ (1-9)
{id:1, sec:0, t:'Обучение и повышение квалификации работников (VR, AR, цифровые симуляторы)', b:6, d:sections[0], s:'warn', p:45, due:'31.12.2026', done:'—',
r:'Генеральный директор КУ / Директора филиалов и ДАО',
docs:['Протоколы обучения','Электронная ведомость'],
ai:'Обучение ведётся по графику. Охвачено 45% персонала. VR-тренажёры развёрнуты в 3 филиалах. Рисков срыва нет.',
h:['15.01 — Мероприятие создано','01.03 — Запущено обучение в Алматинском филиале','15.05 — VR-симуляторы установлены в 3 филиалах']},
{id:2, sec:0, t:'Анализ и пересмотр ВНД согласно Стратегии развития ПБ на 2024-2028 гг.', b:0, d:sections[0], s:'done', p:100, due:'31.03.2026', done:'28.03.2026',
r:'Директор ДПБ / Директор ДИТ / Директора филиалов',
docs:['Отчёт о проведённом анализе','Утверждённый ВНД'],
ai:'Анализ завершён в срок. ВНД пересмотрены. Ключевые показатели ПБ установлены. Замечаний нет.',
h:['10.01 — Мероприятие создано','15.02 — Проведён анализ ВНД','28.03 — Отчёт утверждён']},
{id:3, sec:0, t:'Тематические совещания по вопросам ПБ (руководство–филиалы, филиалы–СП, филиалы–подрядчики)', b:0, d:sections[0], s:'warn', p:50, due:'31.12.2026', done:'—',
r:'Главный административный директор / Директор ДПБ',
docs:['Протоколы совещаний (a, b, c)'],
ai:'Проведено 2 квартальных совещания руководства с филиалами. Ежемесячные совещания филиалов со СП — выполняется. Совещания с подрядчиками — график соблюдается.',
h:['10.01 — Мероприятие создано','15.02 — Совещание Q1 проведено','15.05 — Совещание Q2 проведено']},
{id:4, sec:0, t:'Проверка знаний в формате тестирования после инструктажей по ОТ', b:6, d:sections[0], s:'warn', p:55, due:'31.12.2026', done:'—',
r:'Директора филиалов и ДАО',
docs:['Отчёты о проделанной работе','Тесты для оценки знаний'],
ai:'Тестирование внедрено в 6 филиалах. Средний результат — 82%. Рекомендуется усилить тесты по пожарной безопасности.',
h:['01.02 — Мероприятие создано','01.04 — Внедрено тестирование','01.06 — Промежуточный отчёт']},
{id:5, sec:0, t:'Нематериальное поощрение филиалов/ДАО со снижением НС, пожаров и аварий', b:0, d:sections[0], s:'done', p:100, due:'31.03.2026', done:'25.03.2026',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Информация о нематериальном поощрении'],
ai:'Положение о поощрении утверждено. Определены 3 филиала-лидера за 2023-2025 гг. Поощрение доведено до коллективов.',
h:['15.01 — Проект положения','01.03 — Согласование','25.03 — Утверждено']},
{id:6, sec:0, t:'Разработка ВНД по внутренним тренерам (отбор, подготовка, доплата)', b:6, d:sections[0], s:'warn', p:60, due:'30.06.2026', done:'—',
r:'Генеральный директор КУ / Управляющий директор по персоналу',
docs:['ВНД по внутренним тренерам','Перечень внутренних тренеров'],
ai:'Проект ВНД на финальном согласовании. Перечень тренеров сформирован (12 чел). Риск: доплаты требуют бюджета.',
h:['01.03 — Мероприятие создано','15.04 — Проект ВНД','01.06 — Перечень тренеров']},
{id:7, sec:0, t:'Обмен опытом в области ПБ (Комитет HSE, выезды, онлайн-семинары)', b:1, d:sections[0], s:'warn', p:40, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Материалы обмена опытом'],
ai:'Проведён 1 выезд на площадку KEGOC. Онлайн-семинар с иностранной компанией запланирован на июль. Активность средняя.',
h:['15.02 — Мероприятие создано','01.04 — Выезд на KEGOC','15.05 — План семинаров']},
{id:8, sec:0, t:'Анализ эффективности охраны здоровья (медосмотры, Well-being Week, скрининг, микротравмы)', b:4, d:sections[0], s:'wait', p:15, due:'30.09.2026', done:'—',
r:'Директор ДПБ / Управляющий директор по персоналу',
docs:['Заключительный Акт','Программа Well-being','Отчёт о скрининге','Отчёт о микротравмах'],
ai:'Медосмотры по графику — Q3. Well-being Week запланирован на сентябрь. Алгоритм микротравм в разработке. Ранний этап.',
h:['01.04 — Мероприятие создано','01.06 — Проект алгоритма микротравм']},
{id:9, sec:0, t:'Участие в международных/национальных конкурсах профмастерства по ПБ', b:6, d:sections[0], s:'wait', p:20, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Результаты конкурсов','Пакет материалов'],
ai:'Определены 2 конкурса для участия. Заявки готовятся. Срок — до конца года.',
h:['01.05 — Мероприятие создано','01.06 — Отобраны конкурсы']},
// II. БЕЗОПАСНОСТЬ ПРИ ЭКСПЛУАТАЦИИ ОБОРУДОВАНИЯ (10-18)
{id:10, sec:1, t:'Техническое перевооружение изношенного оборудования, зданий и сооружений', b:1, d:sections[1], s:'warn', p:55, due:'31.12.2026', done:'—',
r:'Генеральный директор ОДС / Директор СФ / Директор ДУП / Директор ДИТ',
docs:['Аналитическая справка по филиалам/ДАО'],
ai:'По плану 2024-2027. Заменено 55% единиц оборудования. Карагандинский филиал отстаёт на 12%. Рекомендуется усилить контроль.',
h:['01.01 — Переходящее с 2024','01.04 — Промежуточный отчёт','01.06 — 55% исполнения']},
{id:11, sec:1, t:'Пересмотр и актуализация нарядно-допускной системы (сертификаты безопасности)', b:1, d:sections[1], s:'warn', p:70, due:'30.06.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Переутверждённая процедура наряд-допусков','Фотоотчёт','Протоколы обучения'],
ai:'Процедура пересмотрена. Пилот сертификатов безопасности запущен в ВКО филиале. Обучение новых процедур — 85% персонала.',
h:['01.02 — Мероприятие создано','01.04 — Проект процедуры','15.05 — Пилот в ВКО']},
{id:12, sec:1, t:'Внедрение цифровой маркировки опасных техустройств (QR-коды)', b:8, d:sections[1], s:'wait', p:8, due:'30.09.2026', done:'—',
r:'Директора филиалов и ДАО',
docs:['Справка о внедрении','Фотоотчёт'],
ai:'Проект на стадии ТЭО. QR-коды не заказаны. Риск срыва срока — низкий, начало работ запланировано на июль.',
h:['01.05 — Мероприятие создано','01.06 — ТЭО в разработке']},
{id:13, sec:1, t:'Ежеквартальные проверки по проверочным листам БиОТ, пром- и пожарной безопасности', b:0, d:sections[1], s:'warn', p:50, due:'31.12.2026', done:'—',
r:'Директор ДПБ',
docs:['Акты проверок','График проверок'],
ai:'Q1 проверки завершены в 7 из 8 филиалов. Q2 — идёт по графику. Выявлено 23 нарушения, устранено 18.',
h:['01.01 — Мероприятие создано','31.03 — Q1 проверки','01.06 — Q2 начаты']},
{id:14, sec:1, t:'Участие в перекрёстных аудитах ПК согласно Плану-графику', b:0, d:sections[1], s:'warn', p:40, due:'31.12.2026', done:'—',
r:'Директор ДПБ',
docs:['Письмо о предоставлении кандидата'],
ai:'Назначены 4 аудитора от Казахтелеком. Участвовали в 2 аудитах KEGOC и КазТрансОйл. График соблюдается.',
h:['15.01 — Назначены аудиторы','01.03 — Аудит KEGOC','15.04 — Аудит КазТрансОйл']},
{id:15, sec:1, t:'Проактивные инструменты (поведенческие аудиты, Near Miss, право приостановки)', b:0, d:sections[1], s:'warn', p:48, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Аналитическая справка','Журнал опережающих индикаторов'],
ai:'Зарегистрировано 147 Near Miss за полугодие (+12% к 2025). Поведенческие аудиты — 320 шт. Данные вносятся в журнал СУО.',
h:['01.01 — Мероприятие создано','01.04 — Q1: 68 Near Miss','01.06 — Q2: 79 Near Miss']},
{id:16, sec:1, t:'Повышение эффективности управления подрядными организациями (аудиты, стартовые совещания)', b:1, d:sections[1], s:'done', p:85, due:'31.12.2026', done:'—',
r:'Директора филиалов и ДАО',
docs:['План-график перекрёстных аудитов','Акты проверки','Протоколы совещаний'],
ai:'Q1 — 12 подрядчиков проверено. Стартовые совещания — 100% охват перед допуском. Рекомендации: усилить контроль подрядчиков в Алматинском филиале.',
h:['15.01 — План-график','01.03 — Q1 проверки','01.06 — Q2 проверки начаты']},
{id:17, sec:1, t:'Контроль состояния ПБ на объектах (CEO-1, первые руководители филиалов)', b:0, d:sections[1], s:'warn', p:35, due:'31.12.2026', done:'—',
r:'Главный административный директор / Директор ДПБ',
docs:['Отчёты по результатам проверок','График проверок','Фотоотчёт'],
ai:'CEO-1 проверил 2 филиала (ЗКО, Атырау). Первые руководители филиалов — 6 выездов. Активность ниже плана. Рекомендуется график CEO-1 на Q3.',
h:['01.02 — Мероприятие создано','15.03 — Проверка ЗКО','01.05 — Проверка Атырау']},
{id:18, sec:1, t:'Контроль транспортной безопасности (ежемесячный мониторинг нарушений)', b:1, d:sections[1], s:'done', p:90, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Ежемесячный сводный отчёт о нарушениях'],
ai:'Январь-май — 34 нарушения. Применены штрафные санкции к 12 водителям подрядчиков. Тренд на снижение (-15% к 2025).',
h:['01.01 — Мероприятие создано','01.02 — Отчёт январь','01.06 — Отчёт май']},
// III. ПРЕДУПРЕЖДЕНИЕ И ГОТОВНОСТЬ К ЛИКВИДАЦИИ АВАРИЙ И ЧС (19-20)
{id:19, sec:2, t:'Учебные тревоги и тренировки (аварии, пожары, первая помощь)', b:1, d:sections[2], s:'warn', p:30, due:'31.12.2026', done:'—',
r:'Управляющий директор по безопасности / Руководители ДАО',
docs:['Акты о проведении тренировок','Пресс-релизы'],
ai:'Проведено 1 учение по ликвидации аварии (ВКО). Пожарные тренировки: 1 из 2. Занятия по первой помощи запланированы на Q3. Необходимо ускорить.',
h:['01.02 — Мероприятие создано','15.03 — Учение ВКО','15.05 — Пожарная тренировка №1']},
{id:20, sec:2, t:'Усиление реагирования на ЧС (Crisis Management System, обучение, штабы)', b:0, d:sections[2], s:'warn', p:65, due:'30.06.2026', done:'—',
r:'Управляющий директор по безопасности / Директора филиалов',
docs:['Приказ о внедрении CMS','Материалы обучения','Акты заседаний штабов'],
ai:'Приказ о CMS подписан. Обучение проведено для 60% ответственных. Одно заседание штаба проведено, второе — в июне.',
h:['01.03 — Мероприятие создано','01.04 — Приказ CMS','15.05 — Заседание штаба №1']},
// IV. ИНФОРМАЦИОННО-РАЗЪЯСНИТЕЛЬНАЯ РАБОТА (21-31)
{id:21, sec:3, t:'Выпуск обращения Председателя Правления о важности соблюдения требований ПБ', b:0, d:sections[3], s:'done', p:100, due:'31.12.2026', done:'15.02.2026',
r:'Директор ДПБ / Пресс-секретарь ЦА',
docs:['Публикация на информационных порталах'],
ai:'Обращение опубликовано на корпоративном портале и в SK News. Охват — 100% персонала. Задача выполнена досрочно.',
h:['15.01 — Проект обращения','01.02 — Подписание','15.02 — Публикация']},
{id:22, sec:3, t:'Стратегические сессии/Форумы для первых руководителей и семинары для подрядчиков', b:0, d:sections[3], s:'wait', p:15, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Департамент по коммуникациям',
docs:['Протоколы форумов/сессий','Протоколы семинаров'],
ai:'Форум запланирован на октябрь. Семинары для подрядчиков — 2 площадки определены. Начало подготовки.',
h:['01.05 — Мероприятие создано','01.06 — Определены площадки']},
{id:23, sec:3, t:'Олимпиада по производственной безопасности среди специалистов ПБ', b:6, d:sections[3], s:'wait', p:10, due:'30.09.2026', done:'—',
r:'Директор ДПБ',
docs:['Протокол итогов Олимпиады'],
ai:'Положение об Олимпиаде на согласовании. Задания не разработаны. Рекомендуется начать подготовку в июне.',
h:['01.05 — Мероприятие создано','01.06 — Проект положения']},
{id:24, sec:3, t:'Ознакомление работников с обстоятельствами НС с тяжёлым/летальным исходом', b:0, d:sections[3], s:'done', p:92, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Информационные бюллетени','Листы ознакомления'],
ai:'За полугодие разослано 3 бюллетеня по НС в ПК Фонда. Ознакомление — 92% персонала. Рекомендация: догнать до 100% к Q3.',
h:['01.01 — Мероприятие создано','15.02 — Бюллетень №1','01.05 — Бюллетень №3']},
{id:25, sec:3, t:'Молодёжные проектные инициативы (SK News, выезды, аудиты, онлайн-эфиры)', b:6, d:sections[3], s:'warn', p:40, due:'31.12.2026', done:'—',
r:'Управляющий директор по персоналу / Директор ДПБ',
docs:['Публикации в SK News','Материалы мероприятий'],
ai:'Опубликовано 2 истории в SK News. Проведён 1 выезд молодых специалистов. 3 молодых специалиста привлечены в перекрёстные аудиты.',
h:['01.02 — Мероприятие создано','15.03 — Публикация №1','01.05 — Выезд']},
{id:26, sec:3, t:'Наглядная агитация (видеоролики, подкасты, постеры, брошюры)', b:2, d:sections[3], s:'warn', p:50, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Департамент по коммуникациям',
docs:['Видеоролики','Постеры и брошюры'],
ai:'Снято 2 видеоролика («Безопасность будущего», профилактика ДТП). Постеры распространены. Подкаст в разработке. Хороший темп.',
h:['01.02 — Мероприятие создано','01.04 — Видеоролик №1','01.06 — Видеоролик №2']},
{id:27, sec:3, t:'Организация встреч коллектива с получившими производственные травмы', b:3, d:sections[3], s:'warn', p:30, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Фотофиксация'],
ai:'Проведена 1 встреча в Карагандинском филиале. Получены согласия от 3 работников. Рекомендуется расширить на другие филиалы.',
h:['01.03 — Мероприятие создано','01.05 — Встреча в Караганде']},
{id:28, sec:3, t:'Пропаганда безопасности через семейные ценности (письма, Семейный день, конкурс рисунков)', b:0, d:sections[3], s:'warn', p:25, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Департамент по коммуникациям',
docs:['Информационное письмо','Пресс-релизы'],
ai:'Направлено 5 писем семьям отличившихся. Семейный день ОТ запланирован на август. Конкурс рисунков анонсирован.',
h:['01.04 — Мероприятие создано','15.05 — Письма семьям']},
{id:29, sec:3, t:'Разработка корпоративного сборника лучших практик по ПБ', b:6, d:sections[3], s:'late', p:40, due:'30.06.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Корпоративный сборник лучших практик'],
ai:'Сборник в разработке. Опрошено 4 из 8 филиалов. Есть риск срыва срока Q2 — осталось 26 дней. Требуется эскалация.',
h:['01.03 — Мероприятие создано','01.05 — Опрошены 4 филиала','01.06 — Эскалация: риск срыва']},
{id:30, sec:3, t:'Сбор предложений по цифровым решениям для системы управления ПБ', b:7, d:sections[3], s:'warn', p:60, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Предложения по улучшению','План реализации'],
ai:'Собрано 18 предложений. 5 приняты в реализацию. Идёт консолидация в ДПБ. Активность хорошая.',
h:['01.01 — Мероприятие создано','01.04 — 10 предложений','01.06 — 18 предложений']},
{id:31, sec:3, t:'Разработка видеообзора кейсов происшествий в ПК', b:0, d:sections[3], s:'warn', p:75, due:'30.06.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Видеообзор'],
ai:'Видеообзор смонтирован на 75%. Озвучка запланирована на 15 июня. Срок Q2 — укладываемся.',
h:['01.03 — Мероприятие создано','01.05 — Сценарий утверждён','01.06 — Монтаж 75%']},
// V. ВНЕДРЕНИЕ ИИ И ЦИФРОВИЗАЦИИ (32-35)
{id:32, sec:4, t:'Внедрение чат-бота ИИ-ассистента по производственной безопасности', b:8, d:sections[4], s:'warn', p:70, due:'30.06.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Справка о функционировании','Скриншоты'],
ai:'Чат-бот разработан, проходит тестирование в ЦА. База НПА загружена. Пилотный запуск — 15 июня. Успеваем в Q2.',
h:['01.02 — Мероприятие создано','01.04 — Разработка','01.06 — Тестирование']},
{id:33, sec:4, t:'Внедрение системы анализа и предупреждения НС и критических происшествий', b:8, d:sections[4], s:'wait', p:15, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Справка о функционировании','Скриншоты'],
ai:'ТЗ на систему согласовывается. Интеграция с платформой Фонда — прорабатывается. Ранний этап, рисков срыва нет.',
h:['01.04 — Мероприятие создано','01.06 — ТЗ на согласовании']},
{id:34, sec:4, t:'Запуск электронного HSE паспорта на каждого работника', b:8, d:sections[4], s:'wait', p:10, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Справка о функционировании','Скриншоты'],
ai:'Концепция HSE паспорта утверждена. Выбран подрядчик. Интеграция с КЦС — в плане на Q3. Ранний этап.',
h:['01.05 — Мероприятие создано','01.06 — Концепция утверждена']},
{id:35, sec:4, t:'Внедрение системы электронных нарядов-допусков на работы повышенной опасности', b:5, d:sections[4], s:'wait', p:8, due:'31.12.2026', done:'—',
r:'Директор ДПБ / Директора филиалов и ДАО',
docs:['Справка о функционировании','Скриншоты'],
ai:'Проект на предпроектной стадии. Анализ рынка решений проведён. Ожидается выбор платформы в Q3.',
h:['01.05 — Мероприятие создано','01.06 — Анализ рынка']}
]
const events = E
const allBranches = [...new Set(events.map(e=>e.b))].map(i=>branches[i])
// ===== HELPERS =====
function pctBar(p){return `<div class="track"><div class="fill" style="width:${p}%;background:${p>=80?'var(--green)':p>=40?'var(--amber)':'var(--red)'}"></div></div> ${p}%`}
function sBadge(s){const map={done:'green',warn:'amber',late:'red',wait:'gray'};return `<span class="badge ${map[s]||'gray'}">${statusMap[s]||s}</span>`}
function secBadge(s){return `<span class="badge blue">${['I','II','III','IV','V'][s]}</span>`}
// ===== PAGES =====
function pageDashboard(){
let done=events.filter(e=>e.s==='done').length
let late=events.filter(e=>e.s==='late').length
let warn=events.filter(e=>e.s==='warn').length
let wait=events.filter(e=>e.s==='wait').length
let total=events.length
let donePct=Math.round(done/total*100)
return `<div class="page active"><h2>Дашборд производственной безопасности</h2>
<div class="stats-row">
<div class="stat-card"><div class="lbl">Всего мероприятий</div><div class="num">${total}</div><div class="sub">План ПБ на 2026 год</div></div>
<div class="stat-card green"><div class="lbl">Исполнено</div><div class="num">${done}</div><div class="sub">${donePct}% от плана</div></div>
<div class="stat-card amber"><div class="lbl">На контроле</div><div class="num">${warn}</div></div>
<div class="stat-card red"><div class="lbl">Просрочено</div><div class="num">${late}</div></div>
<div class="stat-card blue"><div class="lbl">Не начато</div><div class="num">${wait}</div></div>
</div>
<div class="grid2">
<div class="panel">
<h3>Исполнение по разделам</h3>
${sections.map((s,i)=>{
let items=events.filter(e=>e.sec===i)
let pct=Math.round(items.filter(e=>e.s==='done').length/Math.max(1,items.length)*100)
return `<div class="rating-bar"><span class="name" style="font-size:12px">${s.replace(/^[IVX]+\.\s/,'')}</span><div class="track"><div class="fill" style="width:${pct}%;background:var(--cyan)"></div></div><span style="font-size:13px;font-weight:700;width:40px;text-align:right">${pct}%</span></div>`
}).join('')}
</div>
<div class="panel">
<h3>Рейтинг филиалов</h3>
${allBranches.map((b,i)=>{
let items=events.filter(e=>e.b===i||(e.b===i&&branches[e.b]===b))
// recalc for actual branch index
let _items=events.filter(e=>branches[e.b]===b)
let pct=Math.round(_items.filter(e=>e.s==='done').length/Math.max(1,_items.length)*100)
return `<div class="rating-bar"><span class="name">${b}</span><div class="track"><div class="fill" style="width:${pct}%;background:${pct>=50?'var(--green)':pct>=25?'var(--amber)':'var(--red)'}"></div></div><span style="font-size:13px;font-weight:700;width:40px;text-align:right">${pct}%</span></div>`
}).join('')}
</div>
</div>
<div class="panel">
<h3>Динамика исполнения по кварталам</h3>
<div class="chart-stub">
<div class="bar green" style="height:45%"></div>
<div class="bar amber" style="height:60%"></div>
<div class="bar" style="height:70%"></div>
<div class="bar" style="height:${donePct}%"></div>
</div>
<div class="chart-labels"><span>Q1 (факт)</span><span>Q2 (прогноз)</span><span>Q3 (план)</span><span>Q4 (цель: 100%)</span></div>
</div>
</div>`
}
function pageRegister(filter='',secFilter='',statusFilter=''){
let list=events
if(filter) list=list.filter(e=>e.t.toLowerCase().includes(filter.toLowerCase())||branches[e.b].toLowerCase().includes(filter.toLowerCase()))
if(secFilter!=='') list=list.filter(e=>e.sec===parseInt(secFilter))
if(statusFilter) list=list.filter(e=>e.s===statusFilter)
return `<div class="page active"><h2>Реестр мероприятий</h2>
<div class="filters">
<input placeholder="Поиск по названию или филиалу..." oninput="refreshPage('register',this.value,document.getElementById('secFilter')?.value||'',document.getElementById('statusFilter')?.value||'')" id="searchInput">
<select onchange="refreshPage('register',document.getElementById('searchInput')?.value||'',this.value,document.getElementById('statusFilter')?.value||'')" id="secFilter">
<option value="">Все разделы</option>
${sections.map((s,i)=>`<option value="${i}">${s}</option>`).join('')}
</select>
<select onchange="refreshPage('register',document.getElementById('searchInput')?.value||'',document.getElementById('secFilter')?.value||'',this.value)" id="statusFilter">
<option value="">Все статусы</option>
<option value="done">Исполнено</option><option value="warn">На контроле</option><option value="late">Просрочено</option><option value="wait">Не начато</option>
</select>
<span class="count">Найдено: ${list.length}</span>
</div>
<div class="panel" style="overflow-x:auto">
<table>
<thead><tr><th>№</th><th>Мероприятие</th><th>Раздел</th><th>Филиал</th><th>Срок</th><th>Прогресс</th><th>Статус</th></tr></thead>
<tbody>${list.map(e=>`<tr style="cursor:pointer" onclick="openEvent(${e.id})">
<td>${e.id}</td><td><strong>${e.t}</strong></td><td><span class="badge blue">${['I','II','III','IV','V'][e.sec]}</span></td>
<td style="font-size:12px">${branches[e.b]}</td><td style="font-size:12px">${e.due}</td>
<td style="white-space:nowrap">${pctBar(e.p)}</td><td>${sBadge(e.s)}</td></tr>`).join('')}</tbody>
</table>
</div>
</div>`
}
function pageAnalytics(){
let branchRatings=allBranches.map(bi=>{
let items=events.filter(e=>branches[e.b]===bi)
let done=items.filter(e=>e.s==='done').length
let late=items.filter(e=>e.s==='late').length
return {name:bi,done,late,total:items.length,pct:Math.round(done/Math.max(1,items.length)*100)}
}).sort((a,b)=>b.pct-a.pct)
let sectionStats=sections.map((s,i)=>{
let items=events.filter(e=>e.sec===i)
let done=items.filter(e=>e.s==='done').length
return {name:s.replace(/^[IVX]+\.\s/,''),done,total:items.length,pct:Math.round(done/Math.max(1,items.length)*100)}
})
return `<div class="page active"><h2>Аналитика</h2>
<div class="grid2">
<div class="panel">
<h3>Рейтинг филиалов/ДАО</h3>
${branchRatings.map((r,i)=>`<div class="rating-bar">
<span class="name">${i+1}. ${r.name}</span>
<div class="track"><div class="fill" style="width:${r.pct}%;background:${r.pct>=50?'var(--green)':r.pct>=25?'var(--amber)':'var(--red)'}"></div></div>
<span style="font-size:13px;font-weight:700;width:60px;text-align:right">${r.pct}% (${r.done}/${r.total})</span></div>`).join('')}
</div>
<div class="panel">
<h3>Исполнение по разделам Плана</h3>
${sectionStats.map(s=>`<div class="rating-bar">
<span class="name" style="font-size:12px">${s.name}</span>
<div class="track"><div class="fill" style="width:${s.pct}%;background:var(--cyan)"></div></div>
<span style="font-size:13px;font-weight:700;width:60px;text-align:right">${s.pct}% (${s.done}/${s.total})</span></div>`).join('')}
</div>
</div>
<div class="panel" style="margin-top:16px">
<h3>Просроченные мероприятия</h3>
${events.filter(e=>e.s==='late').map(e=>`<div class="rating-bar">
<span class="name" style="font-size:12px">${e.t.slice(0,50)}...</span>
<span style="font-size:12px;color:var(--red);font-weight:700">${e.due}</span>
<span class="badge red">${branches[e.b]}</span></div>`).join('')||'<p style="color:var(--gray-500)">Просрочек нет</p>'}
</div>
</div>`
}
function pageReports(){
return `<div class="page active"><h2>Формирование отчётности</h2>
<div class="stats-row">
<div class="stat-card"><div class="lbl">Ежемесячный отчёт</div><div class="num" style="font-size:20px">Май 2026</div><div class="sub"><span class="badge green">Сформирован</span></div></div>
<div class="stat-card"><div class="lbl">Квартальный отчёт</div><div class="num" style="font-size:20px">Q2 2026</div><div class="sub"><span class="badge amber">В обработке</span></div></div>
<div class="stat-card"><div class="lbl">Годовой отчёт</div><div class="num" style="font-size:20px">2026</div><div class="sub"><span class="badge gray">Ожидается</span></div></div>
</div>
<div class="panel" style="margin-top:16px">
<h3>Все отчёты</h3>
<table>
<thead><tr><th>Тип отчёта</th><th>Период</th><th>Статус</th><th>Дата</th><th>Формат</th></tr></thead>
<tbody>
<tr><td>Ежемесячный отчёт</td><td>Май 2026</td><td><span class="badge green">Готов</span></td><td>01.06.2026</td><td>PDF, Excel</td></tr>
<tr><td>Аналитическая справка</td><td>Май 2026</td><td><span class="badge green">Готов</span></td><td>02.06.2026</td><td>PDF, Word</td></tr>
<tr><td>Презентация для совещания</td><td>Июнь 2026</td><td><span class="badge amber">В работе</span></td><td>—</td><td>PPTX</td></tr>
<tr><td>Квартальный отчёт</td><td>Q2 2026</td><td><span class="badge amber">В работе</span></td><td>—</td><td>PDF, Excel</td></tr>
<tr><td>Рейтинг филиалов</td><td>Июнь 2026</td><td><span class="badge green">Готов</span></td><td>04.06.2026</td><td>PDF</td></tr>
</tbody>
</table>
</div>
</div>`
}
function pageRisks(){
let risks=events.filter(e=>e.s==='late'||e.s==='warn').map(e=>{
let level=e.s==='late'?'high':e.p<30?'low':e.p<60?'medium':'low'
let reason=e.s==='late'?'Просрочен срок исполнения':e.p<30?'Ранний этап, риск отставания':e.p<60?'Требуется ускорение':'Незначительное отставание'
return {event:e.t,branch:branches[e.b],level,reason}
})
let hi=risks.filter(r=>r.level==='high').length
let md=risks.filter(r=>r.level==='medium').length
let lo=risks.filter(r=>r.level==='low').length
return `<div class="page active"><h2>Карта рисков</h2>
<div class="stats-row">
<div class="stat-card red"><div class="lbl">Высокий риск</div><div class="num">${hi}</div></div>
<div class="stat-card amber"><div class="lbl">Средний риск</div><div class="num">${md}</div></div>
<div class="stat-card blue"><div class="lbl">Низкий риск</div><div class="num">${lo}</div></div>
</div>
<div class="panel">
<h3>Реестр рисков</h3>
<table>
<thead><tr><th>Мероприятие</th><th>Филиал/ДАО</th><th>Уровень</th><th>Причина</th></tr></thead>
<tbody>${risks.map(r=>`<tr>
<td style="font-size:12px"><strong>${r.event.slice(0,65)}...</strong></td>
<td style="font-size:12px">${r.branch}</td>
<td>${r.level==='high'?'<span class="badge red">Высокий</span>':r.level==='medium'?'<span class="badge amber">Средний</span>':'<span class="badge blue">Низкий</span>'}</td>
<td style="font-size:12px">${r.reason}</td></tr>`).join('')}</tbody>
</table>
</div>
</div>`
}
function openEvent(id){
let e=events.find(x=>x.id===id); if(!e) return
document.getElementById('modalContent').innerHTML=`
<button class="close" onclick="closeModal()">&times;</button>
<div style="display:flex;gap:8px;align-items:center;margin-bottom:12px">
<span class="badge blue">Раздел ${['I','II','III','IV','V'][e.sec]}</span>
<span class="ai-tag">🤖 Анализ ИИ</span>
</div>
<h2>${e.t}</h2>
<div class="meta">
<div class="fld">Филиал/ДАО<strong>${branches[e.b]}</strong></div>
<div class="fld">Направление<strong>${e.d}</strong></div>
<div class="fld">Срок исполнения<strong>${e.due}</strong></div>
<div class="fld">Факт исполнения<strong>${e.done}</strong></div>
<div class="fld">Ответственный<strong>${e.r}</strong></div>
<div class="fld">Прогресс<strong>${pctBar(e.p)}</strong></div>
<div class="fld">Статус<strong>${sBadge(e.s)}</strong></div>
</div>
${e.docs.length?`<div style="font-weight:600;margin:8px 0 4px;font-size:14px">Форма завершения / материалы:</div><div class="docs">${e.docs.map(d=>`<span>📄 ${d}</span>`).join('')}</div>`:''}
<div class="ai-block"><h4>🤖 Выводы ИИ-агента</h4>${e.ai}</div>
<div style="font-weight:600;margin:8px 0 4px;font-size:14px">История изменений:</div>
<div class="timeline">${e.h.map(h=>`<div class="timeline-item"><div class="dot"></div>${h}</div>`).join('')}</div>
`
document.getElementById('modalOverlay').classList.add('open')
}
function closeModal(){document.getElementById('modalOverlay').classList.remove('open')}
function navTo(page,filter='',secFilter='',statusFilter=''){
document.querySelectorAll('.sidebar nav a').forEach(a=>a.classList.remove('active'))
document.querySelector(`[data-page="${page}"]`)?.classList.add('active')
let content
switch(page){
case 'dashboard': content=pageDashboard(); break
case 'register': content=pageRegister(filter,secFilter,statusFilter); break
case 'analytics': content=pageAnalytics(); break
case 'reports': content=pageReports(); break
case 'risks': content=pageRisks(); break
default: content=pageDashboard()
}
document.getElementById('mainContent').innerHTML=content
}
function refreshPage(page,filter,secFilter,statusFilter){navTo(page,filter,secFilter,statusFilter)}
document.querySelectorAll('.sidebar nav a').forEach(a=>a.addEventListener('click',e=>{e.preventDefault();navTo(a.dataset.page)}))
document.getElementById('modalOverlay').addEventListener('click',function(e){if(e.target===this)closeModal()})
document.addEventListener('keydown',e=>{if(e.key==='Escape')closeModal()})
navTo('dashboard')
</script>
</body>
</html>