diff --git a/index.html b/index.html index f682ec7..7abe363 100644 --- a/index.html +++ b/index.html @@ -105,7 +105,7 @@ td{border-bottom:1px solid var(--gray-200)}tr:hover td{background:var(--cyan-50)

АО «Казахтелеком» — мониторинг производственной безопасности

-

admin@telecom.kz / ahmetov@telecom.kz / serikov@telecom.kz — пароль любой

+

curator@telecom.kz (куратор) / admin@telecom.kz / north@telecom.kz (Север) / almaty@telecom.kz (Алматы) — пароль любой

Неверная почта или пароль

@@ -128,12 +128,14 @@ td{border-bottom:1px solid var(--gray-200)}tr:hover td{background:var(--cyan-50)
- + +
+
@@ -143,6 +145,8 @@ td{border-bottom:1px solid var(--gray-200)}tr:hover td{background:var(--cyan-50) "use strict"; var sections=["I. Люди","II. Оборудование","III. Аварии и ЧС","IV. Информ. работа","V. ИИ и цифровизация"]; var branches=["Дирекция производственной безопасности","Дивизион «Сеть»","Дивизион по корпоративному бизнесу","Дивизион по розничному бизнесу","Сервисная фабрика","Дирекция «Телеком Комплект»","Корпоративный университет","Дирекция управления проектами","Дивизион цифрового бизнеса"]; +var regions=["Центральный регион","Алматинский регион","Южный регион","Северный регион","Восточный регион","Западный регион"]; +var branchRegion=[0,3,1,1,2,3,4,0,5]; // branch index -> region index var statusMap={done:"Исполнено",warn:"На контроле",late:"Просрочено",wait:"В процессе"}; var months=["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 monthNames=["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"]; @@ -151,7 +155,24 @@ function esc(s){return s.replace(/&/g,"&").replace(//g function sb(s){var m={done:"green",warn:"amber",late:"red",wait:"gray"};return''+statusMap[s]+''} function pct(p){var c=p>=80?"var(--green)":p>=40?"var(--amber)":"var(--red)";return'
'+p+'%
'} -var users={"dpp@telecom.kz":{name:"Директор ДПБ",branch:0,role:"director"},"ahmetov@telecom.kz":{name:"Ахметов К.Т.",branch:6,role:"resp"},"serikov@telecom.kz":{name:"Сериков А.М.",branch:1,role:"resp"},"nurlanov@telecom.kz":{name:"Нурланов Д.С.",branch:8,role:"resp"},"aliev@telecom.kz":{name:"Алиев Г.С.",branch:4,role:"resp"},"tulegenov@telecom.kz":{name:"Тулегенов Е.А.",branch:2,role:"resp"},"saparov@telecom.kz":{name:"Сапаров А.Д.",branch:3,role:"resp"},"maratov@telecom.kz":{name:"Маратов Ж.К.",branch:5,role:"resp"},"iskakov@telecom.kz":{name:"Искаков Р.Н.",branch:7,role:"resp"},"admin@telecom.kz":{name:"Администратор",branch:0,role:"admin"}}; +var users={ + "curator@telecom.kz":{name:"Куратор Плана",branch:0,role:"curator",region:-1}, + "admin@telecom.kz":{name:"Администратор",branch:0,role:"admin",region:-1}, + "ahmetov@telecom.kz":{name:"Ахметов К.Т.",branch:6,role:"branch",region:4}, + "serikov@telecom.kz":{name:"Сериков А.М.",branch:1,role:"branch",region:3}, + "nurlanov@telecom.kz":{name:"Нурланов Д.С.",branch:8,role:"branch",region:5}, + "aliev@telecom.kz":{name:"Алиев Г.С.",branch:4,role:"branch",region:2}, + "tulegenov@telecom.kz":{name:"Тулегенов Е.А.",branch:2,role:"branch",region:1}, + "saparov@telecom.kz":{name:"Сапаров А.Д.",branch:3,role:"branch",region:1}, + "maratov@telecom.kz":{name:"Маратов Ж.К.",branch:5,role:"branch",region:3}, + "iskakov@telecom.kz":{name:"Искаков Р.Н.",branch:7,role:"branch",region:0}, + "north@telecom.kz":{name:"Отв. Северный регион",branch:1,role:"region",region:3}, + "almaty@telecom.kz":{name:"Отв. Алматинский регион",branch:2,role:"region",region:1}, + "south@telecom.kz":{name:"Отв. Южный регион",branch:4,role:"region",region:2}, + "center@telecom.kz":{name:"Отв. Центральный регион",branch:0,role:"region",region:0}, + "east@telecom.kz":{name:"Отв. Восточный регион",branch:6,role:"region",region:4}, + "west@telecom.kz":{name:"Отв. Западный регион",branch:8,role:"region",region:5} +}; var curUser=null,curMonth=5; // Load events @@ -165,15 +186,32 @@ var events=null; })(); function saveEvents(){localStorage.setItem("samruk_ev",JSON.stringify(events))} -function getMy(){if(!curUser||!events)return[];if(curUser.role==="admin"||curUser.role==="director")return events;return events.filter(function(e){return e.b===curUser.branch})} +// Action log +function addLog(action,eventId,detail){var l=JSON.parse(localStorage.getItem("samruk_log")||"[]");l.push({ts:new Date().toISOString(),user:curUser.name,role:curUser.role,action:action,eid:eventId,detail:detail||""});if(l.length>500)l=l.slice(-500);localStorage.setItem("samruk_log",JSON.stringify(l))} +function getLog(){return JSON.parse(localStorage.getItem("samruk_log")||"[]")} + +function getMy(){if(!curUser||!events)return[];if(curUser.role==="admin"||curUser.role==="curator")return events;if(curUser.role==="region")return events.filter(function(e){return branchRegion[e.b]===curUser.region});return events.filter(function(e){return e.b===curUser.branch})} // Auth -function doLogin(e){e.preventDefault();var em=document.getElementById("loginEmail").value.trim().toLowerCase();if(users[em]){curUser={email:em,name:users[em].name,branch:users[em].branch,role:users[em].role};localStorage.setItem("samruk_u",JSON.stringify(curUser));showApp()}else{document.getElementById("loginErr").style.display="block"}return false} -function doLogout(){localStorage.removeItem("samruk_u");curUser=null;document.getElementById("loginScreen").style.display="flex";document.getElementById("app").style.display="none"} -function showApp(){document.getElementById("loginScreen").style.display="none";document.getElementById("app").style.display="block";document.getElementById("userLabel").innerHTML=""+curUser.name+" · "+branches[curUser.branch];renderAll()} +function doLogin(e){e.preventDefault();var em=document.getElementById("loginEmail").value.trim().toLowerCase();if(users[em]){ curUser={email:em,name:users[em].name,branch:users[em].branch,role:users[em].role,region:users[em].region};localStorage.setItem("samruk_u",JSON.stringify(curUser));addLog("вошёл");showApp()}else{document.getElementById("loginErr").style.display="block"}return false} +function doLogout(){addLog("вышел");localStorage.removeItem("samruk_u");curUser=null;document.getElementById("loginScreen").style.display="flex";document.getElementById("app").style.display="none"} +function showApp(){document.getElementById("loginScreen").style.display="none";document.getElementById("app").style.display="block";var label=curUser.name+" · "+(curUser.role==="curator"||curUser.role==="admin"?"Все регионы":curUser.role==="region"?regions[curUser.region]:branches[curUser.branch]);document.getElementById("userLabel").innerHTML=""+label+"";renderAll()} // Notifs -function notifsUpdate(){var my=getMy(),n=[];my.forEach(function(e){if(e.s==="late")n.push({m:"🔴 Просрочено: "+e.t.slice(0,60)+"...",t:e.due});if(e.s==="warn"&&e.p<30)n.push({m:"🟡 Низкий прогресс: "+e.t.slice(0,50)+"...",t:"Сейчас"})});var el=document.getElementById("notifDrop"),c=document.getElementById("notifCount");c.textContent=n.length;c.style.display=n.length?"inline-block":"none";el.innerHTML=n.length?n.map(function(x){return'
'+esc(x.m)+'
'+x.t+'
'}).join(""):'
Нет уведомлений
'} +function notifsUpdate(){ + var my=getMy(),n=[],now=new Date(); + my.forEach(function(e){ + if(e.s==="late")n.push({m:"🔴 Просрочено: "+e.t.slice(0,50)+"...",t:e.due}); + if(e.s==="warn"&&e.p<30)n.push({m:"🟡 Низкий прогресс: "+e.t.slice(0,50)+"...",t:"Сейчас"}); + // Deadline approaching (within 7 days) + if(e.s!=="done"&&e.s!=="late"){ + var parts=e.due.split(".");if(parts.length===3){ + var due=new Date(parseInt(parts[2]),parseInt(parts[1])-1,parseInt(parts[0])); + var days=(due-now)/86400000; + if(days>0&&days<=7)n.push({m:"⏰ Срок через "+Math.round(days)+" дн: "+e.t.slice(0,50)+"...",t:e.due}); + } + } + });var el=document.getElementById("notifDrop"),c=document.getElementById("notifCount");c.textContent=n.length;c.style.display=n.length?"inline-block":"none";el.innerHTML=n.length?n.map(function(x){return'
'+esc(x.m)+'
'+x.t+'
'}).join(""):'
Нет уведомлений
'} function toggleNotif(){notifsUpdate();document.getElementById("notifDrop").classList.toggle("open")} // Tabs @@ -182,7 +220,7 @@ function switchTab(name){ document.querySelector('[data-tab="'+name+'"]').classList.add("active"); document.querySelectorAll(".tab-content").forEach(function(c){c.classList.remove("active")}); document.getElementById("tab-"+name).classList.add("active"); - if(name==="dashboard")renderDashboard();else if(name==="myevents")renderMyEvents();else if(name==="analytics")renderAnalytics(); + if(name==="dashboard")renderDashboard();else if(name==="myevents")renderMyEvents();else if(name==="analytics")renderAnalytics();else if(name==="journal")renderJournal(); } // ===== DASHBOARD ===== @@ -222,6 +260,15 @@ function renderDashboard(){ h+=''; h+=''; + // Regional breakdown (for curator/admin) + if(curUser.role==="curator"||curUser.role==="admin"){ + h+='

🌍 Исполнение по регионам

'; + regions.forEach(function(r,ri){var items=events.filter(function(e){return branchRegion[e.b]===ri}),d=items.filter(function(e){return e.s==="done"}).length; + h+=''; + }); + h+='
РегионВсегоИсполнено%
'+r+''+items.length+''+d+''+pct(items.length?Math.round(d/items.length*100):0)+'
'; + } + document.getElementById("tab-dashboard").innerHTML=h; } @@ -235,7 +282,7 @@ function downloadReport(){ addRow("",-1); if(e.sub) e.sub.forEach(function(s,i){ addRow(s.l,i); }); }); - var blob=new Blob(["\uFEFF"+csv],{type:"text/csv;charset=utf-8"}),a=document.createElement("a");a.href=URL.createObjectURL(blob);a.download="otchet_pb_"+M(from)+"-"+M(to)+".csv";a.click() + var blob=new Blob(["\uFEFF"+csv],{type:"text/csv;charset=utf-8"}),a=document.createElement("a");a.href=URL.createObjectURL(blob);a.download="otchet_pb_"+M(from)+"-"+M(to)+".csv";a.click();addLog("скачал CSV-отчёт",null,M(from)+"-"+M(to)) } function downloadHTML(){ @@ -257,7 +304,7 @@ function downloadHTML(){ h+=''; try{ var blob=new Blob(["\uFEFF"+h],{type:"text/html;charset=utf-8"}),a=document.createElement("a"); - a.href=URL.createObjectURL(blob);a.download="otchet_pb_"+M(from)+"-"+M(to)+".html";a.click(); + a.href=URL.createObjectURL(blob);a.download="otchet_pb_"+M(from)+"-"+M(to)+".html";a.click();addLog("скачал HTML-отчёт",null,M(from)+"-"+M(to)); setTimeout(function(){URL.revokeObjectURL(a.href)},60000); }catch(e){ alert("⚠️ Отчёт слишком большой для скачивания. Попробуйте выбрать меньший период или очистить часть файлов."); @@ -327,7 +374,7 @@ function toggleSubItem(eid, subIdx, checked) { else if(sc.length === 0 && e.s === 'done') e.s = 'warn'; e.p = Math.max(e.p, pct); e.h.push(new Date().toLocaleDateString()+' — '+curUser.name+': подпункты '+sc.length+'/'+e.sub.length); - saveEvents(); + saveEvents();addLog("подпункты",eid,sc.length+"/"+e.sub.length); } renderMyEvents(); } @@ -444,7 +491,7 @@ function saveEdit(id, mk){ } var now=new Date().toLocaleDateString();e.h.push(now+" — "+curUser.name+": "+statusMap[e.s]+", "+e.p+"%"+(cmt?" — "+cmt:"")); if(e.s==="done"&&e.done==="\u2014")e.done=now; - saveEvents();closeEM();renderAll(); + saveEvents();addLog("изменил статус",id,statusMap[e.s]+" "+e.p+"%");closeEM();renderAll(); } function closeEM(){document.getElementById("editModalOverlay").classList.remove("open")} @@ -507,6 +554,7 @@ function uploadFiles(eid,mk,si){ var bak=JSON.parse(JSON.stringify(ad));bak[mk].files=bak[mk].files.slice(0,-(pr-sk)||0);try{setMD(eid,bak,si)}catch(e2){} alert("⚠️ Хранилище заполнено. Удалите старые файлы (кнопка «Очистить файлы» на дашборде)."); } + addLog("загрузил файлы",eid,(pr-sk)+" файл(ов) за "+M(mk)); if(sk)alert(sk+" файл(ов) > 3 МБ пропущены");closeEM();openEdit(eid,curMonth,si>=0?si:undefined) } for(var i=0;iMAX){sk++;pr++;if(pr===fi.files.length)fin();return} @@ -516,8 +564,17 @@ function uploadFiles(eid,mk,si){ function dlF(eid,mk,idx,si){si=si||-1;mk=months[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 rmF(eid,mk,idx,si){si=si||-1;mk=months[mk];var ad=getMD(eid,si);if(!ad[mk]||!ad[mk].files)return;ad[mk].files.splice(idx,1);setMD(eid,ad,si);closeEM();openEdit(eid,curMonth,si>=0?si:undefined)} -// Init -function renderAll(){notifsUpdate();switchTab(document.querySelector(".tab-btn.active").dataset.tab)} +function renderJournal(){ + var log=getLog().reverse(),h='

Журнал действий

'; + if(!log.length){h+='

Действий пока нет

'}else{ + h+=''; + log.forEach(function(l){var e=null;if(l.eid)for(var i=0;i'; + }); + h+='
ВремяПользовательРольДействиеМероприятиеДетали
'+esc(l.user)+''+(l.role==="curator"?"Куратор":l.role==="admin"?"Админ":l.role==="region"?"Регион":"Филиал")+''+esc(l.action)+''+(e?e.id+". "+esc(e.t.slice(0,40))+"...":"—")+''+esc(l.detail)+'
'} + h+='
'; + document.getElementById("tab-journal").innerHTML=h; +} document.querySelectorAll(".tab-btn").forEach(function(b){b.addEventListener("click",function(){switchTab(this.dataset.tab)})}); document.getElementById("editModalOverlay").addEventListener("click",function(e){if(e.target===this)closeEM()}); document.addEventListener("keydown",function(e){if(e.key==="Escape"){closeEM();document.getElementById("notifDrop").classList.remove("open")}});