samruk-ai-agent/index.html

294 lines
40 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{--b:#0F1218;--c:#00E5FF;--w:#fff;--g5:#5B6573;--g1:#F2F4F7;--g2:#E5E7EB;--gn:#10B981;--rd:#EF4444;--am:#F59E0B}
*{box-sizing:border-box;margin:0;padding:0}
body{font:14px/1.4 Arial,sans-serif;color:var(--b);background:var(--g1)}
input,select,textarea,button{font:inherit;outline:none}
.btn{background:var(--c);color:var(--b);padding:10px 20px;border-radius:8px;font-weight:700;font-size:14px;border:none;cursor:pointer}.btn:hover{opacity:.85}
.btn-sm{padding:6px 12px;font-size:12px}.btn-red{background:var(--rd);color:#fff}.btn-gn{background:var(--gn);color:#fff}
#login{display:flex;align-items:center;justify-content:center;min-height:100vh;background:var(--b)}
#login>div{background:var(--w);border-radius:16px;padding:40px 36px;width:400px;max-width:90vw;text-align:center}
#login h1{font-size:22px;font-weight:800;margin-bottom:4px}#login h1 span{color:var(--c)}
#login p{color:var(--g5);font-size:13px;margin-bottom:28px}
#login input{display:block;width:100%;padding:12px 14px;border:1px solid var(--g2);border-radius:8px;font-size:14px;margin-bottom:14px}
#app{display:none;max-width:1200px;margin:0 auto;padding:16px}
.top{display:flex;justify-content:space-between;align-items:center;padding:12px 0;border-bottom:2px solid var(--g2);margin-bottom:16px}
.top b{font-size:18px}.top b span{color:var(--c)}
.top .r{display:flex;align-items:center;gap:12px;font-size:13px;color:var(--g5)}
.notif-btn{position:relative;background:none;border:none;font-size:20px;cursor:pointer}
.notif-btn .cnt{position:absolute;top:-4px;right:-6px;background:var(--rd);color:#fff;border-radius:100px;font-size:9px;padding:1px 5px;font-weight:700}
.notif-drop{position:absolute;top:48px;right:0;width:380px;max-width:90vw;background:var(--w);border:1px solid var(--g2);border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,.12);z-index:300;display:none;max-height:400px;overflow-y:auto}
.notif-drop.on{display:block}
.notif-drop .it{padding:12px 16px;border-bottom:1px solid var(--g1);font-size:12px}.notif-drop .it b{display:block;margin-bottom:2px}.notif-drop .it span{font-size:10px;color:var(--g5)}
.tabs{display:flex;gap:4px;margin-bottom:16px}
.tab{padding:10px 20px;border:none;background:var(--w);cursor:pointer;font-size:14px;font-weight:600;color:var(--g5);border-radius:8px 8px 0 0}.tab.on{color:var(--b);border-bottom:3px solid var(--c)}
.pg{display:none}.pg.on{display:block}
.card{background:var(--w);border-radius:12px;padding:20px;margin-bottom:14px;border:1px solid var(--g2)}
.card h3{font-size:16px;margin-bottom:8px}
.row{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:12px}
.stat{background:var(--w);border-radius:10px;padding:16px 20px;border:1px solid var(--g2);min-width:120px;flex:1;text-align:center}
.stat .n{font-size:26px;font-weight:800}.stat .l{font-size:12px;color:var(--g5)}.stat.r .n{color:var(--rd)}.stat.g .n{color:var(--gn)}.stat.a .n{color:var(--am)}
table{width:100%;border-collapse:collapse}th,td{padding:8px 12px;text-align:left;font-size:13px}
th{font-weight:600;color:var(--g5);font-size:11px;text-transform:uppercase;border-bottom:2px solid var(--g2);cursor:pointer}th:hover{color:var(--b)}
td{border-bottom:1px solid var(--g2)}
tr.rd td{background:#FFF5F5}tr.am td{background:#FFFDF5}
.badge{display:inline-block;padding:3px 8px;border-radius:100px;font-size:11px;font-weight:700}
.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:#eee;color:#666}
.fr{display:flex;gap:8px;margin-bottom:10px;flex-wrap:wrap;align-items:center}
.fr input,.fr select{padding:8px 12px;border:1px solid var(--g2);border-radius:6px;font-size:13px;background:var(--w)}
.fr input{min-width:200px}
.modal-o{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:99;display:none;align-items:center;justify-content:center}.modal-o.on{display:flex}
.modal{background:var(--w);border-radius:14px;max-width:700px;width:94vw;max-height:90vh;overflow-y:auto;padding:28px}
.modal .x{float:right;border:none;background:none;font-size:24px;cursor:pointer;color:var(--g5)}
.modal label{display:block;font-size:12px;font-weight:600;color:var(--g5);margin-bottom:3px;margin-top:10px}
.modal input,.modal select,.modal textarea{width:100%;padding:8px 12px;border:1px solid var(--g2);border-radius:6px;font-size:13px;margin-bottom:6px}
.modal textarea{min-height:60px;resize:vertical}
.meta{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:8px;font-size:12px;color:var(--g5)}.meta strong{display:block;color:var(--b);font-size:13px}
.mt{display:flex;gap:4px;flex-wrap:wrap;margin-bottom:10px}.mt span{padding:4px 10px;border:1px solid var(--g2);border-radius:100px;font-size:11px;font-weight:600;cursor:pointer}.mt span.on{background:var(--c);color:var(--b)}
.si{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--g1);border-radius:6px;margin-bottom:4px;font-size:12px}.si .n{font-weight:700;color:var(--c);font-size:14px;min-width:18px}
.fl{display:flex;align-items:center;gap:6px;padding:6px 10px;background:var(--g1);border-radius:6px;margin-bottom:3px;font-size:12px}.fl .nm{font-weight:600;cursor:pointer}.fl .nm:hover{color:var(--c)}.fl .sz{font-size:10px;color:var(--g5)}
.up{border:2px dashed var(--g2);border-radius:8px;padding:12px;margin-top:6px;text-align:center}
.up p{font-size:12px;color:var(--g5);margin-bottom:8px}
.up input[type=file]{font-size:12px}
.ai-block{background:#E8FCFF;border-radius:6px;padding:10px;margin:10px 0;font-size:12px}
.hi{font-size:11px;color:var(--g5);padding:2px 0}.hi .d{display:inline-block;width:5px;height:5px;border-radius:50%;background:var(--c);margin-right:4px;vertical-align:middle}
.ai-chat{border:1px solid var(--g2);border-radius:8px;padding:12px;max-height:300px;overflow-y:auto;margin-bottom:10px;background:var(--w)}
.ai-chat .msg{margin-bottom:8px;padding:8px 12px;border-radius:8px;font-size:13px;max-width:90%}
.ai-chat .user{background:var(--c);color:var(--b);margin-left:auto;text-align:right}
.ai-chat .bot{background:var(--g1);color:var(--b)}
.ai-input{display:flex;gap:8px}.ai-input input{flex:1;padding:8px 12px;border:1px solid var(--g2);border-radius:6px;font-size:13px}
@media(max-width:600px){#app{padding:8px}.row{flex-direction:column}.stat{min-width:auto}.meta{grid-template-columns:1fr}}
</style>
</head>
<body>
<div id="login"><div><h1><span>План ПБ</span> 2026</h1><p>АО «Казахтелеком»</p><input id="lem" placeholder="curator@telecom.kz"><input id="lpw" type="password" placeholder="Пароль (любой)"><p class="err" id="lerr" style="color:var(--rd);font-size:12px;display:none">Неверная почта</p><button class="btn" style="width:100%" onclick="doLogin()">Войти</button></div></div>
<div id="app">
<div class="top"><b><span>План ПБ</span> 2026</b>
<div class="r"><span id="ul"></span>
<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-sm btn-red" onclick="doLogout()">Выйти</button>
</div>
</div>
<div class="tabs">
<button class="tab on" data-pg="ev">📋 Мероприятия</button>
<button class="tab" data-pg="an">📊 Аналитика</button>
<button class="tab" data-pg="rp">📥 Отчёты</button>
<button class="tab" data-pg="ai">🤖 AI</button>
</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 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=["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"];
function M(i){return mn[parseInt(ms[i].split("-")[1])-1]+" "+ms[i].split("-")[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"";if(e.s==="late")return'<span style="color:var(--rd);font-weight:700">ПРОСРОЧЕНО</span>';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-new Date())/86400000);if(days<0)return'<span style="color:var(--rd);font-weight:700">'+Math.abs(days)+' дн. просрочки</span>';if(days<=7)return'<span style="color:var(--rd);font-weight:700">'+days+' дн.</span>';if(days<=14)return'<span style="color:var(--am);font-weight:700">'+days+' дн.</span>';if(days<=30)return'<span style="color:var(--am)">'+days+' дн.</span>';return days+' дн.'}return""}
function rowClass(e){if(e.s==="late")return"rd";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-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,cm=5,cr=0,esi=-1,ex={},sc2=null,sd2=1;
function getMD(id,ri,si){ri=ri||0;var k=si>=0?"sf_"+id+"_s"+si+"_r"+ri:"sf_"+id+"_r"+ri;var r=localStorage.getItem(k);return r?JSON.parse(r):{}}
function setMD(id,o,ri,si){ri=ri||0;var k=si>=0?"sf_"+id+"_s"+si+"_r"+ri:"sf_"+id+"_r"+ri;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))}
var ev=null;
function le(){var s=localStorage.getItem("se2");if(s){try{ev=JSON.parse(s);return}catch(e){}}ld()}
function se(){localStorage.setItem("se2",JSON.stringify(ev||[]))}
function ld(){var x=new XMLHttpRequest();x.open("GET","data.json",false);try{x.send();if(x.status===200)ev=JSON.parse(x.responseText);else ev=[]}catch(e){ev=[]};se()}
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));show()}else document.getElementById("lerr").style.display="block"}
function doLogout(){localStorage.removeItem("su");cu=null;document.getElementById("login").style.display="flex";document.getElementById("app").style.display="none"}
function show(){document.getElementById("login").style.display="none";document.getElementById("app").style.display="block";document.getElementById("ul").innerHTML="<b>"+cu.n+"</b> · "+(cu.r==="cur"?"Все":br[cu.b]);switchPg("ev")}
function switchPg(n){document.querySelectorAll(".tab").forEach(function(t){t.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");if(n==="ev")re();else if(n==="an")ra();else if(n==="rp")rr();else if(n==="ai")rAI()}
document.querySelectorAll(".tab").forEach(function(t){t.addEventListener("click",function(){switchPg(this.dataset.pg)})});
// ===== 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,50)+"...",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,ti:e.due});
else if(days<=7)n.push({c:"🟠",m:"7 дн: №"+e.id+" — "+e.t.slice(0,40)+"...",ti:e.due});
else if(days<=14)n.push({c:"🟡",m:"14 дн: №"+e.id+" — "+e.t.slice(0,40)+"...",ti:e.due});
else if(days<=30)n.push({c:"🔵",m:"30 дн: №"+e.id+" — "+e.t.slice(0,40)+"...",ti:e.due});
}
});
n.sort(function(a,b){var o={"🔴":0,"🟠":1,"🟡":2,"🔵":3};return(o[a.c]||9)-(o[b.c]||9)});
var el=document.getElementById("nd"),c=document.getElementById("nc");c.textContent=n.length;c.style.display=n.length?"inline-block":"none";
el.innerHTML=n.length?n.map(function(x){return'<div class="it"><b>'+x.c+' '+esc(x.m)+'</b><span>Срок: '+x.ti+'</span></div>'}).join(""):'<div class="it" style="text-align:center;color:var(--g5)">Нет уведомлений</div>'}
// ===== EVENTS =====
function toggleEx(id){ex[id]=!ex[id];re()}
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);se()}re()}
function re(){
var sf=document.getElementById("sf2");sf=sf?sf.value:"";
var sr2=document.getElementById("sr2");sr2=sr2?sr2.value.toLowerCase():"";
var bf=document.getElementById("bf2");bf=bf?bf.value:"";
var list=ev||[];
if(sf)list=list.filter(function(e){return e.s===sf});
if(sr2)list=list.filter(function(e){return e.t.toLowerCase().indexOf(sr2)>=0||br[e.b].toLowerCase().indexOf(sr2)>=0});
if(bf)list=list.filter(function(e){return e.b===parseInt(bf)});
if(sc2){list.sort(function(a,b){var va=a[sc2],vb=b[sc2];if(typeof va==="string")va=va.toLowerCase(),vb=vb.toLowerCase();return va>vb?sd2:va<vb?-sd2:0})}
nu();
var h='<div class="card"><div class="fr"><input id="sr2" placeholder="Поиск..." oninput="re()"><select id="sf2" onchange="re()"><option value="">Все статусы</option><option value="wait">Не начато</option><option value="warn">В процессе</option><option value="done">Исполнено</option><option value="late">Просрочено</option></select><select id="bf2" onchange="re()"><option value="">Все филиалы</option>'+br.map(function(b,i){return'<option value="'+i+'">'+b+'</option>'}).join("")+'</select><span style="font-size:12px;color:var(--g5);margin-left:auto">'+list.length+' из '+ev.length+'</span></div>';
h+='<table><tr><th>№</th><th>Мероприятие</th><th>Ответственные</th><th>Раздел</th><th>Срок</th><th class="srt">Осталось</th><th>Статус</th><th></th></tr>';
list.forEach(function(e){
var hs=e.sub&&e.sub.length,sc=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:300px">';
if(hs)h+='<span onclick="event.stopPropagation();toggleEx('+e.id+')" style="cursor:pointer;margin-right:4px">'+(ex[e.id]?'▼':'▶')+'</span>';
h+=esc(e.t);if(hs)h+=' <span style="font-size:10px;color:var(--g5)">('+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(--g1)"><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 ra(){
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="row"><div class="stat"><div class="l">Всего</div><div class="n">'+all.length+'</div></div><div class="stat g"><div class="l">Исполнено</div><div class="n">'+done.length+'</div><div class="n" style="font-size:14px">'+dp+'%</div></div><div class="stat a"><div class="l">В процессе</div><div class="n">'+warn.length+'</div></div><div class="stat r"><div class="l">Просрочено</div><div class="n">'+late.length+'</div></div><div class="stat"><div class="l">Не начато</div><div class="n">'+wait.length+'</div></div></div>';
// Branch ranking — TOP
var brData=[];br.forEach(function(b,i){var it=all.filter(function(e){return e.b===i}),d=it.filter(function(e){return e.s==="done"}).length,p=it.length?Math.round(d/it.length*100):0,l=it.filter(function(e){return e.s==="late"}).length;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(--gn)':b.pct>=40?'var(--am)':'var(--rd)')+';font-weight:700">'+b.pct+'%</span></td><td>'+(b.late?b.late:'—')+'</td></tr>'});
h+='</table></div>';
// TOP problem events
var problems=all.filter(function(e){return e.s==="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>'}
// Quantities
var tq=0,rq={};reg.forEach(function(r,ri){rq[ri]=0});all.forEach(function(e){reg.forEach(function(ri){var d=getMD(e.id,ri,-1);for(var k in d){if(d.hasOwnProperty(k)&&d[k]){tq+=d[k].qty||0;rq[ri]+=d[k].qty||0}if(e.sub)e.sub.forEach(function(si){var sd=getMD(e.id,ri,si);for(var sk in sd){if(sd.hasOwnProperty(sk)&&sd[sk]){tq+=sd[sk].qty||0;rq[ri]+=sd[sk].qty||0}}})}})});
h+='<div class="card"><h3>📊 Количественные показатели</h3><div class="row"><div class="stat" style="background:var(--c);color:var(--b)"><div class="l">Всего единиц</div><div class="n">'+tq+'</div></div>';reg.forEach(function(r,ri){if(rq[ri])h+='<div class="stat"><div class="l">'+r+'</div><div class="n">'+rq[ri]+'</div></div>'});h+='</div></div>';
document.getElementById("pg-an").innerHTML=h;
}
// ===== REPORTS =====
function rr(){var h='<div class="card"><h3>Сводный отчёт</h3><div class="fr"><select id="rf">'+ms.map(function(m,i){return'<option value="'+i+'">'+M(i)+'</option>'}).join("")+'</select><span>—</span><select id="rt">'+ms.map(function(m,i){return'<option value="'+i+'"'+(i===11?" selected":"")+'>'+M(i)+'</option>'}).join("")+'</select><button class="btn btn-sm" onclick="dCSV()">CSV</button><button class="btn btn-sm" onclick="dHTML()">HTML</button></div>';
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}
h+='<p style="font-size:12px;color:var(--g5);margin-bottom:8px">Хранилище: '+(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" 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);var csv="\uFEFF№;Филиал;Мероприятие;Регион;Статус;Осталось;Срок\n";(ev||[]).forEach(function(e){reg.forEach(function(_,ri){var d=getMD(e.id,ri,-1),rep="";for(var i=f;i<=t;i++){var m=ms[i];if(d[m]&&d[m].report)rep+=M(i)+": "+d[m].report.replace(/"/g,'""')+"; "}csv+=e.id+';'+br[e.b]+';"'+e.t.replace(/"/g,'""')+'";'+reg[ri]+';'+st[e.s]+';'+daysLeft(e).replace(/<[^>]*>/g,'')+';'+e.due+';"'+rep+'"\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);var h='<!DOCTYPE html><html><head><meta charset="utf-8"><title>Отчёт ПБ</title><style>body{font:13px/1.4 Arial;max-width:1000px;margin:0 auto;padding:20px}.ev{border:1px solid #ddd;border-radius:8px;padding:14px;margin-bottom:12px}.badge{display:inline-block;padding:2px 6px;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:6px 10px;border-radius:4px;margin:4px 0}a.flink{color:#0F1218;font-weight:600}</style></head><body><h2>Сводный отчёт ПБ</h2>';ev.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>';
reg.forEach(function(_,ri){var d=getMD(e.id,ri,-1);for(var i=f;i<=t;i++){var m=ms[i];if(d[m]&&(d[m].report||(d[m].files&&d[m].files.length))){h+='<div class="m"><b>'+M(i)+' — '+reg[ri]+'</b>';if(d[m].report)h+='<p>'+esc(d[m].report)+'</p>';if(d[m].qty)h+='<p>Количество: <b>'+d[m].qty+'</b></p>';
if(d[m].files&&d[m].files.length)h+='<p>📎 Файлы: <br>'+d[m].files.map(function(f2){return'<a class="flink" href="'+f2.data+'" download="'+esc(f2.name)+'">📄 '+esc(f2.name)+'</a> ('+(f2.size/1024).toFixed(0)+' КБ) — загружен: '+(f2.date||'')}).join("<br>")+'</p>';h+='</div>'}}});
if(e.sub)e.sub.forEach(function(s,si){reg.forEach(function(_,ri){var sd=getMD(e.id,ri,si);for(var i=f;i<=t;i++){var m=ms[i];if(sd[m]&&(sd[m].report||(sd[m].files&&sd[m].files.length))){h+='<div class="m" style="border-left:3px solid #00E5FF;padding-left:8px"><b>'+s.l+') '+esc(s.t.slice(0,50))+' — '+reg[ri]+' | '+M(i)+'</b>';if(sd[m].report)h+='<p>'+esc(sd[m].report)+'</p>';if(sd[m].qty)h+='<p>Количество: <b>'+sd[m].qty+'</b></p>';
if(sd[m].files&&sd[m].files.length)h+='<p>📎 Файлы: <br>'+sd[m].files.map(function(f2){return'<a class="flink" href="'+f2.data+'" download="'+esc(f2.name)+'">📄 '+esc(f2.name)+'</a> ('+(f2.size/1024).toFixed(0)+' КБ)'}).join("<br>")+'</p>';h+='</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;se();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("Очищено");rr()}
// ===== AI AGENT =====
var chatHistory=[];
function rAI(){
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:var(--b);color:var(--w);border-radius:14px"><h3 style="color:var(--c);font-size:20px">🤖 ИИ-ассистент</h3><p style="color:#9aa3b2;font-size:13px">Задайте вопрос по исполнению Плана мероприятий</p></div>';
h+='<div class="card"><h3>📊 Сводка</h3><div class="row"><div class="stat g"><div class="l">Выполнено</div><div class="n">'+dp+'%</div></div><div class="stat r"><div class="l">Просрочено</div><div class="n">'+late.length+'</div></div><div class="stat"><div class="l">Всего</div><div class="n">'+all.length+'</div></div></div></div>';
// Risk prediction
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(--rd)"><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(--g5);margin:8px 0">Примеры: «просроченные», «риски», «отчёт за квартал», «статус пункта 25», «сводка для руководства»</p>';
// Chat
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.key===\'Enter\')aiAsk()"><button class="btn btn-sm" 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)});
var ans=aiAnswer(q);chatHistory.push({role:"bot",text:"<b>🤖 ИИ:</b> "+ans});
rAI();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"});
if(q.indexOf("просрочен")>=0||q.indexOf("просрочк")>=0){if(!late.length)return "Просроченных мероприятий нет.";return "Просрочено <b>"+late.length+"</b> мероприятий: "+late.map(function(e){return "№"+e.id+" ("+br[e.b]+", срок "+e.due+")"}).join("; ")+"."}
if(q.indexOf("риск")>=0||q.indexOf("невыполнен")>=0){var 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);if(days<=30)risks.push({e:e,days:days})}}});risks.sort(function(a,b){return a.days-b.days});if(!risks.length)return "Мероприятий с высоким риском невыполнения не выявлено.";return "Выявлено <b>"+risks.length+"</b> мероприятий с риском: "+risks.slice(0,5).map(function(r){return "№"+r.e.id+" ("+r.days+" дн)"}).join("; ")+"."}
if(q.indexOf("статус")>=0||q.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)return "Пункт №"+e.id+": <b>"+st[e.s]+"</b>. "+esc(e.t.slice(0,100))+"... Филиал: "+br[e.b]+". Срок: "+e.due+".";return "Пункт №"+num[0]+" не найден."}}
if(q.indexOf("сводка")>=0||q.indexOf("руководств")>=0||q.indexOf("правлен")>=0){var dp=all.length?Math.round(done.length/all.length*100):0;return "<b>Краткая сводка для руководства:</b><br>• План выполнен на "+dp+"% ("+done.length+"/"+all.length+")<br>• Просрочено: "+late.length+" мероприятий<br>• Требует внимания руководства: "+(late.length?"эскалация просрочек по филиалам":"текущая ситуация под контролем")}
if(q.indexOf("филиал")>=0||q.indexOf("отста")>=0){var brLate={};late.forEach(function(e){brLate[e.b]=(brLate[e.b]||0)+1});var worst=Object.keys(brLate).sort(function(a,b){return brLate[b]-brLate[a]})[0];if(worst>=0)return "Филиал с наибольшим числом просрочек: <b>"+br[parseInt(worst)]+"</b> ("+brLate[worst]+" просрочек). Рекомендуется усилить контроль.";return "Все филиалы работают без просрочек."}
if(q.indexOf("отчёт")>=0||q.indexOf("квартал")>=0){return "Для формирования отчёта перейдите на вкладку «Отчёты», выберите период и нажмите CSV или HTML. Также доступен полный бекап через кнопку «💾 Сохранить»."}
return "Я могу ответить на вопросы:<br>• «просроченные» — список просрочек<br>• «риски» — прогноз рисков<br>• «статус пункта N» — статус конкретного мероприятия<br>• «сводка» — краткая сводка для руководства<br>• «филиалы» — какой филиал отстаёт<br>• «отчёт» — как сформировать отчётность";
}
// ===== EDIT =====
function oe(id,mi,ri,si){
if(typeof mi==="number")cm=mi;if(typeof ri==="number")cr=ri;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],sc=gsc(e.id),md=getMD(e.id,cr,-1),cd=md[m]||{report:"",files:[]},cfs=cd.files||[];
var h='<span class="x" onclick="closeM()">&times;</span><span class="badge b">'+["I","II","III","IV","V"][e.sec]+'</span>';
h+='<h3 style="margin:6px 0">'+esc(e.t)+'</h3>';
h+='<div class="meta"><div>Филиал<strong>'+br[e.b]+'</strong></div><div>Срок<strong>'+e.due+' ('+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(--c)':'')+'"><span class="n">'+sb.l+')</span> <span style="flex:1;font-size:11px">'+esc(sb.t)+'</span><button class="btn btn-sm" onclick="oe('+e.id+','+cm+','+cr+','+i+')" style="'+(isA?'background:var(--c);font-weight:700':'')+'">'+(isA?'📂':'📎')+'</button></div>';
if(isA){var sd=getMD(e.id,cr,i),scd=sd[m]||{report:"",files:[]},scfs=scd.files||[];h+='<div style="margin:0 0 10px 12px;padding:12px;background:#E8FCFF;border-radius:6px;border:2px solid var(--c)"><b>'+sb.l+') '+esc(sb.t)+'</b><p style="font-size:10px;color:var(--g5)">'+reg[cr]+' · '+M(cm)+'</p>';h+='<label>Описание</label><textarea id="mr2_s'+i+'">'+esc(scd.report||"")+'</textarea>';h+='<div style="display:flex;gap:8px"><div style="flex:1"><label>Количество</label><input type="number" id="mq2_s'+i+'" min="0" value="'+(scd.qty||0)+'"></div></div>';scfs.forEach(function(f,fi){h+='<div class="fl"><span class="nm" onclick="dlF2('+e.id+','+cm+','+fi+','+cr+','+i+')">📄 '+esc(f.name)+'</span><span class="sz">'+(f.size/1024).toFixed(0)+' КБ · '+f.date+'</span><button onclick="rmF2('+e.id+','+cm+','+fi+','+cr+','+i+')" style="border:none;color:var(--rd);cursor:pointer">×</button></div>'});h+='<div class="up"><p>📤 Загрузить документы (PDF, DOC, XLS, фото, презентации)</p><input type="file" id="fi2_s'+i+'" multiple><button class="btn btn-sm" id="ub2_s'+i+'" onclick="upF2('+e.id+','+cm+','+cr+','+i+')" style="margin-top:6px">Загрузить</button></div></div>'}})}
}else{
h+='<label>Месяц</label><div class="mt">';ms.forEach(function(_,i){h+='<span class="'+(i===cm?"on":"")+'" onclick="oe('+e.id+','+i+','+cr+')">'+M(i)+'</span>'});h+='</div>';
h+='<label>Регион</label><div class="mt">';reg.forEach(function(r,i){h+='<span class="'+(i===cr?"on":"")+'" onclick="oe('+e.id+','+cm+','+i+')">'+r+'</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+','+cr+',-1)">📄 '+esc(f.name)+'</span><span class="sz">'+(f.size/1024).toFixed(0)+' КБ · '+f.date+'</span><button onclick="rmF2('+e.id+','+cm+','+i+','+cr+',-1)" style="border:none;color:var(--rd);cursor:pointer">×</button></div>'});
h+='<div class="up"><p>📤 Загрузить документы</p><input type="file" id="fi2" multiple><button class="btn btn-sm" id="ub2" onclick="upF2('+e.id+','+cm+','+cr+',-1)" style="margin-top:6px">Загрузить</button><div class="types" style="font-size:10px;color:var(--g5);margin-top:4px">PDF, DOC/DOCX, XLS/XLSX, фото, презентации</div></div>';
}
if(e.sub&&e.sub.length){h+='<label>Месяц</label><div class="mt">';ms.forEach(function(_,i){h+='<span class="'+(i===cm?"on":"")+'" onclick="oe('+e.id+','+i+','+cr+','+esi+')">'+M(i)+'</span>'});h+='</div>';h+='<label>Регион</label><div class="mt">';reg.forEach(function(r,i){h+='<span class="'+(i===cr?"on":"")+'" onclick="oe('+e.id+','+cm+','+i+','+esi+')">'+r+'</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+='<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:14px;display:flex;gap:8px"><button class="btn" onclick="sv('+e.id+','+cm+')">Сохранить</button><button class="btn" style="background:var(--g2)" 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,cr,-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,cr,-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,cr,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,cr,i)}})}var now=new Date().toLocaleDateString();e.h.push(now+" — "+cu.n+": "+st[e.s]);if(e.s==="done"&&e.done==="—")e.done=now;se();closeM();re()}
function closeM(){document.getElementById("mo").classList.remove("on")}
function upF2(eid,mk,ri,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,ri,si);if(!ad[mk])ad[mk]={report:"",files:[]};var arr=ad[mk].files,pr=0,sk=0;function fin(){try{setMD(eid,ad,ri,si)}catch(e){alert("Хранилище заполнено")}if(sk)alert(sk+" файлов >3 МБ");closeM();oe(eid,cm,ri,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,ri,si){si=si||-1;mk=ms[mk];var ad=getMD(eid,ri,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,ri,si){si=si||-1;mk=ms[mk];var ad=getMD(eid,ri,si);if(!ad[mk]||!ad[mk].files)return;ad[mk].files.splice(idx,1);setMD(eid,ad,ri,si);closeM();oe(eid,cm,ri,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")});
le();var su=localStorage.getItem("su");if(su){try{cu=JSON.parse(su);show()}catch(e){}}
</script>
</body>
</html>