samruk-ai-agent/index.html

414 lines
29 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;--purple:#8B5CF6;--sidebar-w:260px;--topbar-h:60px}
*{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}
a{color:var(--cyan);text-decoration:none}
/* SIDEBAR */
.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:18px;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 */
.main{margin-left:var(--sidebar-w);flex:1;padding:24px 32px;max-width:calc(100vw - var(--sidebar-w))}
/* PAGE */
.page{display:none}
.page.active{display:block}
.page h2{font-size:24px;font-weight:700;margin-bottom:20px}
/* STATS ROW */
.stats-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,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 .stat-label{font-size:13px;color:var(--gray-500);margin-bottom:4px}
.stat-card .stat-num{font-size:32px;font-weight:800;line-height:1}
.stat-card .stat-sub{font-size:13px;color:var(--gray-500);margin-top:4px}
.stat-card.red .stat-num{color:var(--red)}
.stat-card.green .stat-num{color:var(--green)}
.stat-card.amber .stat-num{color:var(--amber)}
.stat-card.blue .stat-num{color:var(--blue)}
/* GRID 2col */
.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 */
.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 */
.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;transition:.3s}
.chart-stub .bar.danger{background:var(--red)}
.chart-stub .bar.warn{background:var(--amber)}
.chart-stub .bar.good{background:var(--green)}
.chart-stub .bar.blue{background:var(--blue)}
.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}
/* RING */
.ring{position:relative;width:120px;height:120px;margin:0 auto 12px}
.ring svg{transform:rotate(-90deg)}
.ring .pct{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:28px;font-weight:800}
.ring-label{text-align:center;font-size:14px;color:var(--gray-500)}
/* TABLE */
table{width:100%;border-collapse:collapse}
th,td{padding:12px 16px;text-align:left;font-size:14px}
th{font-weight:600;color:var(--gray-500);font-size:12px;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 */
.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 */
.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:260px}
.filters .count{font-size:13px;color:var(--gray-500);margin-left:auto}
/* AI BADGE */
.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 */
.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:800px;width:90vw;max-height:85vh;overflow-y:auto;padding:32px}
.modal h2{font-size:22px;margin-bottom:8px}
.modal .close{float:right;background:none;border:none;font-size:24px;cursor:pointer;line-height:1;color:var(--gray-500)}
.modal .meta{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:20px 0}
.modal .meta .field{font-size:13px;color:var(--gray-500)}
.modal .meta .field 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;color:var(--ink)}
.modal .timeline{font-size:13px;color:var(--gray-500);line-height:2}
.modal .timeline strong{color:var(--ink)}
/* RATINGS */
.rating-bar{display:flex;align-items:center;gap:12px;padding:8px 0}
.rating-bar .name{width:180px;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;transition:.5s}
/* RESPONSIVE */
@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>
<!-- SIDEBAR -->
<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>Реестр мероприятий</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 CONTENT -->
<main class="main" id="mainContent"></main>
<!-- MODAL -->
<div class="modal-overlay" id="modalOverlay">
<div class="modal" id="modalContent"></div>
</div>
<script>
// ===== MOCK DATA =====
const branches = ['АО "КазТрансОйл"','АО "KEGOC"','АО "Казпочта"','АО "НК КТЖ"','АО "Казатомпром"','АО "Эйр Астана"','АО "QazaqGaz"','ТОО "Самрук-Энерго"']
const statuses = {done:'Исполнено',warn:'На контроле',late:'Просрочено',wait:'Не начато'}
const directions = ['Охрана труда','Пожарная безопасность','Промбезопасность','Экология','ГО и ЧС','Транспортная безопасность']
const events = [
{id:1, title:'Проведение внепланового инструктажа по охране труда', branch:branches[0], dir:directions[0], status:'done', progress:100, due:'15.02.2026', done:'12.02.2026', resp:'Ахметов К.Т.', docs:['Приказ №45','Журнал инструктажа','Фотоотчёт'], ai:'Все документы в наличии. Инструктаж проведён в срок. Замечаний нет.', history:['10.02 — Мероприятие создано','12.02 — Загружен журнал инструктажа','12.02 — Статус: Исполнено'] },
{id:2, title:'Замена средств пожаротушения на складе ГСМ', branch:branches[1], dir:directions[1], status:'warn', progress:65, due:'30.06.2026', done:'—', resp:'Сериков А.М.', docs:['Смета','Договор поставки'], ai:'Отсутствует акт выполненных работ. Рекомендуется ускорить монтаж. Срок под риском.', history:['05.03 — Мероприятие создано','20.04 — Загружен договор','01.06 — ИИ: риск срыва срока'] },
{id:3, title:'Аттестация сварщиков по промышленной безопасности', branch:branches[4], dir:directions[2], status:'late', progress:30, due:'01.04.2026', done:'—', resp:'Касымов Б.Е.', docs:[], ai:'Просрочка 64 дня. Подтверждающие документы отсутствуют. Эскалация руководителю.', history:['01.01 — Мероприятие создано','01.04 — Срок истёк','05.04 — Эскалация руководителю'] },
{id:4, title:'Экологический аудит очистных сооружений', branch:branches[6], dir:directions[3], status:'done', progress:100, due:'01.03.2026', done:'26.02.2026', resp:'Нурланов Д.С.', docs:['Акт аудита','Лабораторные пробы','Фото'], ai:'Аудит пройден. Лабораторные показатели в норме. Рекомендации выполнены.', history:['10.01 — Мероприятие создано','20.02 — Загружены пробы','26.02 — Статус: Исполнено'] },
{id:5, title:'Обучение персонала по ГО и ЧС', branch:branches[7], dir:directions[4], status:'warn', progress:45, due:'01.08.2026', done:'—', resp:'Тулегенов Е.А.', docs:['Программа обучения'], ai:'Утверждена программа. Не загружены списки групп. Рекомендуется начать обучение.', history:['01.04 — Мероприятие создано','15.05 — Утверждена программа','01.06 — ИИ: ожидается список групп'] },
{id:6, title:'Проверка состояния ж/д переездов', branch:branches[3], dir:directions[5], status:'done', progress:100, due:'10.05.2026', done:'05.05.2026', resp:'Искаков Р.Н.', docs:['Акт осмотра','Предписание','Фото'], ai:'Проверка проведена. Выявлено 3 замечания. Устранены в срок.', history:['01.04 — Мероприятие создано','28.04 — Акт осмотра','05.05 — Статус: Исполнено'] },
{id:7, title:'Монтаж системы оповещения на производстве', branch:branches[2], dir:directions[1], status:'wait', progress:0, due:'01.12.2026', done:'—', resp:'Сапаров А.Д.', docs:[], ai:'Мероприятие не начато. До срока 180 дней. Рекомендуется назначить поставщика.', history:['01.06 — Мероприятие создано'] },
{id:8, title:'Медицинский осмотр работников вредных производств', branch:branches[5], dir:directions[0], status:'done', progress:100, due:'01.04.2026', done:'28.03.2026', resp:'Алиева Г.С.', docs:['Приказ','Заключения врачей','Отчёт'], ai:'Осмотр пройден. 100% охват. Противопоказаний не выявлено.', history:['01.03 — Мероприятие создано','25.03 — Загружены заключения','28.03 — Статус: Исполнено'] },
{id:9, title:'Ремонт ограждения опасной зоны на участке №3', branch:branches[0], dir:directions[2], status:'late', progress:20, due:'15.03.2026', done:'—', resp:'Маратов Ж.К.', docs:[], ai:'Просрочено 81 день. Отсутствует проектная документация. Требуется вмешательство.', history:['15.01 — Мероприятие создано','15.03 — Срок истёк','20.03 — Эскалация'] },
{id:10, title:'Закуп СИЗ для полевых бригад', branch:branches[1], dir:directions[0], status:'warn', progress:80, due:'15.07.2026', done:'—', resp:'Омарова Д.Т.', docs:['Спецификация','Счёт-фактура'], ai:'СИЗ закуплены. Ожидается поставка. Рисков срыва нет.', history:['01.05 — Мероприятие создано','01.06 — Счёт оплачен'] },
{id:11, title:'Пересмотр плана ликвидации аварий', branch:branches[4], dir:directions[4], status:'warn', progress:50, due:'01.09.2026', done:'—', resp:'Касымов Б.Е.', docs:['Проект ПЛА'], ai:'Проект ПЛА на согласовании. Замечаний от проверяющих нет.', history:['01.04 — Мероприятие создано','20.05 — Проект загружен'] },
{id:12, title:'Проверка дымовых извещателей в админздании', branch:branches[7], dir:directions[1], status:'done', progress:100, due:'01.05.2026', done:'28.04.2026', resp:'Тулегенов Е.А.', docs:['Акт проверки','Сертификаты'], ai:'Все извещатели исправны. Сертификаты действительны.', history:['01.04 — Мероприятие создано','28.04 — Статус: Исполнено'] },
]
// ===== 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'}">${statuses[s]||s}</span>`
}
// ===== PAGES =====
function pageDashboard(){
let done=events.filter(e=>e.status==='done').length
let late=events.filter(e=>e.status==='late').length
let warn=events.filter(e=>e.status==='warn').length
let total=events.length
let donePct=Math.round(done/total*100)
return `
<div class="page active" id="page-dashboard">
<h2>Дашборд производственной безопасности</h2>
<div class="stats-row">
<div class="stat-card green"><div class="stat-label">Всего мероприятий</div><div class="stat-num">${total}</div></div>
<div class="stat-card green"><div class="stat-label">Исполнено</div><div class="stat-num">${done}</div><div class="stat-sub">${donePct}% от плана</div></div>
<div class="stat-card amber"><div class="stat-label">На контроле</div><div class="stat-num">${warn}</div></div>
<div class="stat-card red"><div class="stat-label">Просрочено</div><div class="stat-num">${late}</div></div>
<div class="stat-card blue"><div class="stat-label">Общий прогресс</div><div class="stat-num">${donePct}%</div></div>
</div>
<div class="grid2">
<div class="panel">
<h3>Исполнение по направлениям</h3>
${directions.map(d=>{
let items=events.filter(e=>e.dir===d)
let pct=Math.round(items.filter(e=>e.status==='done').length/Math.max(1,items.length)*100)
return `<div class="rating-bar"><span class="name">${d}</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>
${branches.slice(0,6).map(b=>{
let items=events.filter(e=>e.branch===b)
let pct=Math.round(items.filter(e=>e.status==='done').length/Math.max(1,items.length)*100)
return `<div class="rating-bar"><span class="name">${b.replace('АО ','').replace('ТОО ','')}</span><div class="track"><div class="fill" style="width:${pct}%;background:${pct>=70?'var(--green)':pct>=40?'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" style="height:40%"></div>
<div class="bar" style="height:55%"></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 (план)</span></div>
</div>
</div>`
}
function pageRegister(filter='',statusFilter=''){
let list=events
if(filter) list=list.filter(e=>e.title.toLowerCase().includes(filter.toLowerCase())||e.branch.toLowerCase().includes(filter.toLowerCase()))
if(statusFilter) list=list.filter(e=>e.status===statusFilter)
return `
<div class="page active" id="page-register">
<h2>Реестр мероприятий</h2>
<div class="filters">
<input placeholder="Поиск по названию или организации..." oninput="refreshPage('register',this.value,document.getElementById('statusFilter')?.value||'')" id="searchInput">
<select onchange="refreshPage('register',document.getElementById('searchInput')?.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.title}</strong></td>
<td style="font-size:13px">${e.branch}</td>
<td style="font-size:13px">${e.dir}</td>
<td style="font-size:13px">${e.due}</td>
<td>${pctBar(e.progress)}</td>
<td>${sBadge(e.status)}</td>
</tr>`).join('')}</tbody>
</table>
</div>
</div>`
}
function pageAnalytics(){
let orgRatings=branches.map(b=>{
let items=events.filter(e=>e.branch===b)
let done=items.filter(e=>e.status==='done').length
let late=items.filter(e=>e.status==='late').length
return {name:b,done,late,total:items.length,pct:Math.round(done/Math.max(1,items.length)*100)}
}).sort((a,b)=>b.pct-a.pct)
return `
<div class="page active" id="page-analytics">
<h2>Аналитика</h2>
<div class="grid2">
<div class="panel">
<h3>Рейтинг организаций по исполнению</h3>
${orgRatings.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>=70?'var(--green)':r.pct>=40?'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>
${events.filter(e=>e.status==='late').map(e=>`
<div class="rating-bar">
<span class="name" style="font-size:13px">${e.title.slice(0,40)}...</span>
<span style="font-size:13px;color:var(--red);font-weight:700">${e.due}</span>
<span class="badge red">${e.branch.split('"')[1]||e.branch}</span>
</div>`).join('')||'<p style="color:var(--gray-500)">Просроченных мероприятий нет</p>'}
</div>
</div>
<div class="grid3" style="margin-top:24px">
<div class="panel" style="text-align:center">
<h3>Всего мероприятий</h3>
<div style="font-size:48px;font-weight:800;color:var(--cyan)">${events.length}</div>
</div>
<div class="panel" style="text-align:center">
<h3>Документов загружено</h3>
<div style="font-size:48px;font-weight:800;color:var(--green)">${events.reduce((s,e)=>s+e.docs.length,0)}</div>
</div>
<div class="panel" style="text-align:center">
<h3>Средний % исполнения</h3>
<div style="font-size:48px;font-weight:800;color:var(--blue)">${Math.round(events.reduce((s,e)=>s+e.progress,0)/events.length)}%</div>
</div>
</div>
</div>`
}
function pageReports(){
return `
<div class="page active" id="page-reports">
<h2>Формирование отчётности</h2>
<div class="stats-row">
<div class="stat-card"><div class="stat-label">Ежемесячный отчёт</div><div class="stat-num" style="font-size:20px">За май 2026</div><div class="stat-sub"><span class="badge green">Сформирован</span></div></div>
<div class="stat-card"><div class="stat-label">Квартальный отчёт</div><div class="stat-num" style="font-size:20px">Q2 2026</div><div class="stat-sub"><span class="badge amber">В обработке</span></div></div>
<div class="stat-card"><div class="stat-label">Годовой отчёт</div><div class="stat-num" style="font-size:20px">2026</div><div class="stat-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=[
{event:'Замена средств пожаротушения на складе ГСМ', branch:branches[1], level:'medium', reason:'Отставание от графика на 15%'},
{event:'Аттестация сварщиков', branch:branches[4], level:'high', reason:'Просрочка 64 дня, отсутствуют документы'},
{event:'Ремонт ограждения опасной зоны', branch:branches[0], level:'high', reason:'Просрочка 81 день, нет проектной документации'},
{event:'Обучение персонала по ГО и ЧС', branch:branches[7], level:'low', reason:'Не загружены списки групп'},
{event:'Пересмотр плана ликвидации аварий', branch:branches[4], level:'low', reason:'План на согласовании, замечаний нет'},
]
return `
<div class="page active" id="page-risks">
<h2>Карта рисков</h2>
<div class="stats-row">
<div class="stat-card red"><div class="stat-label">Высокий риск</div><div class="stat-num">${risks.filter(r=>r.level==='high').length}</div></div>
<div class="stat-card amber"><div class="stat-label">Средний риск</div><div class="stat-num">${risks.filter(r=>r.level==='medium').length}</div></div>
<div class="stat-card blue"><div class="stat-label">Низкий риск</div><div class="stat-num">${risks.filter(r=>r.level==='low').length}</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><strong>${r.event}</strong></td>
<td style="font-size:13px">${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:13px">${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 class="ai-tag">🤖 Анализ ИИ</div>
<h2>${e.title}</h2>
<div class="meta">
<div class="field">Организация<strong>${e.branch}</strong></div>
<div class="field">Направление<strong>${e.dir}</strong></div>
<div class="field">Срок исполнения<strong>${e.due}</strong></div>
<div class="field">Дата исполнения<strong>${e.done}</strong></div>
<div class="field">Ответственный<strong>${e.resp}</strong></div>
<div class="field">Прогресс<strong>${pctBar(e.progress)}</strong></div>
<div class="field">Статус<strong>${sBadge(e.status)}</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.history.map(h=>`<div>${h}</div>`).join('')}</div>
`
document.getElementById('modalOverlay').classList.add('open')
}
function closeModal(){ document.getElementById('modalOverlay').classList.remove('open') }
// NAVIGATION
function navTo(page, filter='', 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,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,statusFilter){ navTo(page,filter,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()})
// INIT
navTo('dashboard')
</script>
</body>
</html>