safety-audit/app.html

160 lines
30 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>ПАБ — Система</title>
<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,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}
// ===== 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>