samruk-ai-agent/index.html

536 lines
61 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:'Провести анализ, в том числе с использованием аналитических платформ (Microsoft Teams, Power BI, Tableau, Qlik и др.), и в случае необходимости, осуществить пересмотр внутренних нормативных документов филиалов/ДАО Общества в соответствии со «Стратегией развития производственной безопасности АО «Самрук-Қазына» на 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:'Организовывать тематические совещания по вопросам производственной безопасности: a) руководство Общества с филиалами/ДАО, не менее 1 раза в квартал; b) руководство филиалов/ДАО со структурными подразделениями, не менее 1 раза в месяц; c) руководство региональных подразделений с подрядными организациями, не менее 1 раза в квартал.', 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:'Проводить мероприятия по обмену опытом в области производственной безопасности: a) продолжить практику обмена передовым опытом на площадке Комитета HSE, в том числе путем выездов на производственные объекты ПК; b) рассмотреть возможность организации обмена опытом с иностранными и казахстанскими компаниями, в том числе путем проведения онлайн-семинаров.', 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:'Провести анализ эффективности реализуемых мероприятий по охране здоровья: a) организовать и обеспечить 100% прохождение обязательных периодических медицинских осмотров; b) организовать ежегодную «Неделю благополучия» (Well-being Week); c) создать условия для прохождения медицинского скрининга; d) внедрить алгоритм учета и расследования микротравм.', b:4, d:sections[0], s:'wait', p:15, due:'30.09.2026', done:'—',
r:'a) Директор ДПБ / b) Генеральный директор КУ, Управляющий директор по персоналу / c,d) Генеральные директора филиалов и ДАО',
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:'Проводить работы по техническому перевооружению морально и физически изношенного оборудования, зданий и сооружений, эксплуатация которых из-за их технического состояния сопровождается повышенными рисками возникновения аварий и несчастных случаев с тяжёлыми и летальными исходами, в соответствии с ранее утвержденными Планами на 2024-2027 годы.', 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:'Филиалам/ДАО Общества не реже 1 раза в квартал проводить проверку согласно адаптированным проверочным листам в области БиОТ, промышленной и пожарной безопасности в соответствии с требованиями законодательства Республики Казахстан.', 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:'Провести работу по повышению эффективности управления подрядными организациями: a) аудит подрядчиков внутри филиалов/ДАО согласно типового чек-листа Фонда; b) проведение стартовых/установочных совещаний с подрядными организациями перед допуском на территорию объекта.', 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:'Обеспечить контроль за состоянием производственной безопасности на производственных объектах: a) руководителям Общества уровня СЕО-1 не реже 1 раза в квартал лично проверять одно из филиалов/подрядных организаций; b) первым руководителям филиалов лично принимать участие во внутреннем производственном контроле с посещением площадок не реже 1 раза в квартал.', b:0, d:sections[1], s:'warn', p:35, due:'31.12.2026', done:'—',
r:'a) Главный административный директор, Директор ДПБ / b) Генеральные директора филиалов и ДАО',
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:'Обеспечить проведение: a) не менее одной учебной тревоги и/или противоаварийной тренировки по ликвидации крупной аварии, ЧС на опасном производственном объекте; b) не менее двух тренировок по тушению пожара в административных зданиях; c) не менее одного практического занятия по оказанию первой помощи.', b:1, d:sections[2], s:'warn', p:30, due:'31.12.2026', done:'—',
r:'a) Управляющий директор по безопасности / b) Генеральный директор СФ / c) Генеральные директора филиалов и ДАО',
docs:['Акты о проведении тренировок','Пресс-релизы'],
ai:'Проведено 1 учение по ликвидации аварии (ВКО). Пожарные тренировки: 1 из 2. Занятия по первой помощи запланированы на Q3. Необходимо ускорить.',
h:['01.02 — Мероприятие создано','15.03 — Учение ВКО','15.05 — Пожарная тренировка №1']},
{id:20, sec:2, t:'Усилить работу по реагированию на ЧС: a) внедрить процедуру «Crisis Management System»; b) провести обучение ответственных работников по действиям в условиях ЧС; c) провести не менее двух заседаний штабов.', 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:'Проведение мероприятий по производственной безопасности: a) стратегических сессий/Форумов для первых руководителей филиалов/ДАО; b) семинаров для подрядных организаций ПК на отдельных площадках филиалов/ДАО.', 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:'Проведение молодежных проектных инициатив в рамках работы Центра молодых работников по производственной безопасности: a) публикация историй в журнале «SK News»; b) посещение рабочих мест с НС за 2022-2025 гг.; c) привлечение молодых специалистов в перекрёстные аудиты; d) онлайн-семинары/прямые эфиры.', b:6, d:sections[3], s:'warn', p:40, due:'31.12.2026', done:'—',
r:'a) Управляющий директор по персоналу, Департамент по коммуникациям / b,c,d) Директор ДПБ, Генеральные директора филиалов и ДАО',
docs:['Публикации в SK News','Материалы мероприятий'],
ai:'Опубликовано 2 истории в SK News. Проведён 1 выезд молодых специалистов. 3 молодых специалиста привлечены в перекрёстные аудиты.',
h:['01.02 — Мероприятие создано','15.03 — Публикация №1','01.05 — Выезд']},
{id:26, sec:3, t:'Усилить наглядную агитацию по производственной безопасности: a) разработка видеороликов/презентаций; b) серии видеороликов «Безопасность будущего»; c) выпуск подкаста с участием трудовых династий; d) разработка постеров, брошюр, информационных рассылок.', b:2, d:sections[3], s:'warn', p:50, due:'31.12.2026', done:'—',
r:'a) Генеральные директора филиалов и ДАО / b,c) Директор ДПБ, Департамент по коммуникациям / d) Директор ДПБ',
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:'Проведение мероприятий, направленных на пропаганду безопасного выполнения работ через семейные ценности: a) направление информационного письма членам семьи отличившегося работника; b) проведение Семейных дней охраны труда; c) проведение конкурса рисунков на тему «Спецодежда будущего!».', 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:'Разработать корпоративный сборник лучших практик по производственной безопасности в формате методического пособия или интерактивного PDF документа, отражающий меры по снижению производственного травматизма и управлению критическими рисками.', 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>