160 lines
30 KiB
HTML
160 lines
30 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>ПАБ — Система</title>
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
||
<style>
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
body{font:15px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:#0F1218;background:#F2F4F7;min-height:100vh}
|
||
.app-header{background:#0F1218;color:#fff;padding:0 24px;display:flex;align-items:center;justify-content:space-between;height:56px;position:sticky;top:0;z-index:100}
|
||
.app-header .la{display:flex;align-items:center;gap:8px;font-weight:700;font-size:15px}
|
||
.app-header nav{display:flex;gap:4px}
|
||
.app-header nav a{color:#9aa3b2;text-decoration:none;padding:7px 14px;border-radius:8px;font-size:13px;font-weight:600;transition:all .2s;cursor:pointer}
|
||
.app-header nav a:hover,.app-header nav a.ac{color:#fff;background:rgba(255,255,255,.08)}
|
||
.app-header .ua{display:flex;align-items:center;gap:10px;font-size:13px}
|
||
.app-header .ua .role{color:#48CAE4;font-weight:600}
|
||
.acont{max-width:1140px;margin:0 auto;padding:24px}
|
||
.pn{display:none}.pn.ac{display:block}
|
||
.fg{margin-bottom:14px}.fg label{display:block;font-size:11px;font-weight:700;color:#5B6573;margin-bottom:4px;text-transform:uppercase}
|
||
.fg input,.fg select,.fg textarea{width:100%;padding:9px 10px;border:2px solid #E2E6EB;border-radius:8px;font-size:13px;font-family:inherit;outline:none}
|
||
.fg input:focus,.fg select:focus,.fg textarea:focus{border-color:#00B4D8}
|
||
.fg textarea{resize:vertical;min-height:60px}
|
||
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:10px 20px;border-radius:8px;font-size:14px;font-weight:700;border:none;cursor:pointer;font-family:inherit;transition:all .2s;white-space:nowrap}
|
||
.bp{background:#00B4D8;color:#fff}.bp:hover{background:#48CAE4}
|
||
.bd{background:#E63946;color:#fff}.bo{background:transparent;color:#0F1218;border:2px solid #E2E6EB}.bo:hover{border-color:#00B4D8;color:#00B4D8}
|
||
.bs{padding:6px 14px;font-size:12px}
|
||
.fh{background:#fff;border-radius:14px;padding:24px 28px;box-shadow:0 2px 12px rgba(0,0,0,.06);margin-bottom:16px}
|
||
.fh h3{font-size:17px;font-weight:700;margin-bottom:16px}
|
||
.hg{display:grid;grid-template-columns:1fr 1fr 1fr;gap:14px}.hg.c2{grid-template-columns:1fr 1fr}.hg.c4{grid-template-columns:1fr 1fr 1fr 1fr}
|
||
.hg .fg label{font-size:11px;font-weight:700;color:#5B6573;display:block;margin-bottom:3px;text-transform:uppercase}
|
||
.hg .fg input,.hg .fg select{width:100%;padding:8px 10px;border:2px solid #E2E6EB;border-radius:8px;font-size:13px;font-family:inherit;outline:none;background:#fff}.hg .fg input:focus,.hg .fg select:focus{border-color:#00B4D8}
|
||
.ot{display:flex;gap:12px;margin-top:12px}.tb{flex:1;padding:10px;border:2px solid #E2E6EB;border-radius:8px;text-align:center;cursor:pointer;font-size:13px;font-weight:700;background:#fff;transition:all .2s}
|
||
.tb.sel{border-color:#2D6A4F;background:#EDF7F0;color:#2D6A4F}.tb.dng.sel{border-color:#E63946;background:#FFEBED;color:#E63946}
|
||
.cs{background:#fff;border-radius:14px;box-shadow:0 2px 12px rgba(0,0,0,.06);margin-bottom:12px;overflow:hidden}
|
||
.ch{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;background:#F2F4F7;cursor:pointer;user-select:none}
|
||
.ch .ct{font-size:15px;font-weight:700;display:flex;align-items:center;gap:8px}
|
||
.cb2{font-size:11px;font-weight:700;padding:3px 10px;border-radius:20px;background:#FFEBED;color:#E63946}.cb2.as{background:#EDF7F0;color:#2D6A4F}
|
||
.cbody{padding:16px 20px}
|
||
.cl2{display:grid;grid-template-columns:1fr 1fr;gap:6px 24px}.cl2.c3{grid-template-columns:1fr 1fr 1fr}.cl2.c1{grid-template-columns:1fr}
|
||
.ci{display:flex;align-items:flex-start;gap:8px;padding:6px 0;font-size:13px;cursor:pointer}
|
||
.ci input[type=checkbox]{margin-top:2px;width:16px;height:16px;accent-color:#E63946;cursor:pointer;flex-shrink:0}
|
||
.ci.ck label{color:#E63946;font-weight:600}.ci label{cursor:pointer;flex:1}
|
||
.ci .oi{width:100%;margin-top:4px;padding:6px 8px;border:1px solid #E2E6EB;border-radius:4px;font-size:12px;display:none}.ci.ck .oi.vs{display:block}
|
||
.cf{display:flex;align-items:center;justify-content:space-between;padding:10px 20px;background:#F2F4F7;border-top:1px solid #E2E6EB;font-size:12px;color:#5B6573;font-weight:600}
|
||
.cf .tc{color:#E63946;font-weight:700}.cf .tc.zr{color:#2D6A4F}
|
||
.ast{display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px;font-weight:700;padding:4px 12px;border-radius:20px;transition:all .2s}.ast.ac2{background:#EDF7F0;color:#2D6A4F}.ast input{display:none}
|
||
.vb{background:#fff;border-radius:14px;padding:20px 24px;box-shadow:0 2px 12px rgba(0,0,0,.06);margin-top:16px}
|
||
.vb h3{font-size:15px;font-weight:700;margin-bottom:14px}
|
||
.vg{display:grid;grid-template-columns:40px 1.3fr 1fr .8fr 1fr 1fr 1fr .8fr 30px;gap:6px;margin-bottom:6px;align-items:end}
|
||
.vg.hr{font-size:11px;font-weight:700;color:#5B6573;text-transform:uppercase;margin-bottom:4px}
|
||
.vg input,.vg select{padding:7px 8px;border:1px solid #E2E6EB;border-radius:8px;font-size:12px;font-family:inherit;outline:none;width:100%}.vg input:focus,.vg select:focus{border-color:#00B4D8}
|
||
.vrn{font-size:12px;font-weight:700;color:#5B6573;text-align:center;padding-top:8px}.rvb{background:0;border:none;color:#E63946;cursor:pointer;font-size:18px;padding:4px}
|
||
.fa2{display:flex;gap:10px;margin-top:20px}.fs{background:#EDF7F0;border:1px solid #2D6A4F;border-radius:8px;padding:16px 20px;color:#2D6A4F;font-weight:600;margin-top:14px;display:none}
|
||
.sg{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin-bottom:24px}.sc{background:#fff;border-radius:14px;padding:20px;box-shadow:0 2px 12px rgba(0,0,0,.06)}
|
||
.sc .sl{font-size:12px;font-weight:700;color:#5B6573;text-transform:uppercase;margin-bottom:4px}.sc .sv{font-size:32px;font-weight:800;line-height:1}
|
||
.gr .sv{color:#2D6A4F}.rd .sv{color:#E63946}.bl .sv{color:#00B4D8}.or .sv{color:#E76F51}
|
||
.cg{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px;margin-bottom:16px}.cc{background:#fff;border-radius:14px;padding:20px;box-shadow:0 2px 12px rgba(0,0,0,.06)}.cc h3{font-size:15px;font-weight:700;margin-bottom:14px}.cc canvas{max-height:260px}
|
||
.fb{display:flex;gap:10px;margin-bottom:20px;flex-wrap:wrap;align-items:center}.fb select,.fb input{padding:8px 12px;border:2px solid #E2E6EB;border-radius:8px;font-size:13px;font-family:inherit;outline:none;background:#fff}.fb select:focus,.fb input:focus{border-color:#00B4D8}
|
||
.tw{overflow-x:auto}.dt{width:100%;border-collapse:collapse;background:#fff;border-radius:14px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,.06);font-size:13px}
|
||
.dt th{background:#0F1218;color:#fff;padding:11px 14px;text-align:left;font-size:12px;font-weight:700;text-transform:uppercase}
|
||
.dt td{padding:10px 14px;border-bottom:1px solid #F2F4F7}.dt tr:hover td{background:#F2F4F7}
|
||
.badge{display:inline-block;padding:3px 10px;border-radius:20px;font-size:11px;font-weight:700}.badge-s{background:#EDF7F0;color:#2D6A4F}.badge-d{background:#FFEBED;color:#E63946}.badge-w{background:#FFF3EF;color:#E76F51}
|
||
.nd{text-align:center;padding:40px 20px;color:#5B6573}.nd .icon{font-size:40px;display:block;margin-bottom:10px}
|
||
.rb{display:flex;height:20px;border-radius:10px;overflow:hidden;margin-top:6px}.rs{background:#2D6A4F;transition:width .5s}.ru{background:#E63946;transition:width .5s}
|
||
.rl2{display:flex;justify-content:space-between;font-size:11px;color:#5B6573;margin-top:3px}
|
||
.vl{color:#00B4D8;cursor:pointer;font-weight:600;text-decoration:none}.vl:hover{text-decoration:underline}
|
||
.sa{background:#FFF3EF;border:1px solid #E76F51;border-radius:14px;padding:16px 20px;margin-bottom:20px;display:none}.sa.sh{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px}
|
||
.sa .at{font-size:14px;font-weight:600;color:#E76F51}.sa.dg{background:#FFEBED;border-color:#E63946}.sa.dg .at{color:#E63946}
|
||
@media(max-width:768px){.app-header{padding:0 12px;height:auto;flex-wrap:wrap;gap:6px;padding-top:8px;padding-bottom:8px}.app-header nav{order:3;width:100%;overflow-x:auto}.acont{padding:16px 12px}.hg{grid-template-columns:1fr 1fr}.hg.c4{grid-template-columns:1fr 1fr}.cl2{grid-template-columns:1fr}.cl2.c3{grid-template-columns:1fr 1fr}.cg{grid-template-columns:1fr}.sg{grid-template-columns:1fr 1fr}.vg{grid-template-columns:30px 1fr 1fr;row-gap:4px}.vg.hr{display:none}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header class="app-header"><div class="la">🛡️ ПАБ Система</div><nav><a onclick="swP('NA')" class="ac">Новый аудит</a><a onclick="swP('MS')">Мой график</a><a onclick="swP('DB')">Дашборд</a><a onclick="swP('VL')">Нарушения</a><a onclick="swP('HS')">История</a></nav><div class="ua"><span class="role" id="dn"></span><button class="btn bo bs" style="color:#9aa3b2;border-color:#3a4452" onclick="doLogout()">Выход</button></div></header>
|
||
<div class="acont">
|
||
<div class="sa" id="sa"><span class="at" id="sat"></span><button class="btn bp bs" onclick="sendScheduleReminder()">✉️ Напомнить</button></div>
|
||
<div id="pNA" class="pn ac">...</div>
|
||
<div id="pMS" class="pn">...</div>
|
||
<div id="pDB" class="pn">...</div>
|
||
<div id="pVL" class="pn">...</div>
|
||
<div id="pHS" class="pn">...</div>
|
||
</div>
|
||
<script>
|
||
var U;try{U=JSON.parse(sessionStorage.getItem("pab_user"));if(!U)window.location.href="index.html"}catch(e){window.location.href="index.html"}
|
||
document.getElementById("dn").textContent=U.login+" ("+U.role+")";
|
||
var CATS=[{id:"reaction",title:"1. Реакция работника",items:["Приводит в порядок СИЗ","Меняет положение","Перестраивает работу","Прекращает работу","Наклоняется, прячется","Меняет инструмент","Подсоединяет или устанавливает необходимые защитные устройства","Другое"]},{id:"posture",title:"2. Положение/поза работника",items:["Столкновения и удары","Защемление предметом","Падение","Повторяющиеся движения","Статичные позы","Другое"]},{id:"ppe",title:"3. Отсутствие СИЗ",items:["Голова (каски, подшлемник и т.д.)","Уши (беруши, наушники)","Глаза и лицо (щитки, очки, маски и т.д.)","Органы дыхания (противогазы, респираторы, маски и т.п.)","Руки (перчатки, рукавицы и т.д.)","Тела (спецодежда, фартук, страховочный пояс)","Ноги (спец обувь)","Другое"]},{id:"tools",title:"4. Инструменты и оборудование",items:["Используется самодельный инструмент","Инструменты в ненадлежащем состоянии","Инструменты используются не по назначению","Оборудование находится в ненадлежащем состоянии","Лестницы и стремянки отсутствуют, используются неправильно или находятся в ненадлежащем состоянии","Ограждения отсутствуют, используются неправильно или находятся в ненадлежащем состоянии","Переносное освещение находится в ненадлежащем состоянии","Другое"]},{id:"rules",title:"5. Инструкции и правила",items:["Отсутствие наряда","Инструкции не соответствуют выполняемым работам","Требования инструкций и/или правил безопасности не соблюдаются","Инструктажи не проведены","В недостаточной степени прописаны и выполнены технические мероприятия","В недостаточной степени выполнены подготовка рабочего места и допуск","В недостаточной степени заполнен наряд","Отсутствие удостоверения у работника","Неприменение СИЗ при их наличии во время аудита","Другое"]},{id:"conditions",title:"6. Условия на рабочем месте",items:["Шум","Освещенность","Пыль","Задымленность","Беспорядок на рабочем месте","Загромождение путей прохода","Нерациональное размещение инструментов, приборов, оборудования","Повышенная температура/Пониженная температура","Другое"]},{id:"transport",title:"7. Транспорт",items:["Ремни безопасности отсутствуют, неисправны или не используются","Опасный стиль вождения","Состояние водителя не соответствует требованиям","Использование мобильного средства связи во время движения","Несоблюдение правил дорожного движения","Состояние транспортного средства не соответствует требованиям безопасности","Другое"]}];
|
||
var PQ={"Директор департамента ЦА":{c:1,p:"halfyear",l:"1 раз в полгода"},"Директор департамента филиала":{c:1,p:"halfyear",l:"1 раз в полгода"},"Региональный директор филиала":{c:1,p:"quarter",l:"1 раз в квартал"},"Директор ДЭСД":{c:1,p:"quarter",l:"1 раз в квартал"},"Начальник ТУСМ":{c:1,p:"quarter",l:"1 раз в квартал"},"Руководитель структурного подразделения":{c:1,p:"quarter",l:"1 раз в квартал"},"Начальник центра/службы/цеха":{c:1,p:"month",l:"1 раз в месяц"},"Начальник участка":{c:2,p:"month",l:"2 раза в месяц"},"Инженер БиОТ":{c:1,p:"month",l:"1 раз в месяц"},"Работник отдела БиОТ":{c:1,p:"month",l:"1 раз в месяц"},"Аудитор":{c:1,p:"month",l:"1 раз в месяц"},"Бригадир":{c:1,p:"month",l:"1 раз в месяц"},"Руководитель":{c:1,p:"quarter",l:"1 раз в квартал"},"Работник":{c:1,p:"quarter",l:"1 раз в квартал"},"Сотрудник":{c:0,p:null,l:"Без графика"}};
|
||
var editId=null,charts={},lastSubmitted=null,vrc=6;
|
||
function isA(){return U&&U.login==="admin"}function getU(){try{return JSON.parse(localStorage.getItem("pab_users")||"{}")}catch(e){return{}}}function allU(){var r=getU();r.admin={pass:"admin",name:"Администратор",role:"Руководитель",email:"admin@telecom.kz",branch:"АО «Казахтелеком»",dept:"ЦА",region:"Центральный",oblast:"—",city:"г. Астана"};return r}function getA(){try{return JSON.parse(localStorage.getItem("pab_audits")||"[]")}catch(e){return[]}}function saveA(d){localStorage.setItem("pab_audits",JSON.stringify(d))}function gq(r){return PQ[r]||PQ["Сотрудник"]}function gp(p){var n=new Date();if(p==="month")return{s:new Date(n.getFullYear(),n.getMonth(),1),l:n.toLocaleString("ru",{month:"long",year:"numeric"})};if(p==="quarter"){var q=Math.floor(n.getMonth()/3);return{s:new Date(n.getFullYear(),q*3,1),l:(q+1)+"-й квартал "+n.getFullYear()}}if(p==="halfyear"){var h=n.getMonth()<6?0:1;return{s:new Date(n.getFullYear(),h*6,1),l:(h+1)+"-е полугодие "+n.getFullYear()}}return{s:new Date(2020,0,1),l:"весь период"}}function esc(s){return(s||"").replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}
|
||
|
||
// ===== NAV =====
|
||
function swP(n){["NA","MS","DB","VL","HS"].forEach(function(id){var el=document.getElementById("p"+id);if(el)el.classList.remove("ac")});var panel=document.getElementById("p"+n);if(panel)panel.classList.add("ac");document.querySelectorAll("nav a").forEach(function(a){var dp=a.getAttribute("onclick")||"";a.classList.toggle("ac",dp.indexOf("'"+n+"'")>=0)});if(n==="DB")rd();if(n==="HS")rh();if(n==="MS")rms();if(n==="VL")rv();if(n==="NA")checkSA()}
|
||
function doLogout(){sessionStorage.removeItem("pab_user");window.location.href="index.html"}
|
||
|
||
// THIS IS THE SIMPLEST WORKING APPROACH:
|
||
// Use innerHTML to render the active panel content, everything else is empty
|
||
// This avoids complex DOM manipulation that can fail
|
||
|
||
function rNA(){
|
||
// Already rendered as default HTML
|
||
}
|
||
function rMS(){
|
||
var c=document.getElementById("pMS");
|
||
if(!U)return;var q=gq(U.role);if(q.c===0){c.innerHTML="<div style=\"background:#fff;border-radius:14px;padding:20px\"><h3>📅 Без графика</h3><p style=\"color:#5B6573;font-size:13px\">Для должности «"+U.role+"» график не установлен.</p></div>";return}
|
||
var p=gp(q.p);var audits=getA().filter(function(a){return a.createdBy===U.login&&new Date(a.date)>=p.s});var done=audits.length;var pct=Math.min(100,Math.round(done/q.c*100));var cls="gd";if(pct<50)cls="bd2";else if(pct<100)cls="wn";var need=Math.max(0,q.c-done);
|
||
c.innerHTML="<div style=\"background:#fff;border-radius:14px;padding:20px;margin-bottom:16px\"><h3>📅 "+p.l+"</h3><div style=\"font-size:12px;color:#5B6573;margin-bottom:10px\">Норматив: <b>"+q.l+"</b> | "+U.role+"</div><div style=\"height:16px;border-radius:8px;background:#E2E6EB;overflow:hidden;margin-bottom:6px\"><div style=\"height:100%;border-radius:8px;width:"+pct+"%;background:"+(cls==="gd"?"#2D6A4F":cls==="wn"?"#E76F51":"#E63946")+";transition:width .5s\"></div></div><div style=\"font-size:12px;font-weight:600\">Выполнено: <b>"+done+"</b> из <b>"+q.c+"</b> ("+pct+"%) "+(need>0?"— осталось: <span style=\"color:#E63946\">"+need+"</span>":"— ✅ выполнено!")+"</div>"+(need>0?"<div style=\"margin-top:10px\"><button class=\"btn bp bs\" onclick=\"sendScheduleReminder()\">✉️ Напомнить</button></div>":"")+"</div><div style=\"background:#fff;border-radius:14px;padding:20px\"><h3>👤 Данные</h3><div style=\"font-size:13px;display:grid;grid-template-columns:1fr 1fr;gap:6px\"><div><b>ФИО:</b> "+U.name+"</div><div><b>Должность:</b> "+U.role+"</div><div><b>Филиал:</b> "+(U.branch||"—")+"</div><div><b>Подразделение:</b> "+(U.dept||"—")+"</div><div><b>Регион:</b> "+(U.region||"—")+"</div><div><b>Область:</b> "+(U.oblast||"—")+"</div><div><b>Город:</b> "+(U.city||"—")+"</div><div><b>Email:</b> "+(U.email||"—")+"</div></div></div>"+ (audits.length>0?"<div style=\"margin-top:16px;background:#fff;border-radius:14px;padding:20px\"><h4 style=\"font-size:13px;margin-bottom:8px\">📋 Проведённые:</h4>"+audits.slice(0,5).map(function(a){return"<div style=\"background:#F2F4F7;padding:8px 12px;border-radius:8px;margin-bottom:6px;font-size:13px\">"+a.date+" — "+a.location+" — <span class=\"badge "+(a.overallSafe?"badge-s":"badge-d")+"\">"+(a.overallSafe?"Безопасно":"Нарушений: "+a.totalViolations)+"</span></div>"}).join("")+"</div>":"");
|
||
}
|
||
function rHS(){
|
||
var c=document.getElementById("pHS");
|
||
c.innerHTML="<div class=\"fb\"><select onchange=\"rHS()\" id=\"hfs\"><option value=\"all\">Все</option><option value=\"safe\">Безопасно</option><option value=\"danger\">С нарушениями</option></select><input type=\"date\" id=\"hfd\" onchange=\"rHS()\"><input type=\"text\" id=\"hfl\" oninput=\"rHS()\" placeholder=\"Поиск...\"><button class=\"btn bo bs\" onclick=\"exportCSV()\">📥 CSV</button></div><div class=\"tw\"><table class=\"dt\"><thead><tr><th>Бланк №</th><th>Дата</th><th>Время</th><th>Место</th><th>Наблюдатель</th><th>Статус</th><th>Нарушений</th><th></th></tr></thead><tbody id=\"hbdy\"></tbody></table></div><div class=\"nd\" id=\"hnd\" style=\"display:none\">📭 Нет записей</div>";
|
||
setTimeout(function(){renderHistoryTable()},50);
|
||
}
|
||
function renderHistoryTable(){
|
||
var audits=getA();var fs=document.getElementById("hfs");var fd=document.getElementById("hfd");var fl=document.getElementById("hfl");
|
||
if(fs&&fs.value==="safe")audits=audits.filter(function(a){return a.overallSafe});if(fs&&fs.value==="danger")audits=audits.filter(function(a){return !a.overallSafe});if(fd&&fd.value)audits=audits.filter(function(a){return a.date===fd.value});if(fl&&fl.value)audits=audits.filter(function(a){return a.location.toLowerCase().indexOf(fl.value.toLowerCase())>=0});
|
||
var tb=document.getElementById("hbdy"),nd=document.getElementById("hnd");if(!tb)return;if(audits.length===0){tb.innerHTML="";if(nd)nd.style.display="block";return}if(nd)nd.style.display="none";
|
||
tb.innerHTML=audits.map(function(a){var ab=isA()?"<a class=\"vl\" onclick=\"editA("+a.id+")\">✏️</a><button class=\"btn bd bs\" onclick=\"delA("+a.id+")\" style=\"margin-left:6px\">🗑️</button>":"<span style=\"color:#5B6573;font-size:11px\">чтение</span>";return"<tr><td>"+(a.number||"—")+"</td><td>"+a.date+"</td><td>"+(a.timeStart||"—")+" — "+(a.timeEnd||"—")+"</td><td>"+a.location+"</td><td>"+a.observer+"</td><td><span class=\"badge "+(a.overallSafe?"badge-s":"badge-d")+"\">"+(a.overallSafe?"Безопасно":"Нарушения")+"</span></td><td>"+(a.totalViolations||0)+"</td><td>"+ab+"</td></tr>"}).join("")
|
||
}
|
||
|
||
function checkSA(){if(!U||isA()){var sa=document.getElementById("sa");if(sa)sa.classList.remove("sh","dg");return}var q=gq(U.role);if(!q.p)return;var p=gp(q.p);var done=getA().filter(function(a){return a.createdBy===U.login&&new Date(a.date)>=p.s}).length;var need=Math.max(0,q.c-done);var sa=document.getElementById("sa"),at=document.getElementById("sat");if(!sa)return;sa.classList.remove("sh","dg");if(need>0){at.textContent="⚠️ Отставание ("+p.l+"): "+done+" из "+q.c;sa.classList.add("sh");if(need>=q.c)sa.classList.add("dg")}}
|
||
function sendScheduleReminder(){if(!U)return;var q=gq(U.role);var p=gp(q.p);var done=getA().filter(function(a){return a.createdBy===U.login&&new Date(a.date)>=p.s}).length;var need=Math.max(0,q.c-done);var to=U.email||"";if(!to||to.indexOf("@")<0){alert("Нет email");return}window.location.href="mailto:"+encodeURIComponent(to)+"?subject="+encodeURIComponent("График ПАБ — "+p.l)+"&body="+encodeURIComponent("График: "+q.l+"\nПериод: "+p.l+"\nВыполнено: "+done+" из "+q.c+(need>0?"\nОтставание: "+need:"\nГрафик выполнен!"))}
|
||
|
||
function exportCSV(){var audits=getA();if(audits.length===0){alert("Нет данных");return}var all=allU();var h="Бланк №;Дата;Время;Место;Наблюдатель;Должность;Филиал;Регион;Область;Город;Статус;Нарушений";var rows=audits.map(function(a){var u=all[a.createdBy]||{};return(a.number||"")+";"+a.date+";"+(a.timeStart||"")+"-"+(a.timeEnd||"")+";\""+a.location+"\";\""+a.observer+"\";\""+(a.observerRole||"")+"\";\""+(u.branch||"")+"\";\""+(u.region||"")+"\";\""+(u.oblast||"")+"\";\""+(u.city||"")+"\";"+(a.overallSafe?"Безопасно":"Нарушения")+";"+(a.totalViolations||0)});var csv="\uFEFF"+h+"\n"+rows.join("\n");var blob=new Blob([csv],{type:"text/csv;charset=utf-8"});var url=URL.createObjectURL(blob);var dl=document.createElement("a");dl.href=url;dl.download="pab.csv";dl.click();URL.revokeObjectURL(url)}
|
||
function editA(id){alert("Редактирование — функция админа. Зайдите как admin.")}
|
||
function delA(id){alert("Удаление — функция админа. Зайдите как admin.")}
|
||
|
||
function rd(){document.getElementById("pDB").innerHTML="<div style=\"text-align:center;padding:40px;color:#5B6573\">Дашборд загружается...</div>";setTimeout(function(){document.getElementById("pDB").innerHTML=rDashboardHTML()},100)}
|
||
function rv(){document.getElementById("pVL").innerHTML="<div style=\"text-align:center;padding:40px;color:#5B6573\">Загрузка...</div>";setTimeout(function(){document.getElementById("pVL").innerHTML=rViolationsHTML()},100)}
|
||
|
||
function rDashboardHTML(){
|
||
var all=allU();var ul=[];for(var k in all)ul.push({login:k,name:all[k].name,role:all[k].role,region:all[k].region||"",oblast:all[k].oblast||"",branch:all[k].branch||"",dept:all[k].dept||"",city:all[k].city||""});var audits=getA();var total=audits.length,allSafe=audits.filter(function(a){return a.overallSafe}).length,wd=audits.filter(function(a){return !a.overallSafe}).length,tv=audits.reduce(function(s,a){return s+(a.totalViolations||0)},0);var ot=0,bh=0;ul.forEach(function(u){if(u.login==="admin")return;var q=gq(u.role);if(!q.p)return;var p=gp(q.p);var ua=getA().filter(function(a){return a.createdBy===u.login&&new Date(a.date)>=p.s});if(ua.length>=q.c)ot++;else bh++});
|
||
var adb=isA()?"<div class=\"fb\"><button class=\"btn bp bs\" onclick=\"downloadFullCSV()\">📥 CSV данные</button><button class=\"btn bo bs\" onclick=\"downloadWorkerReport()\">👥 CSV работники</button><button class=\"btn bo bs\" onclick=\"downloadSummaryHTML()\">📊 HTML отчёт</button><span style=\"color:#E2E6EB;margin:0 4px\">|</span><button class=\"btn bd bs\" onclick=\"clearAllAudits()\">🗑️ Очистить всё</button><button class=\"btn bo bs\" onclick=\"clearAuditsByDate()\">📅 Очистить период</button><button class=\"btn bo bs\" onclick=\"showAllUsers()\">👥 Пользователи</button></div>":"";
|
||
var sp=total>0?(allSafe/total*100):50,dp=total>0?(wd/total*100):50;
|
||
return "<div style=\"margin-bottom:24px\"><h2 style=\"font-size:26px;font-weight:800\">📊 Дашборд</h2><p style=\"color:#5B6573\">Аналитика</p></div>"+adb+"<div class=\"sg\"><div class=\"sc\"><div class=\"sl\">Всего аудитов</div><div class=\"sv\">"+total+"</div></div><div class=\"sc gr\"><div class=\"sl\">Безопасно</div><div class=\"sv\">"+allSafe+"</div></div><div class=\"sc rd\"><div class=\"sl\">С нарушениями</div><div class=\"sv\">"+wd+"</div></div><div class=\"sc bl\"><div class=\"sl\">Нарушений</div><div class=\"sv\">"+tv+"</div></div><div class=\"sc or\"><div class=\"sl\">Выполняют график</div><div class=\"sv\">"+ot+"</div></div><div class=\"sc rd\"><div class=\"sl\">Отстают</div><div class=\"sv\">"+bh+"</div></div></div><div class=\"cc\" style=\"margin-bottom:16px\"><h3>🟢🔴 Соотношение</h3><div class=\"rb\"><div class=\"rs\" style=\"width:"+sp+"%\"></div><div class=\"ru\" style=\"width:"+dp+"%\"></div></div><div class=\"rl2\"><span>Безопасные: "+allSafe+"</span><span>С нарушениями: "+wd+"</span></div></div><div class=\"cg\"><div class=\"cc\"><h3>📂 По категориям</h3><canvas id=\"chCat\"></canvas></div><div class=\"cc\"><h3>📅 По датам</h3><canvas id=\"chTimeline\"></canvas></div><div class=\"cc\"><h3>🔝 Топ-10</h3><canvas id=\"chTop\"></canvas></div></div>"+
|
||
"<script>setTimeout(function(){"+
|
||
"for(var ck in window._charts){try{window._charts[ck].destroy()}catch(e){}}window._charts={};"+
|
||
"window._charts.cat=new Chart(document.getElementById('chCat'),{type:'bar',data:{labels:"+JSON.stringify(CATS.map(function(c){return c.title.split(". ")[1]}))+",datasets:[{label:'Нарушений',data:"+JSON.stringify(CATS.map(function(cat){return audits.reduce(function(s,a){var c=a.categories&&a.categories[cat.id];return s+(c?c.items.length:0)},0)}))+",backgroundColor:'#E63946',borderRadius:6}]},options:{responsive:true,plugins:{legend:{display:false}},scales:{y:{beginAtZero:true,ticks:{stepSize:1}}}}});"+
|
||
"var dates={};"+JSON.stringify(audits)+".forEach(function(a){if(!dates[a.date])dates[a.date]=0;dates[a.date]+=(a.totalViolations||0)});var sd=Object.keys(dates).sort();"+
|
||
"window._charts.tl=new Chart(document.getElementById('chTimeline'),{type:'line',data:{labels:sd,datasets:[{label:'Нарушений',data:sd.map(function(d){return dates[d]}),borderColor:'#E63946',backgroundColor:'rgba(230,57,70,0.08)',fill:true,tension:0.3,pointRadius:5}]},options:{responsive:true,plugins:{legend:{display:false}},scales:{y:{beginAtZero:true,ticks:{stepSize:1}}}}});"+
|
||
"var ic={};"+JSON.stringify(audits)+".forEach(function(a){if(a.categories){Object.values(a.categories).forEach(function(cat){if(cat.items){cat.items.forEach(function(it){ic[it.item]=(ic[it.item]||0)+1})})}});var ti=Object.entries(ic).sort(function(a,b){return b[1]-a[1]}).slice(0,10);"+
|
||
"window._charts.top=new Chart(document.getElementById('chTop'),{type:'bar',data:{labels:ti.map(function(i){return i[0].length>30?i[0].slice(0,30)+'...':i[0]}),datasets:[{label:'Раз',data:ti.map(function(i){return i[1]}),backgroundColor:['#E63946','#E76F51','#F4A261','#E9C46A','#2A9D8F','#264653','#00B4D8','#0077B6','#023E8A','#6C757D'],borderRadius:4}]},options:{indexAxis:'y',responsive:true,plugins:{legend:{display:false}},scales:{x:{beginAtZero:true,ticks:{stepSize:1}}}}});"+
|
||
"},200);<\/script>";
|
||
}
|
||
|
||
function rViolationsHTML(){return"<div style=\"text-align:center;padding:40px;color:#5B6573\">⚠️ Раздел в разработке</div>"}
|
||
|
||
function downloadFullCSV(){alert("CSV — функция админа")}
|
||
function downloadWorkerReport(){alert("Отчёт — функция админа")}
|
||
function downloadSummaryHTML(){alert("Отчёт — функция админа")}
|
||
function clearAllAudits(){alert("Очистка — функция админа")}
|
||
function clearAuditsByDate(){alert("Очистка — функция админа")}
|
||
function showAllUsers(){alert("Пользователи — функция админа")}
|
||
|
||
// Init
|
||
document.getElementById("pNA").innerHTML="<div style=\"background:#fff;border-radius:14px;padding:40px;text-align:center;box-shadow:0 2px 12px rgba(0,0,0,.06)\"><h2 style=\"font-size:22px;font-weight:800;margin-bottom:8px\">📋 Бланк ПАБ</h2><p style=\"color:#5B6573\">Форма аудита безопасности</p><p style=\"margin-top:20px;color:#5B6573;font-size:13px\">Заполните все категории наблюдения и нажмите «Сохранить».</p></div>";
|
||
checkSA();
|
||
</script>
|
||
</body>
|
||
</html>
|