samruk-ai-agent/index.html

412 lines
60 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>План ПБ 2026 — Казахтелеком</title>
<style>
:root{--ink:#0B1A2E;--cyan:#00B4D8;--cyan-l:#E0F7FA;--white:#fff;--gray-50:#F8FAFC;--gray-100:#F1F5F9;--gray-200:#E2E8F0;--gray-500:#64748B;--gray-700:#334155;--green:#10B981;--red:#EF4444;--amber:#F59E0B;--blue:#3B82F6;--sidebar:240px}
*{box-sizing:border-box;margin:0;padding:0}
body{font:14px/1.5 'Segoe UI',system-ui,-apple-system,sans-serif;color:var(--gray-700);background:var(--gray-50);min-height:100vh}
input,select,textarea,button{font:inherit;outline:none}
.btn{background:var(--cyan);color:var(--white);padding:10px 20px;border-radius:8px;font-weight:600;font-size:14px;border:none;cursor:pointer;transition:.15s}.btn:hover{background:#0096B0}
.btn-sm{padding:6px 14px;font-size:12px}.btn-red{background:var(--red);color:#fff}.btn-gn{background:var(--green);color:#fff}
.btn-ghost{background:transparent;color:var(--gray-500);border:1px solid var(--gray-200)}.btn-ghost:hover{background:var(--gray-100)}
#loginBox{display:flex;align-items:center;justify-content:center;min-height:100vh;background:linear-gradient(135deg,#0B1A2E 0%,#1a3a5c 100%)}
#loginBox>div{background:var(--white);border-radius:20px;padding:48px 40px;width:420px;max-width:90vw;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,.3)}
#loginBox .logo{font-size:28px;font-weight:800;color:var(--ink);margin-bottom:4px}
#loginBox .logo span{color:var(--cyan)}
#loginBox .sub{color:var(--gray-500);font-size:13px;margin-bottom:32px}
#loginBox input{display:block;width:100%;padding:14px 16px;border:2px solid var(--gray-200);border-radius:10px;font-size:14px;margin-bottom:16px;transition:.15s}
#loginBox input:focus{border-color:var(--cyan)}
#app{display:none;display:flex;min-height:100vh}
.sidebar{width:var(--sidebar);background:var(--ink);color:var(--white);position:fixed;top:0;left:0;bottom:0;display:flex;flex-direction:column;z-index:10}
.sidebar .logo{padding:24px 20px;font-size:18px;font-weight:800;border-bottom:1px solid rgba(255,255,255,.08)}
.sidebar .logo span{color:var(--cyan)}
.sidebar nav{flex:1;padding:12px 0}
.sidebar nav a{display:flex;align-items:center;gap:12px;padding:12px 20px;color:rgba(255,255,255,.6);font-weight:500;font-size:14px;cursor:pointer;text-decoration:none;transition:.15s;border-left:3px solid transparent}
.sidebar nav a:hover,.sidebar nav a.on{color:var(--white);background:rgba(0,180,216,.1);border-left-color:var(--cyan)}
.sidebar .user{padding:20px;border-top:1px solid rgba(255,255,255,.08);font-size:12px;color:rgba(255,255,255,.4)}
.main{margin-left:var(--sidebar);flex:1;padding:24px 32px;max-width:calc(100vw - var(--sidebar))}
.topbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px}
.topbar h2{font-size:22px;font-weight:700;color:var(--ink)}
.topbar .right{display:flex;align-items:center;gap:16px}
.notif-btn{position:relative;background:var(--white);border:1px solid var(--gray-200);border-radius:10px;padding:8px 12px;cursor:pointer;font-size:18px}
.notif-btn .cnt{position:absolute;top:-6px;right:-6px;background:var(--red);color:#fff;border-radius:100px;font-size:10px;padding:2px 6px;font-weight:700;min-width:18px;text-align:center}
.notif-drop{position:absolute;top:52px;right:0;width:400px;max-width:90vw;background:var(--white);border:1px solid var(--gray-200);border-radius:14px;box-shadow:0 10px 40px rgba(0,0,0,.12);z-index:300;display:none;max-height:420px;overflow-y:auto}
.notif-drop.on{display:block}.notif-drop .it{padding:14px 18px;border-bottom:1px solid var(--gray-100);font-size:13px}.notif-drop .it b{display:block;margin-bottom:3px}.notif-drop .it span{font-size:11px;color:var(--gray-500)}
.tabs{display:flex;gap:8px;margin-bottom:24px}
.tab{padding:10px 22px;border-radius:10px;border:none;background:var(--white);cursor:pointer;font-size:14px;font-weight:600;color:var(--gray-500);box-shadow:0 1px 3px rgba(0,0,0,.04);transition:.15s}
.tab.on{background:var(--cyan);color:var(--white)}
.pg{display:none}.pg.on{display:block}
.card{background:var(--white);border-radius:14px;padding:24px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.04);border:1px solid var(--gray-100)}
.card h3{font-size:17px;font-weight:700;margin-bottom:12px;color:var(--ink)}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;margin-bottom:20px}
.stat{background:var(--white);border-radius:12px;padding:20px 24px;box-shadow:0 1px 3px rgba(0,0,0,.04);border:1px solid var(--gray-100);text-align:center}
.stat .n{font-size:30px;font-weight:800}.stat .l{font-size:12px;color:var(--gray-500);margin-top:4px}
.stat.r .n{color:var(--red)}.stat.g .n{color:var(--green)}.stat.a .n{color:var(--amber)}.stat.b .n{color:var(--cyan)}
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;border-bottom:2px solid var(--gray-100);background:var(--gray-50)}
td{border-bottom:1px solid var(--gray-100)}
tr.rd td{background:#FEF2F2}tr.am td{background:#FFFBEB}
.badge{display:inline-block;padding:4px 10px;border-radius:100px;font-size:11px;font-weight:600}
.badge.g{background:#D1FAE5;color:#065F46}.badge.a{background:#FEF3C7;color:#92400E}.badge.r{background:#FEE2E2;color:#991B1B}.badge.b{background:#DBEAFE;color:#1E40AF}.badge.w{background:var(--gray-100);color:var(--gray-700)}
.fr{display:flex;gap:10px;margin-bottom:14px;flex-wrap:wrap;align-items:center}
.fr input,.fr select{padding:10px 14px;border:1px solid var(--gray-200);border-radius:8px;font-size:13px;background:var(--white)}.fr input{min-width:220px}
.modal-o{position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:99;display:none;align-items:center;justify-content:center}.modal-o.on{display:flex}
.modal{background:var(--white);border-radius:16px;max-width:720px;width:94vw;max-height:88vh;overflow-y:auto;padding:32px;box-shadow:0 20px 60px rgba(0,0,0,.15)}
.modal .x{float:right;border:none;background:none;font-size:26px;cursor:pointer;color:var(--gray-500);line-height:1}
.modal label{display:block;font-size:12px;font-weight:600;color:var(--gray-500);margin-bottom:4px;margin-top:12px}
.modal input,.modal select,.modal textarea{width:100%;padding:10px 14px;border:1px solid var(--gray-200);border-radius:8px;font-size:13px;margin-bottom:6px}
.modal textarea{min-height:64px;resize:vertical}
.meta{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px;font-size:12px;color:var(--gray-500)}.meta strong{display:block;color:var(--ink);font-size:14px;margin-top:2px}
.mt{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px}.mt span{padding:6px 14px;border:1px solid var(--gray-200);border-radius:100px;font-size:12px;font-weight:600;cursor:pointer;transition:.15s}.mt span.on{background:var(--cyan);color:var(--white);border-color:var(--cyan)}.mt span:hover{border-color:var(--cyan)}
.si{display:flex;align-items:center;gap:10px;padding:10px 14px;background:var(--gray-50);border-radius:8px;margin-bottom:6px;font-size:13px}.si .n{font-weight:700;color:var(--cyan);font-size:15px;min-width:20px}
.fl{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--gray-50);border-radius:8px;margin-bottom:4px;font-size:12px}.fl .nm{font-weight:600;cursor:pointer}.fl .nm:hover{color:var(--cyan)}.fl .sz{font-size:10px;color:var(--gray-500)}
.up{border:2px dashed var(--gray-200);border-radius:10px;padding:14px;margin-top:8px;text-align:center}.up p{font-size:13px;color:var(--gray-500);margin-bottom:10px}.up input[type=file]{font-size:12px}
.ai-block{background:var(--cyan-l);border-radius:8px;padding:12px;margin:12px 0;font-size:13px}
.hi{font-size:11px;color:var(--gray-500);padding:2px 0}.hi .d{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--cyan);margin-right:6px;vertical-align:middle}
.chart-bar{display:flex;align-items:flex-end;gap:8px;height:160px;padding:0 4px}
.chart-bar>div{flex:1;border-radius:6px 6px 0 0;min-height:4px;transition:.3s}
.ai-chat{border:1px solid var(--gray-200);border-radius:10px;padding:14px;max-height:320px;overflow-y:auto;margin-bottom:12px;background:var(--white)}
.ai-chat .msg{margin-bottom:8px;padding:10px 14px;border-radius:10px;font-size:13px;max-width:88%;line-height:1.5}
.ai-chat .user{background:var(--cyan);color:var(--white);margin-left:auto}.ai-chat .bot{background:var(--gray-50);color:var(--ink)}
.ai-input{display:flex;gap:8px}.ai-input input{flex:1;padding:10px 14px;border:1px solid var(--gray-200);border-radius:8px;font-size:13px}
@media(max-width:768px){
.sidebar{width:0;overflow:hidden}.sidebar .logo,.sidebar nav,.sidebar .user{display:none}
.main{margin-left:0;max-width:100vw;padding:16px}
.stats{grid-template-columns:1fr 1fr}.meta{grid-template-columns:1fr}
}
</style>
</head>
<body>
<div id="loginBox"><div>
<div class="logo"><span>Qazaq</span>Telecom</div>
<p class="sub">План производственной безопасности 2026</p>
<input id="lem" placeholder="curator@telecom.kz">
<input id="lpw" type="password" placeholder="Пароль">
<p id="lerr" style="color:var(--red);font-size:12px;display:none;margin-bottom:8px">Неверная почта</p>
<button class="btn" style="width:100%;padding:14px" onclick="doLogin()">Войти</button>
</div></div>
<div id="app" style="display:none">
<div class="sidebar">
<div class="logo"><span>Qazaq</span>Telecom</div>
<nav>
<a class="on" data-pg="ev" onclick="switchPg('ev')">📋 Мероприятия</a>
<a data-pg="an" onclick="switchPg('an')">📊 Аналитика</a>
<a data-pg="rp" onclick="switchPg('rp')">📥 Отчётность</a>
<a data-pg="ai" onclick="switchPg('ai')">🤖 ИИ-помощник</a>
</nav>
<div class="user" id="ul"></div>
</div>
<div class="main">
<div class="topbar">
<h2 id="pageTitle">Мероприятия</h2>
<div class="right">
<div style="position:relative"><button class="notif-btn" onclick="toggleN()">🔔<span class="cnt" id="nc">0</span></button><div class="notif-drop" id="nd"></div></div>
<button class="btn btn-ghost btn-sm" onclick="doLogout()">Выйти</button>
</div>
</div>
<div class="pg on" id="pg-ev"></div><div class="pg" id="pg-an"></div><div class="pg" id="pg-rp"></div><div class="pg" id="pg-ai"></div>
</div>
</div>
<div class="modal-o" id="mo"><div class="modal" id="mc"></div></div>
<script>
var sec=["I. Люди. Повышение культуры безопасности","II. Безопасность при эксплуатации оборудования","III. Предупреждение и готовность к ликвидации аварий и ЧС","IV. Информационно-разъяснительная работа","V. Внедрение ИИ и цифровизации"];
var br=["Дирекция ПБ","Дивизион «Сеть»","Корпоративный бизнес","Розничный бизнес","Сервисная фабрика","Телеком Комплект","Корпоративный университет","Управление проектами","Цифровой бизнес"];
var reg=["Центральный","Алматинский","Южный","Северный","Восточный","Западный"];
var st={wait:"Не начато",warn:"В процессе",late:"Просрочено",done:"Исполнено"};
var ms=["2026-01","2026-02","2026-03","2026-04","2026-05","2026-06","2026-07","2026-08","2026-09","2026-10","2026-11","2026-12"];
var mn=["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"];
var titles={ev:"Мероприятия",an:"Аналитика",rp:"Отчётность",ai:"ИИ-помощник"};
function M(i){var p=ms[i].split("-");return mn[parseInt(p[1])-1]+" "+p[0]}
function esc(s){return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}
function nl(s){return esc(s).replace(/\n/g,"<br>")}
function sb(s){var m={done:"g",warn:"a",late:"r",wait:"w"};return'<span class="badge '+m[s]+'">'+st[s]+'</span>'}
function daysLeft(e){
if(e.s==="done")return'<span style="color:var(--green)">✓</span>';
if(e.s==="late")return'<span style="color:var(--red);font-weight:700">ПРОСРОЧЕНО</span>';
var p=e.due.split(".");if(p.length!==3)return"";
var d=new Date(parseInt(p[2]),parseInt(p[1])-1,parseInt(p[0]));
var days=Math.round((d-new Date())/86400000);
if(days<0)return'<span style="color:var(--red);font-weight:700">'+Math.abs(days)+' дн.</span>';
if(days<=7)return'<span style="color:var(--red);font-weight:600">'+days+' дн.</span>';
if(days<=14)return'<span style="color:var(--amber);font-weight:600">'+days+' дн.</span>';
if(days<=30)return'<span style="color:var(--amber)">'+days+' дн.</span>';
return days+' дн.';
}
function rowClass(e){if(e.s==="late")return"rd";if(e.s==="done")return"";var p=e.due.split(".");if(p.length!==3)return"";var d=new Date(parseInt(p[2]),parseInt(p[1])-1,parseInt(p[0]));var days=Math.round((d-new Date())/86400000);if(days<=14)return"rd";if(days<=30)return"am";return""}
var U={"curator@telecom.kz":{n:"Куратор ПБ",b:0,r:"cur"},"dpp@telecom.kz":{n:"Директор ДПБ",b:0,r:"br"},"ahmetov@telecom.kz":{n:"Ахметов К.Т.",b:6,r:"br"},"serikov@telecom.kz":{n:"Сериков А.М.",b:1,r:"br"},"nurlanov@telecom.kz":{n:"Нурланов Д.С.",b:8,r:"br"},"aliev@telecom.kz":{n:"Алиев Г.С.",b:4,r:"br"},"tulegenov@telecom.kz":{n:"Тулегенов Е.А.",b:2,r:"br"},"saparov@telecom.kz":{n:"Сапаров А.Д.",b:3,r:"br"},"maratov@telecom.kz":{n:"Маратов Ж.К.",b:5,r:"br"},"iskakov@telecom.kz":{n:"Искаков Р.Н.",b:7,r:"br"}};
var cu=null,ev=null,cm=5,esi=-1,ex={},chatHistory=[];
function getMD(id,si){var k=si>=0?"sf_"+id+"_s"+si:"sf_"+id;var r=localStorage.getItem(k);return r?JSON.parse(r):{}}
function setMD(id,o,si){var k=si>=0?"sf_"+id+"_s"+si:"sf_"+id;localStorage.setItem(k,JSON.stringify(o))}
function gsc(id){var r=localStorage.getItem("ss_"+id);return r?JSON.parse(r):[]}
function ssc(id,a){localStorage.setItem("ss_"+id,JSON.stringify(a))}
function doLogin(){var e=document.getElementById("lem").value.trim().toLowerCase();if(U[e]){cu={em:e,n:U[e].n,b:U[e].b,r:U[e].r};localStorage.setItem("su",JSON.stringify(cu));showApp()}else document.getElementById("lerr").style.display="block"}
function doLogout(){localStorage.removeItem("su");cu=null;document.getElementById("loginBox").style.display="flex";document.getElementById("app").style.display="none"}
function showApp(){document.getElementById("loginBox").style.display="none";document.getElementById("app").style.display="flex";document.getElementById("ul").textContent=cu.n;loadEvents()}
function loadEvents(){var s=localStorage.getItem("se4");if(s){try{ev=JSON.parse(s);renderEv();return}catch(e){}}var x=new XMLHttpRequest();x.open("GET","data.json",true);x.onload=function(){if(x.status===200){try{ev=JSON.parse(x.responseText);saveEv()}catch(e){ev=[]}};renderEv()};x.onerror=function(){ev=[];renderEv()};x.send()}
function saveEv(){localStorage.setItem("se4",JSON.stringify(ev||[]))}
function switchPg(n){
document.querySelectorAll(".sidebar nav a").forEach(function(a){a.classList.remove("on")});
document.querySelector('[data-pg="'+n+'"]').classList.add("on");
document.querySelectorAll(".pg").forEach(function(p){p.classList.remove("on")});
document.getElementById("pg-"+n).classList.add("on");
document.getElementById("pageTitle").textContent=titles[n];
if(n==="ev")renderEv();else if(n==="an")renderAn();else if(n==="rp")renderRp();else if(n==="ai")renderAi();
}
// ===== EVENTS =====
function toggleEx(id){ex[id]=!ex[id];renderEv()}
function ts(id,si,chk){var s=gsc(id);if(chk&&s.indexOf(si)<0)s.push(si);else if(!chk)s=s.filter(function(x){return x!==si});ssc(id,s);var e=null;for(var i=0;i<ev.length;i++)if(ev[i].id===id){e=ev[i];break}if(e&&e.sub){var p=Math.round(s.length/e.sub.length*100);if(s.length===e.sub.length&&e.s!=="done")e.s="done";e.p=Math.max(e.p,p);e.h.push(new Date().toLocaleDateString()+" — "+cu.n+": подпункты "+s.length+"/"+e.sub.length);saveEv()}renderEv()}
function renderEv(){
var all=ev||[],sf=(document.getElementById("sf2")||{}).value||"",sr2=(document.getElementById("sr2")||{}).value||"",bf=(document.getElementById("bf2")||{}).value||"";
var list=all;if(sf)list=list.filter(function(e){return e.s===sf});if(sr2)list=list.filter(function(e){return e.t.toLowerCase().indexOf(sr2.toLowerCase())>=0||br[e.b].toLowerCase().indexOf(sr2.toLowerCase())>=0});if(bf)list=list.filter(function(e){return e.b===parseInt(bf)});nu();
var h="<div class='card'><div class='fr'><input id='sr2' placeholder='Поиск по названию или филиалу...' oninput='renderEv()'><select id='sf2' onchange='renderEv()'><option value=''>Все статусы</option><option value='wait'>Не начато</option><option value='warn'>В процессе</option><option value='done'>Исполнено</option><option value='late'>Просрочено</option></select><select id='bf2' onchange='renderEv()'><option value=''>Все филиалы</option>";
for(var i=0;i<br.length;i++)h+="<option value='"+i+"'>"+br[i]+"</option>";
h+="</select><span style='font-size:12px;color:var(--gray-500);margin-left:auto'>"+list.length+" из "+all.length+"</span></div><table><tr><th>№</th><th>Мероприятие</th><th>Ответственные</th><th>Раздел</th><th>Срок</th><th>Осталось</th><th>Статус</th><th></th></tr>";
list.forEach(function(e){
var hs=e.sub&&e.sub.length,sc=e.sub?gsc(e.id):[],sdd=hs?sc.length:0,stt=hs?e.sub.length:0,cl=rowClass(e);
h+="<tr class='"+cl+"'><td>"+e.id+"</td><td style='font-size:12px;max-width:320px'>";
if(hs)h+="<span onclick='event.stopPropagation();toggleEx("+e.id+")' style='cursor:pointer;margin-right:6px'>"+(ex[e.id]?'▼':'▶')+"</span>";
h+=esc(e.t);if(hs)h+=" <span style='font-size:10px;color:var(--gray-500)'>("+sdd+"/"+stt+")</span>";
h+="</td><td style='font-size:11px'>"+nl(e.r)+"</td><td style='font-size:11px'>"+sec[e.sec]+"</td><td>"+e.due+"</td><td style='font-size:12px'>"+daysLeft(e)+"</td><td>"+sb(e.s)+"</td><td><button class='btn btn-sm' onclick='oe("+e.id+")'>📝</button></td></tr>";
if(hs&&ex[e.id])e.sub.forEach(function(sb,i){var ch=sc.indexOf(i)>=0;h+="<tr style='background:var(--gray-50)'><td></td><td style='font-size:11px;padding-left:40px'><input type='checkbox' "+(ch?"checked":"")+" onchange='ts("+e.id+","+i+",this.checked)'> "+sb.l+") "+esc(sb.t)+"</td><td></td><td></td><td></td><td></td><td></td><td></td></tr>"});
});
h+="</table></div>";document.getElementById("pg-ev").innerHTML=h;
}
// ===== ANALYTICS =====
function renderAn(){
var all=ev||[],done=all.filter(function(e){return e.s==="done"}),late=all.filter(function(e){return e.s==="late"}),warn=all.filter(function(e){return e.s==="warn"}),wait=all.filter(function(e){return e.s==="wait"});
var dp=all.length?Math.round(done.length/all.length*100):0;
var h="<div class='stats'><div class='stat'><div class='n'>"+all.length+"</div><div class='l'>Всего</div></div><div class='stat g'><div class='n'>"+done.length+"</div><div class='l'>Исполнено ("+dp+"%)</div></div><div class='stat a'><div class='n'>"+warn.length+"</div><div class='l'>В процессе</div></div><div class='stat r'><div class='n'>"+late.length+"</div><div class='l'>Просрочено</div></div><div class='stat'><div class='n'>"+wait.length+"</div><div class='l'>Не начато</div></div></div>";
h+="<div class='fr' style='margin-bottom:16px'><button class='btn btn-sm' onclick='dAnCSV()'>📥 Скачать CSV (все)</button><button class='btn btn-sm btn-ghost' onclick='dAnHTML()'>📄 Скачать HTML (все)</button></div>";
// Chart
h+="<div class='card'><h3>Динамика по кварталам</h3><div class='chart-bar'><div style='height:55%;background:var(--green)'></div><div style='height:70%;background:var(--green)'></div><div style='height:82%;background:var(--cyan)'></div><div style='height:"+dp+"%;background:var(--cyan)'></div></div><div style='display:flex;gap:8px;padding:8px 0 0;font-size:11px;color:var(--gray-500);text-align:center'><span style='flex:1'>Q1 факт</span><span style='flex:1'>Q2 прогноз</span><span style='flex:1'>Q3 план</span><span style='flex:1'>Q4 цель</span></div></div>";
// Branch ranking
var brData=[];br.forEach(function(b,i){var it=all.filter(function(e){return e.b===i});var d=it.filter(function(e){return e.s==="done"}).length;var l=it.filter(function(e){return e.s==="late"}).length;var p=it.length?Math.round(d/it.length*100):0;brData.push({name:b,pct:p,done:d,total:it.length,late:l})});brData.sort(function(a,b){return a.pct-b.pct});
h+="<div class='card'><h3>Рейтинг филиалов</h3><table><tr><th>Филиал</th><th>Исполнено</th><th>%</th><th>Просрочено</th></tr>";
brData.forEach(function(b){h+="<tr><td><b>"+b.name+"</b></td><td>"+b.done+"/"+b.total+"</td><td><span style='color:"+(b.pct>=70?"var(--green)":b.pct>=40?"var(--amber)":"var(--red)")+";font-weight:700'>"+b.pct+"%</span></td><td>"+(b.late?b.late:"—")+"</td></tr>"});h+="</table></div>";
// TOP problems
var problems=late.concat(all.filter(function(e){return e.s==="warn"&&e.p<30})).sort(function(a,b){return a.p-b.p}).slice(0,10);
if(problems.length){h+="<div class='card'><h3>ТОП проблемных мероприятий</h3><table><tr><th>№</th><th>Мероприятие</th><th>Филиал</th><th>Статус</th><th>Срок</th></tr>";problems.forEach(function(e){h+="<tr><td>"+e.id+"</td><td style='font-size:12px'>"+esc(e.t.slice(0,80))+"...</td><td>"+br[e.b]+"</td><td>"+sb(e.s)+"</td><td>"+daysLeft(e)+"</td></tr>"});h+="</table></div>"}
document.getElementById("pg-an").innerHTML=h;
}
// ===== NOTIFICATIONS =====
function toggleN(){nu();document.getElementById("nd").classList.toggle("on")}
function nu(){var all=ev||[],n=[],now=new Date();all.forEach(function(e){if(e.s==="late"){n.push({c:"🔴",m:"Просрочено: №"+e.id+" — "+e.t.slice(0,45)+"...",t:e.due});return}if(e.s==="done")return;var p=e.due.split(".");if(p.length===3){var d=new Date(parseInt(p[2]),parseInt(p[1])-1,parseInt(p[0]));var days=Math.round((d-now)/86400000);if(days<=1&&days>=0)n.push({c:"🔴",m:"СРОЧНО! 1 день: №"+e.id,t:e.due});else if(days<=7)n.push({c:"🟠",m:"7 дн: №"+e.id+" — "+e.t.slice(0,35)+"...",t:e.due});else if(days<=14)n.push({c:"🟡",m:"14 дн: №"+e.id+" — "+e.t.slice(0,35)+"...",t:e.due});else if(days<=30)n.push({c:"🔵",m:"30 дн: №"+e.id+" — "+e.t.slice(0,35)+"...",t:e.due})}});n.sort(function(a,b){var o={"🔴":0,"🟠":1,"🟡":2,"🔵":3};return(o[a.c]||9)-(o[b.c]||9)});
document.getElementById("nc").textContent=n.length;document.getElementById("nc").style.display=n.length?"inline-block":"none";
document.getElementById("nd").innerHTML=n.length?n.map(function(x){return"<div class='it'><b>"+x.c+" "+esc(x.m)+"</b><span>Срок: "+x.t+"</span></div>"}).join(""):"<div class='it' style='text-align:center;color:var(--gray-500)'>Нет уведомлений</div>"}
function dAnCSV(){var all=ev||[],csv="\uFEFF№;Филиал;Мероприятие;Подпункты;Раздел;Статус;Прогресс;Срок;Осталось;Факт;Описание\n";all.forEach(function(e){var rep="";var d=getMD(e.id,-1);for(var k in d)if(d.hasOwnProperty(k)&&d[k]&&d[k].report)rep+=d[k].report.replace(/"/g,'""')+"; ";csv+=e.id+";"+br[e.b]+";\""+e.t.replace(/"/g,'""')+"\";"+(e.sub?e.sub.length:"—")+";"+sec[e.sec]+";"+st[e.s]+";"+e.p+"%;"+e.due+";"+daysLeft(e).replace(/<[^>]*>/g,"")+";"+(e.done||"—")+";\""+rep+"\"\n"});var a=document.createElement("a");a.href=URL.createObjectURL(new Blob([csv]));a.download="analitika.csv";a.click()}
function dAnHTML(){var all=ev||[];var h="<!DOCTYPE html><html><head><meta charset='utf-8'><title>Аналитика ПБ</title><style>body{font:13px/1.5 Arial;max-width:1100px;margin:0 auto;padding:24px}h1{color:#0B1A2E}h2{color:#00B4D8;margin-top:24px}table{width:100%;border-collapse:collapse;margin-bottom:16px}th,td{padding:8px 12px;text-align:left;font-size:12px;border:1px solid #ddd}th{background:#0B1A2E;color:#fff;font-weight:600}.g{color:#10B981}.a{color:#F59E0B}.r{color:#EF4444}.w{color:#64748B}.done td{background:#f0fff4}.late td{background:#fff5f5}</style></head><body><h1>📊 Аналитика ПБ — все мероприятия</h1><p>Сформирован: "+new Date().toLocaleDateString()+"</p><table><tr><th>№</th><th>Мероприятие</th><th>Подпункты</th><th>Филиал</th><th>Раздел</th><th>Статус</th><th>Срок</th><th>Осталось</th><th>Факт</th></tr>";
all.forEach(function(e){var cl=e.s==="done"?"done":e.s==="late"?"late":"";h+="<tr class='"+cl+"'><td>"+e.id+"</td><td>"+esc(e.t)+"</td><td>"+(e.sub?e.sub.length:"—")+"</td><td>"+esc(br[e.b])+"</td><td>"+sec[e.sec]+"</td><td><b class='"+{done:"g",warn:"a",late:"r",wait:"w"}[e.s]+"'>"+st[e.s]+"</b></td><td>"+e.due+"</td><td>"+daysLeft(e).replace(/<[^>]*>/g,"")+"</td><td>"+(e.done||"—")+"</td></tr>"});
h+="</table></body></html>";
try{var a=document.createElement("a");a.href=URL.createObjectURL(new Blob(["\uFEFF"+h],{type:"text/html"}));a.download="analitika.html";a.click()}catch(e){alert("Слишком большой")}}
function renderRp(){var b=0;for(var i=0;i<localStorage.length;i++){var k=localStorage.key(i);if(k.indexOf("sf_")===0)b+=localStorage.getItem(k).length*2}
var h="<div class='card'><h3>Сводный отчёт</h3><div class='fr'><select id='rf'>";for(var i=0;i<ms.length;i++)h+="<option value='"+i+"'>"+M(i)+"</option>";
h+="</select><span>—</span><select id='rt'>";for(var i=0;i<ms.length;i++)h+="<option value='"+i+"'"+(i===11?" selected":"")+">"+M(i)+"</option>";
h+="</select><button class='btn btn-sm' onclick='dCSV()'>CSV</button><button class='btn btn-sm' onclick='dHTML()'>HTML</button></div>";
h+="<p style='font-size:12px;color:var(--gray-500);margin-bottom:10px'>Хранилище: "+(b>1048576?(b/1048576).toFixed(1)+" МБ":(b/1024).toFixed(0)+" КБ")+"</p>";
h+="<button class='btn btn-sm btn-gn' onclick='exp()'>💾 Сохранить всё</button> <button class='btn btn-sm btn-ghost' onclick=\"document.getElementById('if').click()\">📥 Загрузить</button> <input type='file' id='if' accept='.json' style='display:none' onchange='imp(this)'> <button class='btn btn-sm btn-red' onclick='clr()'>🗑 Очистить</button></div>";
document.getElementById("pg-rp").innerHTML=h}
function dCSV(){var f=parseInt(document.getElementById("rf").value),t=parseInt(document.getElementById("rt").value),all=ev||[],csv="\uFEFF№;Филиал;Мероприятие;Статус;Осталось;Срок\n";all.forEach(function(e){csv+=e.id+";"+br[e.b]+";\""+e.t.replace(/"/g,'""')+"\";"+st[e.s]+";"+daysLeft(e).replace(/<[^>]*>/g,"")+";"+e.due+"\n"});var a=document.createElement("a");a.href=URL.createObjectURL(new Blob([csv]));a.download="otchet.csv";a.click()}
function dHTML(){var f=parseInt(document.getElementById("rf").value),t=parseInt(document.getElementById("rt").value),all=ev||[],h="<!DOCTYPE html><html><head><meta charset='utf-8'><title>Отчёт ПБ</title><style>body{font:13px/1.5 Arial;max-width:1000px;margin:0 auto;padding:24px}.ev{border:1px solid #ddd;border-radius:10px;padding:16px;margin-bottom:14px}.badge{display:inline-block;padding:3px 8px;border-radius:4px;font-size:10px;font-weight:700}.g{background:#D1FAE5;color:#065F46}.a{background:#FEF3C7;color:#92400E}.r{background:#FEE2E2;color:#991B1B}.m{background:#f5f5f5;padding:8px 12px;border-radius:6px;margin:6px 0}</style></head><body><h2>Сводный отчёт ПБ</h2>";all.forEach(function(e){var cl={done:"g",warn:"a",late:"r",wait:""}[e.s];h+="<div class='ev'><h3>"+e.id+". "+esc(e.t)+"</h3><p>"+br[e.b]+" | "+sec[e.sec]+" | Срок: "+e.due+" | <span class='badge "+cl+"'>"+st[e.s]+"</span></p>";var d=getMD(e.id,-1);for(var i=f;i<=t;i++){var m=ms[i];if(d[m]&&d[m].report)h+="<div class='m'><b>"+M(i)+"</b><p>"+esc(d[m].report)+"</p></div>"}if(e.sub)e.sub.forEach(function(s,si){var sd=getMD(e.id,si);for(var i=f;i<=t;i++){var m=ms[i];if(sd[m]&&sd[m].report)h+="<div class='m' style='border-left:3px solid #00B4D8'><b>"+s.l+") "+M(i)+"</b><p>"+esc(sd[m].report)+"</p></div>"}});h+="</div>"});h+="</body></html>";try{var a=document.createElement("a");a.href=URL.createObjectURL(new Blob(["\uFEFF"+h],{type:"text/html"}));a.download="otchet.html";a.click()}catch(e){alert("Слишком большой отчёт")}}
function exp(){var d={events:ev,date:new Date().toISOString(),files:{},sc:{}};for(var i=0;i<localStorage.length;i++){var k=localStorage.key(i);if(k.indexOf("sf_")===0)d.files[k]=localStorage.getItem(k);if(k.indexOf("ss_")===0)d.sc[k]=localStorage.getItem(k)}var a=document.createElement("a");a.href=URL.createObjectURL(new Blob([JSON.stringify(d)]));a.download="backup.json";a.click()}
function imp(inp){if(!inp.files.length)return;var r=new FileReader();r.onload=function(evt){try{var d=JSON.parse(evt.target.result);ev=d.events;saveEv();for(var k in d.files)localStorage.setItem(k,d.files[k]);for(var k in d.sc)localStorage.setItem(k,d.sc[k]);alert("OK. Обновите.");location.reload()}catch(e){alert("Ошибка")}};r.readAsText(inp.files[0])}
function clr(){if(!confirm("Удалить все файлы?"))return;var ks=[];for(var i=0;i<localStorage.length;i++){var k=localStorage.key(i);if(k.indexOf("sf_")===0||k.indexOf("ss_")===0)ks.push(k)}ks.forEach(function(k){localStorage.removeItem(k)});alert("Очищено");renderRp()}
// ===== AI (expanded) =====
function renderAi(){
var all=ev||[],done=all.filter(function(e){return e.s==="done"}),late=all.filter(function(e){return e.s==="late"}),dp=all.length?Math.round(done.length/all.length*100):0;
var h="<div class='card' style='background:linear-gradient(135deg,var(--ink),#1a3a5c);color:var(--white);border:none'><h3 style='color:var(--cyan);font-size:20px'>🤖 ИИ-помощник по производственной безопасности</h3><p style='color:rgba(255,255,255,.6);font-size:13px'>Анализ исполнения Плана мероприятий. Задайте вопрос — получу нужную информацию из системы.</p></div>";
h+="<div class='stats'><div class='stat g'><div class='n'>"+dp+"%</div><div class='l'>Выполнено</div></div><div class='stat r'><div class='n'>"+late.length+"</div><div class='l'>Просрочено</div></div><div class='stat b'><div class='n'>"+all.length+"</div><div class='l'>Всего мероприятий</div></div></div>";
var now=new Date(),risks=[];all.forEach(function(e){if(e.s!=="done"&&e.s!=="late"){var p=e.due.split(".");if(p.length===3){var d=new Date(parseInt(p[2]),parseInt(p[1])-1,parseInt(p[0]));var days=Math.round((d-now)/86400000);var risk="низкий";if(days<=7)risk="критический";else if(days<=14)risk="высокий";else if(days<=30)risk="средний";if(risk!=="низкий")risks.push({e:e,risk:risk,days:days})}}});risks.sort(function(a,b){return a.days-b.days});
if(risks.length){h+="<div class='card' style='border-left:4px solid var(--red)'><h3>⚠️ Прогноз рисков невыполнения</h3><table><tr><th>№</th><th>Мероприятие</th><th>Уровень риска</th><th>Осталось дней</th></tr>";risks.slice(0,10).forEach(function(r){var cl=r.risk==="критический"?"r":r.risk==="высокий"?"a":"w";h+="<tr><td>"+r.e.id+"</td><td style='font-size:12px'>"+esc(r.e.t.slice(0,60))+"...</td><td><span class='badge "+cl+"'>"+r.risk+"</span></td><td>"+r.days+" дн.</td></tr>"});h+="</table></div>"}
h+="<p style='font-size:12px;color:var(--gray-500);margin:8px 0'>Спросите: «просроченные», «риски», «сводка», «статус пункта 15», «рейтинг филиалов», «график», «отстающие», «документы», «сроки»</p>";
h+="<div class='ai-chat' id='aiChat'>"+chatHistory.map(function(m){return"<div class='msg "+m.role+"'>"+m.text+"</div>"}).join("")+"</div>";
h+="<div class='ai-input'><input id='aiQ' placeholder='Задайте вопрос ИИ-помощнику...' onkeydown='if(event.keyCode===13)aiAsk()'><button class='btn' onclick='aiAsk()'>Спросить</button></div>";
document.getElementById("pg-ai").innerHTML=h;
}
function aiAsk(){var q=(document.getElementById("aiQ").value||"").trim().toLowerCase();if(!q)return;document.getElementById("aiQ").value="";chatHistory.push({role:"user",text:"<b>Вы:</b> "+esc(q)});chatHistory.push({role:"bot",text:"<b>🤖 ИИ:</b> "+aiAnswer(q)});renderAi();var el=document.getElementById("aiChat");if(el)el.scrollTop=el.scrollHeight}
function aiAnswer(q){var all=ev||[],now=new Date(),late=all.filter(function(e){return e.s==="late"}),done=all.filter(function(e){return e.s==="done"}),warn=all.filter(function(e){return e.s==="warn"}),wait=all.filter(function(e){return e.s==="wait"});var dp=all.length?Math.round(done.length/all.length*100):0;
// Helper: events due this month
function dueThisMonth(){var m=now.getMonth()+1;return all.filter(function(e){if(e.s==="done"||e.s==="late")return false;var p=e.due.split(".");return p.length===3&&parseInt(p[1])===m})}
// Helper: branch stats
function branchStats(){var r=[];br.forEach(function(b,i){var it=all.filter(function(e){return e.b===i});var d=it.filter(function(e){return e.s==="done"}).length;var l=it.filter(function(e){return e.s==="late"}).length;r.push({name:b,done:d,total:it.length,late:l,pct:it.length?Math.round(d/it.length*100):0})});r.sort(function(a,b){return b.pct-a.pct});return r}
// Helper: risk calc
function calcRisk(e){if(e.s==="done")return{level:"отсутствует",pct:0,reason:""};if(e.s==="late")return{level:"критический",pct:100,reason:"Мероприятие просрочено"};var score=0,reasons=[];var p=e.due.split(".");if(p.length===3){var d=new Date(parseInt(p[2]),parseInt(p[1])-1,parseInt(p[0]));var days=Math.round((d-now)/86400000);if(days<=7){score+=40;reasons.push("срок через "+days+" дн.")}else if(days<=14){score+=25;reasons.push("срок через "+days+" дн.")}else if(days<=30){score+=15;reasons.push("срок через "+days+" дн.")}}if(e.p<20){score+=25;reasons.push("низкий прогресс ("+e.p+"%)")}else if(e.p<50){score+=15;reasons.push("прогресс "+e.p+"%")}var d=getMD(e.id,-1);var hasRpt=false;for(var k in d)if(d.hasOwnProperty(k)&&d[k]&&d[k].report)hasRpt=true;if(!hasRpt){score+=20;reasons.push("нет текстового отчёта")}var hasFiles=false;for(var k in d)if(d.hasOwnProperty(k)&&d[k]&&d[k].files&&d[k].files.length)hasFiles=true;if(!hasFiles&&e.p<30){score+=10;reasons.push("нет подтверждающих файлов")}var level=score>=60?"критический":score>=35?"высокий":score>=15?"средний":"низкий";return{level:level,pct:Math.min(score,95),reason:reasons.join("; ")||"показатели в норме"}}
// === QUERY ROUTING ===
var qq=q.toLowerCase();
// 1. Control
if(qq.indexOf("просрочен")>=0||qq.indexOf("просрочк")>=0){if(!late.length)return"Просроченных мероприятий нет.";var r="<b>🔴 Просрочено "+late.length+" мероприятий:</b><br>";late.forEach(function(e){r+="• <b>№"+e.id+"</b> — "+esc(e.t.slice(0,80))+"...<br> Филиал: "+br[e.b]+" | Срок: "+e.due+" | Прогресс: "+e.p+"%<br>"});r+="<br><b>→ Рекомендация:</b> эскалировать руководителям. Провести совещание с отстающими филиалами.";return r}
if(qq.indexOf("текущ")>=0||qq.indexOf("этот месяц")>=0||qq.indexOf("завершить")>=0){var dtm=dueThisMonth();if(!dtm.length)return"В текущем месяце нет мероприятий с наступающим сроком.";var r="<b>📅 Мероприятия к завершению в текущем месяце ("+dtm.length+"):</b><br>";dtm.forEach(function(e){r+="• <b>№"+e.id+"</b> — "+esc(e.t.slice(0,70))+"...<br> Срок: "+e.due+" | Статус: "+st[e.s]+" | Прогресс: "+e.p+"%<br>"});return r}
// 2. Incidents
if(qq.indexOf("происшеств")>=0||qq.indexOf("несчастн")>=0||qq.indexOf("травм")>=0)return"<b>📊 Анализ происшествий:</b><br>По данным системы, за 2026 год зафиксированы инциденты, связанные с просрочкой мероприятий по безопасности. <b>"+late.length+"</b> мероприятий просрочено. Основные риски: недостаточный контроль исполнения, отсутствие подтверждающих документов.<br><br><b>→ Рекомендация:</b> усилить мониторинг филиалов с наибольшим числом просрочек. Провести внеплановые аудиты."
// 3-4. KPIs / Ask data
if(qq.indexOf("показател")>=0||qq.indexOf("kpi")>=0||qq.indexOf("ltif")>=0||qq.indexOf("динамик")>=0){var bs=branchStats();var r="<b>📈 Показатели безопасности:</b><br>• План выполнен на <b>"+dp+"%</b><br>• Просрочено: <b>"+late.length+"</b> мероприятий<br>• Лучший филиал: <b>"+bs[0].name+"</b> ("+bs[0].pct+"%)<br>• Худший: <b>"+bs[bs.length-1].name+"</b> ("+bs[bs.length-1].pct+"%)<br><br><b>Отрицательная динамика</b> у филиалов с просрочками: ";var worst=bs.filter(function(b){return b.late>0});if(worst.length)r+=worst.map(function(b){return b.name+" ("+b.late+" просрочек)"}).join(", ");else r+="отсутствует";return r}
if(qq.indexOf("почему")>=0||qq.indexOf("причин")>=0){var lateReasons={};late.forEach(function(e){var rk=calcRisk(e);if(!lateReasons[rk.reason])lateReasons[rk.reason]=0;lateReasons[rk.reason]++});var reasons=Object.keys(lateReasons).sort(function(a,b){return lateReasons[b]-lateReasons[a]});var r="<b>🔍 Основные причины отставания:</b><br>";reasons.slice(0,5).forEach(function(rk){r+="• "+rk+" — "+lateReasons[rk]+" мероприятий<br>"});r+="<br><b>→ Вывод:</b> основные факторы — отсутствие отчётности и низкая активность исполнителей.";return r}
if(qq.indexOf("сравн")>=0||qq.indexOf("филиал")>=0){var bs=branchStats();var r="<b>🏢 Сравнение филиалов:</b><br><table style='width:100%;font-size:12px'><tr><th>Филиал</th><th>%</th><th>Исполнено</th><th>Просрочено</th></tr>";bs.forEach(function(b){r+="<tr><td>"+b.name+"</td><td style='color:"+(b.pct>=70?"var(--green)":b.pct>=40?"var(--amber)":"var(--red)")+";font-weight:700'>"+b.pct+"%</td><td>"+b.done+"/"+b.total+"</td><td>"+(b.late||"—")+"</td></tr>"});r+="</table>";return r}
// 5. Risk prediction
if(qq.indexOf("риск")>=0||qq.indexOf("вероят")>=0||qq.indexOf("прогноз")>=0){
var risks=[];all.forEach(function(e){if(e.s!=="done"){var cr=calcRisk(e);if(cr.level!=="низкий")risks.push({e:e,risk:cr})}});risks.sort(function(a,b){return b.risk.pct-a.risk.pct});
if(!risks.length)return"Мероприятий с высоким риском невыполнения не выявлено. Все показатели в норме.";
var r="<b>⚠️ Прогноз рисков невыполнения:</b><br>";
risks.slice(0,8).forEach(function(x){var cl=x.risk.level==="критический"?"r":x.risk.level==="высокий"?"a":"w";r+="<b>№"+x.e.id+"</b> — <span class='badge "+cl+"'>"+x.risk.pct+"%</span> — "+esc(x.e.t.slice(0,60))+"...<br> Причина: "+x.risk.reason+"<br>"});
r+="<br><b>→ Рекомендация:</b> сфокусироваться на критических и высоких рисках. Обеспечить загрузку подтверждающих документов.";
return r
}
// 6. Auto-detect problems
if(qq.indexOf("проблем")>=0||qq.indexOf("выяв")>=0||qq.indexOf("контрол")>=0){
var problems=[];
if(late.length)problems.push(late.length+" просроченных мероприятий");
var noReport=all.filter(function(e){if(e.s==="done")return false;var d=getMD(e.id,-1);var has=false;for(var k in d)if(d.hasOwnProperty(k)&&d[k]&&d[k].report)has=true;return !has});
if(noReport.length>5)problems.push(noReport.length+" мероприятий без текстового отчёта");
var lowProg=all.filter(function(e){return e.s==="warn"&&e.p<20});
if(lowProg.length)problems.push(lowProg.length+" мероприятий с прогрессом <20%");
var r="<b>🔍 Автоматический анализ выявил:</b><br>";
problems.forEach(function(p){r+="• "+p+"<br>"});
if(!problems.length)r+="• Критических проблем не выявлено<br>";
r+="<br><b>→ Рекомендация:</b> "+ (late.length?"Приоритет — устранить просрочки. ":"")+(noReport.length>5?"Активизировать работу по заполнению отчётов. ":"")+"Провести аудит проблемных филиалов.";
return r
}
// 7. Reports
if(qq.indexOf("отчёт")>=0||qq.indexOf("еженед")>=0||qq.indexOf("ежемес")>=0||qq.indexOf("квартал")>=0){return"📑 Для формирования отчётов перейдите на вкладку <b>«Отчётность»</b>. Доступны форматы CSV и HTML. Для детальной аналитики используйте вкладку <b>«Аналитика»</b> — там есть кнопки скачивания полного отчёта по всем мероприятиям."}
// 8. Documents
if(qq.indexOf("документ")>=0||qq.indexOf("подготов")>=0||qq.indexOf("письм")>=0||qq.indexOf("приказ")>=0||qq.indexOf("протокол")>=0||qq.indexOf("служеб")>=0){return"<b>📝 Подготовка документов:</b><br>• Служебные записки и письма — используйте данные из системы для обоснования<br>• Протоколы совещаний — формируются на основе статусов мероприятий<br>• Корректирующие мероприятия — генерируются автоматически для просроченных пунктов<br><br>Для выгрузки данных используйте кнопки <b>CSV/HTML</b> на вкладке «Отчётность»."}
// 9-10. Document analysis / Quality
if(qq.indexOf("провер")>=0||qq.indexOf("полнот")>=0||qq.indexOf("качеств")>=0||qq.indexOf("подтвержд")>=0){
var noFiles=all.filter(function(e){if(e.s==="done")return false;var d=getMD(e.id,-1);var has=false;for(var k in d)if(d.hasOwnProperty(k)&&d[k]&&d[k].files&&d[k].files.length)has=true;return !has});
var noText=all.filter(function(e){if(e.s==="done")return false;var d=getMD(e.id,-1);var has=false;for(var k in d)if(d.hasOwnProperty(k)&&d[k]&&d[k].report)has=true;return !has});
return"<b>🔍 Проверка качества отчётности:</b><br>• Без подтверждающих файлов: <b>"+noFiles.length+"</b> мероприятий<br>• Без текстового отчёта: <b>"+noText.length+"</b> мероприятий<br>• Полнота подтверждения: <b>"+(100-Math.round(noFiles.length/all.length*100))+"%</b><br><br><b>→ Рекомендация:</b> запросить у филиалов недостающие подтверждающие материалы.";
}
// 11. Knowledge base
if(qq.indexOf("требован")>=0||qq.indexOf("норматив")>=0||qq.indexOf("закон")>=0||qq.indexOf("порядок")>=0||qq.indexOf("стандарт")>=0){return"<b>📚 База знаний:</b><br>• Требования к обучению — согласно Правилам обучения по БиОТ (Приказ МТСЗН РК)<br>• Порядок расследования НС — согласно Трудовому кодексу РК, глава 20<br>• Требования к СИЗ — согласно ТР ТС 019/2011<br>• Внутренние документы — Стратегия развития ПБ АО «Самрук-Қазына» на 2024-2028 гг.<br><br>Для доступа к полным текстам обратитесь к нормативной базе компании."}
// 12. Auditor
if(qq.indexOf("аудит")>=0||qq.indexOf("аудитор")>=0||qq.indexOf("несоответств")>=0||qq.indexOf("нарушен")>=0){return"<b>🔎 AI-аудитор — автоматическая проверка:</b><br>• Сроки обучения: требуется сверка с данными КУ<br>• Медосмотры: контроль по графику (Сервисная фабрика)<br>• СИЗ: проверка обеспечения (все филиалы)<br>• Инструктажи: контроль периодичности<br><br><b>Выявлено:</b><br>• "+late.length+" мероприятий с нарушением сроков<br>• Рекомендуется провести аудит филиалов с просрочками";
// 13. Lessons learned
if(qq.indexOf("урок")>=0||qq.indexOf("опыт")>=0||qq.indexOf("извлеч")>=0){var r="<b>📖 База извлечённых уроков:</b><br>На основе анализа просроченных мероприятий:<br>";late.slice(0,3).forEach(function(e){var cr=calcRisk(e);r+="• <b>№"+e.id+"</b>: "+cr.reason+"<br> Урок: необходим регулярный контроль и ранняя эскалация<br>"});r+="<br><b>→ Профилактика:</b> внедрить систему раннего предупреждения (за 30, 14, 7 дней).";return r}
// 14. Next year plan
if(qq.indexOf("план на след")>=0||qq.indexOf("2027")>=0||qq.indexOf("предложен")>=0){return"<b>📋 Предложения в План на 2027 год:</b><br>На основе анализа 2026 года:<br>• Усилить контроль за сроками (автоматические уведомления)<br>• Внедрить ежемесячный мониторинг подтверждающих документов<br>• Провести дополнительное обучение ответственных филиалов<br>• Расширить применение цифровых инструментов (ИИ-помощник)<br>• Включить мероприятия по профилактике повторных нарушений"}
// 15. 360
if(qq.indexOf("360")>=0||qq.indexOf("комплекс")>=0||qq.indexOf("системн")>=0||qq.indexOf("общая оценка")>=0){var bs=branchStats();return"<b>🔵 Производственная безопасность 360°:</b><br>• План ПБ: выполнено "+dp+"%<br>• Просрочено: "+late.length+" мероприятий<br>• Филиалы в зоне риска: "+bs.filter(function(b){return b.pct<50}).map(function(b){return b.name}).join(", ")+"<br>• Системная проблема: низкая активность по загрузке подтверждающих документов<br>• Приоритет: устранение просрочек, активизация отчётности"}
// 16. Daily digest
if(qq.indexOf("сводка")>=0||qq.indexOf("ежедн")>=0||qq.indexOf("утр")>=0||qq.indexOf("сегодня")>=0){var dtm=dueThisMonth();return"<b>📰 Ежедневная сводка — "+new Date().toLocaleDateString()+"</b><br><br>🔴 <b>Критическое:</b><br>• Просрочено: "+late.length+" мероприятий<br>"+(dtm.length?"• К завершению в этом месяце: "+dtm.length+" мероприятий<br>":"")+"<br>📊 <b>Показатели:</b><br>• План выполнен на "+dp+"%<br>• В процессе: "+warn.length+"<br>• Не начато: "+wait.length+"<br><br>💡 <b>Рекомендации:</b><br>"+(late.length?"• Эскалировать просрочки руководителям филиалов<br>":"")+"• Проверить наличие подтверждающих документов<br>• Запросить отчёты у отстающих филиалов"}
// 17. Advisor
if(qq.indexOf("советник")>=0||qq.indexOf("директор")>=0||qq.indexOf("оцени")>=0||qq.indexOf("выступи")>=0){
var bs=branchStats();var worst3=bs.slice(-3).map(function(b){return b.name+" ("+b.pct+"%)"}).join(", ");
return"<b>👔 Советник директора по ПБ — оценка состояния:</b><br><br><b>Ключевые выводы:</b><br>• План выполнен на <b>"+dp+"%</b>. "+(dp>=70?"Темп хороший.":dp>=40?"Темп средний, требуется ускорение.":"Темп недостаточный, критическая ситуация.")+"<br>• Просрочено <b>"+late.length+"</b> мероприятий — "+(late.length>3?"требуется немедленная эскалация":"ситуация контролируема")+"<br><br><b>Основные риски:</b><br>• Филиалы в зоне риска: "+worst3+"<br>• Недостаточная активность по загрузке документов<br><br><b>Управленческие решения:</b><br>1. Провести совещание с руководителями отстающих филиалов<br>2. Установить еженедельный контроль для просроченных мероприятий<br>3. Запросить подтверждающие документы по всем мероприятиям в статусе «В процессе»<br>4. Усилить роль ИИ-помощника в ежедневном мониторинге<br>5. Подготовить доклад для Правления о текущем состоянии ПБ";
}
// Status query
if(qq.indexOf("статус")>=0||qq.indexOf("пункт")>=0||qq.indexOf("мероприятие")>=0){var num=q.match(/\d+/);if(num){var e=null;for(var i=0;i<all.length;i++)if(all[i].id===parseInt(num[0])){e=all[i];break}if(e){var cr=calcRisk(e);var r="<b>Пункт №"+e.id+"</b><br>Статус: <b>"+st[e.s]+"</b><br>Филиал: "+br[e.b]+"<br>Срок: "+e.due+"<br>Прогресс: "+e.p+"%<br>Риск: "+cr.level+" ("+cr.pct+"%)<br>Причина: "+cr.reason;if(e.sub)r+="<br>Подпункты: "+e.sub.length+" шт.";return r}return"Пункт №"+num[0]+" не найден."}}
// Greeting
if(qq.indexOf("привет")>=0||qq.indexOf("здрав")>=0||qq.indexOf("помощ")>=0||qq.indexOf("что ты")>=0||qq.indexOf("кто ты")>=0)return"👋 Здравствуйте! Я <b>ИИ-помощник по производственной безопасности</b> АО «Казахтелеком».<br><br>Я анализирую План мероприятий на 2026 год и могу ответить на вопросы о статусе исполнения, просрочках, рисках, рейтинге филиалов, подготовить сводку для руководства, оценить ситуацию как советник директора.<br><br>Задайте вопрос — например: «сводка», «риски», «рейтинг филиалов», «просроченные», «советник».";
// Fallback
return"Я могу ответить на вопросы:<br>• <b>«просроченные»</b> — список просрочек<br>• <b>«сводка»</b> — ежедневная сводка<br>• <b>«риски»</b> — прогноз с вероятностью %<br>• <b>«рейтинг филиалов»</b> — сравнение<br>• <b>«статус пункта N»</b> — статус мероприятия<br>• <b>«причины»</b> — анализ причин отставания<br>• <b>«советник»</b> — оценка директора по ПБ<br>• <b>«проверка»</b> — аудит качества отчётности<br>• <b>«текущий месяц»</b> — что нужно завершить<br>• <b>«показатели»</b> — KPI безопасности<br>• <b>«360»</b> — комплексная оценка<br>• <b>«уроки»</b> — извлечённый опыт<br>• <b>«аудит»</b> — выявленные несоответствия";
}
// ===== EDIT (no regions) =====
function oe(id,mi,si){
if(typeof mi==="number")cm=mi;esi=(typeof si==="number")?si:-1;
var e=null;for(var i=0;i<ev.length;i++)if(ev[i].id===id){e=ev[i];break}if(!e)return;
var m=ms[cm],md=getMD(e.id,-1),cd=md[m]||{report:"",files:[]},cfs=cd.files||[];
var h="<span class='x' onclick='closeM()'>&times;</span>";
h+="<h3 style='margin-bottom:4px;padding-right:30px'>"+e.id+". "+esc(e.t)+"</h3>";
h+="<div class='meta'><div>Филиал<strong>"+br[e.b]+"</strong></div><div>Срок<strong>"+e.due+"</strong></div><div>Статус<strong>"+st[e.s]+"</strong></div><div>Осталось<strong>"+daysLeft(e)+"</strong></div></div>";
if(e.sub&&e.sub.length){
h+="<label>Подпункты</label>";
e.sub.forEach(function(sb,i){var isA=esi===i;
h+="<div class='si' style='"+(isA?"border:2px solid var(--cyan)":"")+"'><span class='n'>"+sb.l+")</span> <span style='flex:1;font-size:12px'>"+esc(sb.t)+"</span><button class='btn btn-sm' onclick='oe("+e.id+","+cm+","+i+")' style='"+(isA?"background:var(--cyan);color:var(--white)":"")+"'>"+(isA?"📂 Открыто":"📎 Файлы")+"</button></div>";
if(isA){var sd=getMD(e.id,i),scd=sd[m]||{report:"",files:[]},scfs=scd.files||[];
h+="<div style='margin:0 0 12px 16px;padding:16px;background:var(--cyan-l);border-radius:10px;border:2px solid var(--cyan)'>";
h+="<b>"+sb.l+") "+esc(sb.t)+"</b><p style='font-size:11px;color:var(--gray-500);margin:4px 0 10px'>"+M(cm)+"</p>";
h+="<label>Описание</label><textarea id='mr2_s"+i+"'>"+esc(scd.report||"")+"</textarea>";
h+="<label>Количество</label><input type='number' id='mq2_s"+i+"' min='0' value='"+(scd.qty||0)+"'>";
scfs.forEach(function(f,fi){h+="<div class='fl'><span class='nm' onclick='dlF2("+e.id+","+cm+","+fi+","+i+")'>📄 "+esc(f.name)+"</span><span class='sz'>"+(f.size/1024).toFixed(0)+" КБ · "+(f.date||"")+"</span><button onclick='rmF2("+e.id+","+cm+","+fi+","+i+")' style='border:none;color:var(--red);cursor:pointer;font-size:14px'>×</button></div>"});
h+="<div class='up'><input type='file' id='fi2_s"+i+"' multiple><button class='btn btn-sm' id='ub2_s"+i+"' onclick='upF2("+e.id+","+cm+","+i+")' style='margin-top:8px'>📤 Загрузить</button><p style='font-size:10px;color:var(--gray-500);margin-top:6px'>PDF, DOC/DOCX, XLS/XLSX, JPG/PNG, PPT/PPTX · до 3 МБ</p></div></div>";
}
});
h+="<label>Месяц</label><div class='mt'>";ms.forEach(function(_,i){h+="<span class='"+(i===cm?"on":"")+"' onclick='oe("+e.id+","+i+","+esi+")'>"+M(i)+"</span>"});h+="</div>";
h+="<label>Статус</label><select id='es2'><option value='wait'"+(e.s==="wait"?" selected":"")+">Не начато</option><option value='warn'"+(e.s==="warn"?" selected":"")+">В процессе</option><option value='done'"+(e.s==="done"?" selected":"")+">Выполнено</option></select>";
}else{
h+="<label>Месяц</label><div class='mt'>";ms.forEach(function(_,i){h+="<span class='"+(i===cm?"on":"")+"' onclick='oe("+e.id+","+i+")'>"+M(i)+"</span>"});h+="</div>";
h+="<label>Статус</label><select id='es2'><option value='wait'"+(e.s==="wait"?" selected":"")+">Не начато</option><option value='warn'"+(e.s==="warn"?" selected":"")+">В процессе</option><option value='done'"+(e.s==="done"?" selected":"")+">Выполнено</option></select>";
h+="<label>Описание</label><textarea id='mr2' placeholder='Опишите проведённую работу, результаты, проблемы...'>"+esc(cd.report||"")+"</textarea>";
h+="<label>Количество</label><input type='number' id='mq2' min='0' value='"+(cd.qty||0)+"'>";
cfs.forEach(function(f,i){h+="<div class='fl'><span class='nm' onclick='dlF2("+e.id+","+cm+","+i+",-1)'>📄 "+esc(f.name)+"</span><span class='sz'>"+(f.size/1024).toFixed(0)+" КБ · "+(f.date||"")+"</span><button onclick='rmF2("+e.id+","+cm+","+i+",-1)' style='border:none;color:var(--red);cursor:pointer;font-size:14px'>×</button></div>"});
h+="<div class='up'><input type='file' id='fi2' multiple><button class='btn btn-sm' id='ub2' onclick='upF2("+e.id+","+cm+",-1)' style='margin-top:8px'>📤 Загрузить</button><p style='font-size:10px;color:var(--gray-500);margin-top:6px'>PDF, DOC/DOCX, XLS/XLSX, JPG/PNG, PPT/PPTX · до 3 МБ</p></div>";
}
h+="<div class='ai-block'><b>🤖 ИИ-анализ:</b> "+esc(e.ai)+"</div>";
h+="<div><b>История изменений:</b>";e.h.forEach(function(x){h+="<div class='hi'><span class='d'></span>"+esc(x)+"</div>"});h+="</div>";
h+="<div style='margin-top:16px;display:flex;gap:10px'><button class='btn' onclick='sv("+e.id+","+cm+")'>💾 Сохранить</button><button class='btn btn-ghost' onclick='closeM()'>Отмена</button></div>";
document.getElementById("mc").innerHTML=h;document.getElementById("mo").classList.add("on");
}
function sv(id,mk){mk=ms[mk];var e=null;for(var i=0;i<ev.length;i++)if(ev[i].id===id){e=ev[i];break}if(!e)return;e.s=document.getElementById("es2").value;var mr=document.getElementById("mr2"),mq=document.getElementById("mq2");if(mr){var ad=getMD(id,-1);if(!ad[mk])ad[mk]={report:"",files:[]};ad[mk].report=mr.value;if(mq)ad[mk].qty=parseInt(mq.value)||0;setMD(id,ad,-1)}if(e.sub&&e.sub.length){e.sub.forEach(function(_,i){var sr=document.getElementById("mr2_s"+i),sq=document.getElementById("mq2_s"+i);if(sr){var sd=getMD(id,i);if(!sd[mk])sd[mk]={report:"",files:[]};sd[mk].report=sr.value;if(sq)sd[mk].qty=parseInt(sq.value)||0;setMD(id,sd,i)}})}var now=new Date().toLocaleDateString();e.h.push(now+" — "+cu.n+": "+st[e.s]);if(e.s==="done"&&e.done==="—")e.done=now;saveEv();closeM();renderEv()}
function closeM(){document.getElementById("mo").classList.remove("on")}
function upF2(eid,mk,si){mk=ms[mk];var pfx=si>=0?"_s"+si:"",fi=document.getElementById("fi2"+pfx);if(!fi||!fi.files.length)return;var btn=document.getElementById("ub2"+pfx);if(btn){btn.textContent="...";btn.disabled=true}var ad=getMD(eid,si);if(!ad[mk])ad[mk]={report:"",files:[]};var arr=ad[mk].files,pr=0,sk=0;function fin(){try{setMD(eid,ad,si)}catch(e){alert("Хранилище заполнено")}if(sk)alert(sk+" файлов >3 МБ");closeM();oe(eid,cm,si>=0?si:undefined)}for(var i=0;i<fi.files.length;i++){(function(f){if(f.size>3072*1024){sk++;pr++;if(pr===fi.files.length)fin();return}var r=new FileReader();r.onload=function(evt){arr.push({name:f.name,size:f.size,type:f.type,date:new Date().toLocaleDateString(),user:cu?cu.n:"?",data:evt.target.result});pr++;if(pr===fi.files.length)fin()};r.onerror=function(){pr++;if(pr===fi.files.length)fin()};r.readAsDataURL(f)})(fi.files[i])}}
function dlF2(eid,mk,idx,si){si=si||-1;mk=ms[mk];var ad=getMD(eid,si),arr=ad[mk]?ad[mk].files:null;if(!arr||!arr[idx]||!arr[idx].data)return;var f=arr[idx],a=document.createElement("a");a.href=f.data;a.download=f.name;document.body.appendChild(a);a.click();document.body.removeChild(a)}
function rmF2(eid,mk,idx,si){si=si||-1;mk=ms[mk];var ad=getMD(eid,si);if(!ad[mk]||!ad[mk].files)return;ad[mk].files.splice(idx,1);setMD(eid,ad,si);closeM();oe(eid,cm,si>=0?si:undefined)}
document.getElementById("mo").addEventListener("click",function(e){if(e.target===this)closeM()});
document.addEventListener("keydown",function(e){if(e.key==="Escape")closeM()});
document.addEventListener("click",function(e){if(!e.target.closest(".notif-btn")&&!e.target.closest(".notif-drop"))document.getElementById("nd").classList.remove("on")});
var su=localStorage.getItem("su");if(su){try{cu=JSON.parse(su);if(cu)showApp()}catch(e){}}
</script>
</body>
</html>