safety-audit/app.html

283 lines
66 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>
:root{--ink:#0F1218;--cyan:#00B4D8;--cyan-light:#48CAE4;--cyan-bg:#E0F7FA;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7;--gray-200:#E2E6EB;--red:#E63946;--red-bg:#FFEBED;--green:#2D6A4F;--green-bg:#EDF7F0;--orange:#E76F51;--orange-bg:#FFF3EF;--radius:8px;--radius-lg:14px;--shadow:0 2px 12px rgba(0,0,0,.06)}
*{box-sizing:border-box;margin:0;padding:0}
body{font:15px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--gray-100);min-height:100vh}
.app-screen{display:block}
.app-header{background:var(--ink);color:var(--white);padding:0 24px;display:flex;align-items:center;justify-content:space-between;height:56px;position:sticky;top:0;z-index:100;box-shadow:0 2px 8px rgba(0,0,0,.15)}
.app-header .la{display:flex;align-items:center;gap:8px;font-weight:700;font-size:15px}.app-header .la .icon{font-size:22px}
.app-header nav{display:flex;gap:4px}
.app-header nav a{color:#9aa3b2;text-decoration:none;padding:7px 14px;border-radius:var(--radius);font-size:13px;font-weight:600;transition:all .2s;cursor:pointer}
.app-header nav a:hover,.app-header nav a.active{color:var(--white);background:rgba(255,255,255,.08)}
.app-header .ua{display:flex;align-items:center;gap:10px;font-size:13px}
.app-header .ua .role{color:var(--cyan-light);font-weight:600}
.app-content{max-width:1140px;margin:0 auto;padding:24px}
.panel{display:none}.panel.active{display:block}
.page-header{margin-bottom:24px}.page-header h2{font-size:26px;font-weight:800;margin-bottom:6px}.page-header p{color:var(--gray-500);font-size:15px}
.sa{background:var(--orange-bg);border:1px solid var(--orange);border-radius:var(--radius-lg);padding:16px 20px;margin-bottom:20px;display:none;box-shadow:var(--shadow)}
.sa.show{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px}
.sa .at{font-size:14px;font-weight:600;color:var(--orange)}.sa.danger{background:var(--red-bg);border-color:var(--red)}.sa.danger .at{color:var(--red)}
.pc{background:var(--white);border-radius:var(--radius-lg);padding:20px;box-shadow:var(--shadow);margin-bottom:16px}
.pc h3{font-size:15px;font-weight:700;margin-bottom:4px}.pc .qi{font-size:12px;color:var(--gray-500);margin-bottom:10px}
.pb{height:16px;border-radius:8px;background:var(--gray-200);overflow:hidden;margin-bottom:6px}
.pf{height:100%;border-radius:8px;transition:width .5s}.pf.good{background:var(--green)}.pf.warn{background:var(--orange)}.pf.bad{background:var(--red)}
.pc .ps{font-size:12px;font-weight:600}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:10px 20px;border-radius:var(--radius);font-size:14px;font-weight:700;border:none;cursor:pointer;text-decoration:none;font-family:inherit;transition:all .2s;white-space:nowrap}
.btn-p{background:var(--cyan);color:var(--white)}.btn-p:hover{background:var(--cyan-light)}
.btn-d{background:var(--red);color:var(--white)}.btn-d:hover{background:#c1121f}
.btn-o{background:transparent;color:var(--ink);border:2px solid var(--gray-200)}.btn-o:hover{border-color:var(--cyan);color:var(--cyan)}
.btn-s{padding:6px 14px;font-size:12px}
.af{max-width:900px}
.fh{background:var(--white);border-radius:var(--radius-lg);padding:24px 28px;box-shadow:var(--shadow);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}
.fg{margin-bottom:14px}
.fg label{display:block;font-size:11px;font-weight:700;color:var(--gray-500);margin-bottom:4px;text-transform:uppercase;letter-spacing:.5px}
.hg .fg label{font-size:11px;font-weight:700;color:var(--gray-500);display:block;margin-bottom:3px;text-transform:uppercase}
.fg input,.fg select,.fg textarea{width:100%;padding:9px 10px;border:2px solid var(--gray-200);border-radius:var(--radius);font-size:13px;font-family:inherit;color:var(--ink);background:var(--white);transition:border-color .2s;outline:none}
.fg input:focus,.fg select:focus,.fg textarea:focus{border-color:var(--cyan)}
.hg .fg input,.hg .fg select{width:100%;padding:8px 10px;border:2px solid var(--gray-200);border-radius:var(--radius);font-size:13px;font-family:inherit;outline:none;background:var(--white)}
.hg .fg input:focus,.hg .fg select:focus{border-color:var(--cyan)}
.fg textarea{resize:vertical;min-height:60px}
.ot{display:flex;gap:12px;margin-top:12px}
.tb{flex:1;padding:10px;border:2px solid var(--gray-200);border-radius:var(--radius);text-align:center;cursor:pointer;font-size:13px;font-weight:700;background:var(--white);transition:all .2s}
.tb.safe.selected{border-color:var(--green);background:var(--green-bg);color:var(--green)}
.tb.danger.selected{border-color:var(--red);background:var(--red-bg);color:var(--red)}
.cs{background:var(--white);border-radius:var(--radius-lg);box-shadow:var(--shadow);margin-bottom:12px;overflow:hidden}
.ch{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;background:var(--gray-100);cursor:pointer;user-select:none;border-bottom:1px solid var(--gray-200);transition:background .2s}
.ch:hover{background:var(--gray-200)}.ch .ct{font-size:15px;font-weight:700;display:flex;align-items:center;gap:8px}
.cb{font-size:11px;font-weight:700;padding:3px 10px;border-radius:20px;background:var(--red-bg);color:var(--red)}.cb.as{background:var(--green-bg);color:var(--green)}
.ca{font-size:12px;transition:transform .3s;color:var(--gray-500)}.ch.open .ca{transform:rotate(180deg)}
.cbody{display:none;padding:16px 20px}.cbody.open{display:block}
.cl{display:grid;grid-template-columns:1fr 1fr;gap:6px 24px}.cl.c3{grid-template-columns:1fr 1fr 1fr}.cl.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:var(--red);cursor:pointer;flex-shrink:0}
.ci.checked label{color:var(--red);font-weight:600}.ci label{cursor:pointer;flex:1}
.ci .oi{width:100%;margin-top:4px;padding:6px 8px;border:1px solid var(--gray-200);border-radius:4px;font-size:12px;display:none}
.ci.checked .oi.visible{display:block}
.cf{display:flex;align-items:center;justify-content:space-between;padding:10px 20px;background:var(--gray-100);border-top:1px solid var(--gray-200);font-size:12px;color:var(--gray-500);font-weight:600}
.cf .tc{color:var(--red);font-weight:700}.cf .tc.zero{color:var(--green)}
.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.active{background:var(--green-bg);color:var(--green)}.ast input{display:none}
.vb{background:var(--white);border-radius:var(--radius-lg);padding:20px 24px;box-shadow:var(--shadow);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:var(--gray-500);text-transform:uppercase;margin-bottom:4px}
.vg input,.vg select{padding:7px 8px;border:1px solid var(--gray-200);border-radius:var(--radius);font-size:12px;font-family:inherit;outline:none;width:100%}
.vg input:focus,.vg select:focus{border-color:var(--cyan)}
.vrn{font-size:12px;font-weight:700;color:var(--gray-500);text-align:center;padding-top:8px}
.rvb{background:0;border:none;color:var(--red);cursor:pointer;font-size:18px;padding:4px}
.fa{display:flex;gap:10px;margin-top:20px}
.fs{background:var(--green-bg);border:1px solid var(--green);border-radius:var(--radius);padding:16px 20px;color:var(--green);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:var(--white);border-radius:var(--radius-lg);padding:20px;box-shadow:var(--shadow)}
.sc .sl{font-size:12px;font-weight:700;color:var(--gray-500);text-transform:uppercase;margin-bottom:4px}
.sc .sv{font-size:32px;font-weight:800;line-height:1}
.sc.g .sv{color:var(--green)}.sc.r .sv{color:var(--red)}.sc.b .sv{color:var(--cyan)}.sc.o .sv{color:var(--orange)}
.cg{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px;margin-bottom:16px}
.cc{background:var(--white);border-radius:var(--radius-lg);padding:20px;box-shadow:var(--shadow)}
.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 var(--gray-200);border-radius:var(--radius);font-size:13px;font-family:inherit;outline:none;background:var(--white)}
.fb select:focus,.fb input:focus{border-color:var(--cyan)}
.tw{overflow-x:auto}
.dt{width:100%;border-collapse:collapse;background:var(--white);border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow);font-size:13px}
.dt th{background:var(--ink);color:var(--white);padding:11px 14px;text-align:left;font-size:12px;font-weight:700;text-transform:uppercase}
.dt td{padding:10px 14px;border-bottom:1px solid var(--gray-100)}.dt tr:hover td{background:var(--gray-100)}
.badge{display:inline-block;padding:3px 10px;border-radius:20px;font-size:11px;font-weight:700}
.bs{background:var(--green-bg);color:var(--green)}.bd{background:var(--red-bg);color:var(--red)}.bw{background:var(--orange-bg);color:var(--orange)}
.nd{text-align:center;padding:40px 20px;color:var(--gray-500)}.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:var(--green);transition:width .5s}.ru{background:var(--red);transition:width .5s}
.rl{display:flex;justify-content:space-between;font-size:11px;color:var(--gray-500);margin-top:3px}
.vl{color:var(--cyan);cursor:pointer;font-weight:600;text-decoration:none}.vl:hover{text-decoration:underline}
@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}
.app-content{padding:16px 12px}.hg{grid-template-columns:1fr 1fr}.hg.c4{grid-template-columns:1fr 1fr}
.cl{grid-template-columns:1fr}.cl.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>
<div class="app-screen">
<header class="app-header"><div class="la"><span class="icon">🛡️</span>ПАБ Система</div>
<nav><a data-p="NA" class="active">Новый аудит</a><a data-p="MS">Мой график</a><a data-p="DB">Дашборд</a><a data-p="VL">Нарушения</a><a data-p="HS">История</a></nav>
<div class="ua"><span class="role" id="dn"></span><button class="btn btn-s btn-o" style="color:#9aa3b2;border-color:#3a4452" id="lo">Выход</button></div></header>
<div class="app-content">
<div class="sa" id="sa"><span class="at" id="sat"></span><button class="btn btn-s btn-p" id="sr">✉️ Напомнить себе</button></div>
<div id="pNA" class="panel active"><div class="page-header"><h2>📋 Бланк ПАБ</h2><p>Заполните все категории</p></div>
<div class="af"><div class="fh"><h3>📝 Данные аудита</h3>
<div class="hg c4"><div class="fg"><label>Бланк ПАБ №</label><input type="number" id="pn" min="1"></div><div class="fg"><label>Дата</label><input type="date" id="pd"></div><div class="fg"><label>Начало</label><input type="time" id="ps"></div><div class="fg"><label>Конец</label><input type="time" id="pe"></div></div>
<div class="hg" style="margin-top:12px"><div class="fg"><label>Место</label><input id="pl" placeholder="Цех, участок"></div><div class="fg"><label>Тип работы</label><input id="pw" placeholder="Ремонт линий связи"></div><div class="fg"><label>Регион</label><select id="pr"><option value="">-- Выберите --</option><option>Центральный</option><option>Алматинский</option><option>Восточный</option><option>Западный</option><option>Северный</option><option>Южный</option><option>Другое</option></select></div></div>
<div class="hg" style="margin-top:12px"><div class="fg"><label>Кол-во наблюдаемых</label><input type="number" id="pc" min="1" value="1"></div><div class="fg"></div><div class="fg"></div></div>
<div class="hg c2" style="margin-top:12px"><div class="fg"><label>ФИО наблюдателя</label><input id="po"></div><div class="fg"><label>Должность</label><input id="por"></div></div>
<div class="hg c2" style="margin-top:12px"><div class="fg"><label>ФИО руководителя</label><input id="psv"></div><div class="fg"><label>Должность</label><input id="psr"></div></div>
<div class="hg" style="margin-top:12px"><div class="fg"><label>Email</label><input type="email" id="pem" placeholder="email@telecom.kz"></div><div class="fg"></div><div class="fg"></div></div>
<div class="fg" style="margin-top:14px;margin-bottom:0"><label>Отметка БиОТ ДПБ</label><div class="ot"><div class="tb safe selected" id="os">ВСЕ БЕЗОПАСНО</div><div class="tb danger" id="od">ЕСТЬ ОПАСНО</div></div></div></div>
<div id="cats"></div>
<div class="vb"><h3>📄 Несоответствия и меры</h3><div class="vg hr" style="display:grid"><span></span><span>Несоответствие</span><span>Исполнитель</span><span>Вид</span><span>Меры</span><span>Ответственный</span><span>Дата</span><span>Завершение</span><span></span></div><div id="vr"></div><button class="btn btn-o btn-s" id="av" style="margin-top:8px">+ Добавить</button></div>
<div class="vb"><h3>💬 Итог диалога</h3><p style="font-size:12px;color:var(--gray-500);margin-bottom:14px">Результаты беседы с работником</p><div class="cl c1" id="dc"><div class="ci"><input type="checkbox" id="d0"><div><label for="d0">Работник привёл примеры безопасных действий</label></div></div><div class="ci"><input type="checkbox" id="d1"><div><label for="d1">Были обсуждены риски / проблемы</label></div></div><div class="ci"><input type="checkbox" id="d2"><div><label for="d2">Определены корректирующие меры</label></div></div><div class="ci"><input type="checkbox" id="d3"><div><label for="d3">Предложения работника зафиксированы</label></div></div><div class="ci"><input type="checkbox" id="d4"><div><label for="d4">Другое</label><input class="oi" id="do" placeholder="Укажите..."></div></div></div><div style="margin-top:14px;padding:12px;background:var(--gray-100);border-radius:var(--radius);font-size:12px;color:var(--gray-500)">📎 Фото и документы — приложите к бумажному бланку.</div></div>
<div class="fa"><button class="btn btn-p" id="sb">💾 Сохранить</button><button class="btn btn-o" id="rb">🗑️ Очистить</button></div>
<div class="fs" id="fs"><div style="font-size:18px;margin-bottom:8px">✅ Аудит сохранён!</div><div id="sd" style="font-size:13px;color:var(--ink);margin-bottom:14px"></div><div style="display:flex;gap:10px;flex-wrap:wrap"><button class="btn btn-p btn-s" id="em">✉️ На email</button><button class="btn btn-o btn-s" id="prt">🖨️ Печать</button></div></div>
</div></div>
<div id="pMS" class="panel"><div class="page-header"><h2>📅 Мой график</h2><p>Выполнение норматива</p></div><div id="msc"></div></div>
<div id="pDB" class="panel"><div class="page-header"><h2>📊 Дашборд</h2><p>Аналитика по всем аудитам</p></div>
<div class="fb"><div id="ab" style="display:none;gap:8px;flex-wrap:wrap"><button class="btn btn-p btn-s" id="d1">📥 CSV все данные</button><button class="btn btn-o btn-s" id="d2">👥 CSV работники</button><button class="btn btn-o btn-s" id="d3">📊 HTML отчёт</button><span style="color:var(--gray-200);margin:0 4px">|</span><button class="btn btn-d btn-s" id="ca">🗑️ Очистить всё</button><button class="btn btn-o btn-s" id="cpd">📅 Очистить период</button><button class="btn btn-o btn-s" id="su">👥 Пользователи</button></div></div>
<div class="fb"><select id="fr"><option value="all">Все регионы</option></select><select id="fo"><option value="all">Все области</option></select><select id="fb"><option value="all">Все филиалы</option></select><select id="fd"><option value="all">Все подразделения</option></select><select id="fc"><option value="all">Все города</option></select><span style="font-size:12px;color:var(--gray-500)">с</span><input type="date" id="df" style="width:140px"><span style="font-size:12px;color:var(--gray-500)">по</span><input type="date" id="dt" style="width:140px"></div>
<div class="sg"><div class="sc"><div class="sl">Всего аудитов</div><div class="sv" id="s1">0</div></div><div class="sc g"><div class="sl">Безопасно</div><div class="sv" id="s2">0</div></div><div class="sc r"><div class="sl">С нарушениями</div><div class="sv" id="s3">0</div></div><div class="sc b"><div class="sl">Нарушений</div><div class="sv" id="s4">0</div></div><div class="sc o"><div class="sl">Выполняют график</div><div class="sv" id="s5">0</div></div><div class="sc r"><div class="sl">Отстают</div><div class="sv" id="s6">0</div></div></div>
<div class="cc" style="margin-bottom:16px"><h3>🟢🔴 Соотношение</h3><div class="rb"><div class="rs" id="rsb" style="width:50%"></div><div class="ru" id="rub" style="width:50%"></div></div><div class="rl"><span>Безопасные: <span id="rsl">0</span></span><span>С нарушениями: <span id="rul">0</span></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>🏢 По регионам</h3><canvas id="chRegions"></canvas></div><div class="cc"><h3>🏭 По филиалам</h3><canvas id="chBranches"></canvas></div><div class="cc"><h3>👤 График по должностям</h3><canvas id="chRoles"></canvas></div><div class="cc"><h3>🔝 Топ-10 нарушений</h3><canvas id="chTop"></canvas></div></div></div>
<div id="pVL" class="panel"><div class="page-header"><h2>⚠️ Несоответствия</h2><p>Контроль исполнения мер</p></div>
<div class="sg"><div class="sc"><div class="sl">Всего</div><div class="sv" id="vt">0</div></div><div class="sc g"><div class="sl">Устранено</div><div class="sv" id="vd">0</div></div><div class="sc r"><div class="sl">Просрочено</div><div class="sv" id="vo">0</div></div><div class="sc o"><div class="sl">В работе</div><div class="sv" id="vp">0</div></div></div>
<div class="fb"><select id="vfs"><option value="all">Все</option><option value="done">Устранено</option><option value="overdue">Просрочено</option><option value="pending">В работе</option></select><select id="vft"><option value="all">Все виды</option><option>Нарушение</option><option>Замечание</option><option>Риск</option></select></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><th>Завершение</th><th>Статус</th></tr></thead><tbody id="vbdy"></tbody></table></div>
<div class="nd" id="vnd" style="display:none"><span class="icon"></span><p>Несоответствий не найдено</p></div></div>
<div id="pHS" class="panel"><div class="page-header"><h2>📁 История</h2><p>Архив аудитов</p></div>
<div class="fb"><select id="hfs"><option value="all">Все</option><option value="safe">Безопасно</option><option value="danger">С нарушениями</option></select><input type="date" id="hfd"><input type="text" id="hfl" placeholder="Поиск по месту..."><button class="btn btn-o btn-s" id="ec">📥 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"><span class="icon">📭</span><p>Нет записей</p></div></div>
</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 currentUser=U,editId=null,charts={},lastSubmitted=null,vrc=6;
function isA(){return currentUser&&currentUser.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;")}
// BUILD CATS
(function(){var h="";CATS.forEach(function(c){var col=c.items.length>7?"c3":(c.items.length<=4?"c1":"");h+="<div class=\"cs\"><div class=\"ch\" data-cat=\""+c.id+"\"><span class=\"ct\">"+c.title+"</span><span class=\"cb as\" id=\"badge-"+c.id+"\">ВСЕ БЕЗОПАСНО</span><span class=\"ca\">▼</span></div><div class=\"cbody open\" id=\"body-"+c.id+"\"><div class=\"ast active\" id=\"as-"+c.id+"\"><input type=\"checkbox\" checked id=\"ascb-"+c.id+"\">ВСЕ БЕЗОПАСНО</div><div class=\"cl "+col+"\">"+c.items.map(function(it,i){return"<div class=\"ci\" id=\"item-"+c.id+"-"+i+"\"><input type=\"checkbox\" id=\"cb-"+c.id+"-"+i+"\"><div><label for=\"cb-"+c.id+"-"+i+"\">"+it+"</label>"+(it==="Другое"?"<input class=\"oi\" id=\"other-"+c.id+"\" placeholder=\"Укажите...\">":"")+"</div></div>"}).join("")+"</div></div><div class=\"cf\"><span>Итого: <span class=\"tc zero\" id=\"total-"+c.id+"\">0</span></span></div></div>"});document.getElementById("cats").innerHTML=h})();
// CAT EVENTS
document.getElementById("cats").addEventListener("click",function(e){var h=e.target.closest(".ch");if(h){var id=h.getAttribute("data-cat");if(id)toggleCat(id);return}var as=e.target.closest(".ast");if(as){toggleAllSafe(as.id.replace("as-",""));e.preventDefault()}});
document.getElementById("cats").addEventListener("change",function(e){if(e.target.type==="checkbox"&&e.target.id&&e.target.id.indexOf("cb-")===0){var p=e.target.id.split("-");if(p.length===3)onCheckItem(p[1],parseInt(p[2]))}});
function toggleCat(id){document.getElementById("body-"+id).classList.toggle("open");var h=document.querySelector(".ch[data-cat=\""+id+"\"]");if(h)h.classList.toggle("open")}
function toggleAllSafe(id){var cb=document.getElementById("ascb-"+id),tog=document.getElementById("as-"+id);var is=!cb.checked;cb.checked=is;if(is){tog.classList.add("active");var cat=CATS.find(function(c){return c.id===id});if(cat)cat.items.forEach(function(_,i){var e=document.getElementById("cb-"+id+"-"+i);if(e){e.checked=false;e.parentElement.classList.remove("checked")}var o=document.getElementById("other-"+id);if(o){o.style.display="none";o.classList.remove("visible")}})}else{tog.classList.remove("active")}updateCatTotal(id)}
function onCheckItem(id,idx){var cb=document.getElementById("cb-"+id+"-"+idx);if(!cb)return;if(cb.checked){cb.parentElement.classList.add("checked");document.getElementById("ascb-"+id).checked=false;document.getElementById("as-"+id).classList.remove("active")}else{cb.parentElement.classList.remove("checked")}var cat=CATS.find(function(c){return c.id===id});if(cat&&cat.items[idx]==="Другое"){var o=document.getElementById("other-"+id);if(o){if(cb.checked){o.style.display="block";o.classList.add("visible")}else{o.style.display="none";o.classList.remove("visible")}}}updateCatTotal(id)}
function updateCatTotal(id){var cat=CATS.find(function(c){return c.id===id});if(!cat)return;var cnt=0;cat.items.forEach(function(_,i){if(document.getElementById("cb-"+id+"-"+i)&&document.getElementById("cb-"+id+"-"+i).checked)cnt++});var te=document.getElementById("total-"+id);if(te){te.textContent=cnt;if(cnt===0)te.classList.add("zero");else te.classList.remove("zero")}var b=document.getElementById("badge-"+id);if(b){if(cnt===0){b.textContent="ВСЕ БЕЗОПАСНО";b.classList.add("as")}else{b.textContent="Нарушений: "+cnt;b.classList.remove("as")}}}
document.getElementById("os").onclick=function(){setO("safe")};document.getElementById("od").onclick=function(){setO("danger")};
function setO(t){document.getElementById("os").classList.toggle("selected",t==="safe");document.getElementById("od").classList.toggle("selected",t==="danger")}
// VIO ROWS
function initVR(){var h="";for(var i=0;i<vrc;i++)h+=mkV(i+1);document.getElementById("vr").innerHTML=h}
function mkV(num,data){var d=data||{};return"<div class=\"vg\" id=\"vioRow"+num+"\" style=\"display:grid\"><span class=\"vrn\">"+num+"</span><input class=\"v-nc\" placeholder=\"Описание\" value=\""+esc(d.nc||"")+"\"><input class=\"v-exec\" placeholder=\"Исполнитель\" value=\""+esc(d.executor||"")+"\"><select class=\"v-type\"><option"+(d.type==="Нарушение"?" selected":"")+">Нарушение</option><option"+(d.type==="Замечание"?" selected":"")+">Замечание</option><option"+(d.type==="Риск"?" selected":"")+">Риск</option></select><input class=\"v-measure\" placeholder=\"Меры\" value=\""+esc(d.measure||"")+"\"><input class=\"v-resp\" placeholder=\"Ответственный\" value=\""+esc(d.responsible||"")+"\"><input type=\"date\" class=\"v-date\" value=\""+esc(d.date||"")+"\"><input class=\"v-done\" placeholder=\"Завершение\" value=\""+esc(d.done||"")+"\"><button class=\"rvb\" data-row=\""+num+"\">×</button></div>"}
document.getElementById("av").onclick=function(){vrc++;var d=document.getElementById("vr");d.insertAdjacentHTML("beforeend",mkV(vrc))};
document.getElementById("vr").addEventListener("click",function(e){if(e.target.classList.contains("rvb")){var n=parseInt(e.target.getAttribute("data-row"));removeVR(n)}});
function removeVR(n){if(vrc<=1)return;var r=document.getElementById("vioRow"+n);if(r)r.remove();var rs=document.querySelectorAll("#vr .vg");vrc=rs.length;rs.forEach(function(r,i){r.id="vioRow"+(i+1);r.querySelector(".vrn").textContent=i+1;var b=r.querySelector(".rvb");if(b)b.setAttribute("data-row",(i+1))})}
function getVR(){var rs=[];document.querySelectorAll("#vr .vg").forEach(function(r){var nc=r.querySelector(".v-nc").value.trim();if(!nc)return;rs.push({nc:nc,executor:r.querySelector(".v-exec").value.trim()||"",type:r.querySelector(".v-type").value||"Нарушение",measure:r.querySelector(".v-measure").value.trim()||"",responsible:r.querySelector(".v-resp").value.trim()||"",date:r.querySelector(".v-date").value||"",done:r.querySelector(".v-done").value.trim()||""})});return rs}
// DIALOGUE
var d4=document.getElementById("d4"),doEl=document.getElementById("do");d4.onchange=function(){doEl.style.display=this.checked?"block":"none";doEl.classList.toggle("visible",this.checked)};
function getDL(){var r=[];["Работник привёл примеры безопасных действий","Были обсуждены риски / проблемы","Определены корректирующие меры","Предложения работника зафиксированы","Другое"].forEach(function(l,i){var cb=document.getElementById("d"+i);if(cb&&cb.checked)r.push({item:l,other:l==="Другое"?doEl.value.trim()||"":null})});return r}
function setDL(items){for(var i=0;i<5;i++){var cb=document.getElementById("d"+i);if(cb)cb.checked=false}doEl.value="";doEl.style.display="none";doEl.classList.remove("visible");items.forEach(function(di){var idx=["Работник привёл примеры безопасных действий","Были обсуждены риски / проблемы","Определены корректирующие меры","Предложения работника зафиксированы","Другое"].indexOf(di.item);if(idx>=0){var cb=document.getElementById("d"+idx);if(cb){cb.checked=true;if(di.item==="Другое"&&di.other){doEl.value=di.other;doEl.style.display="block";doEl.classList.add("visible")}}}})}
// SUBMIT
document.getElementById("sb").onclick=function(){
if(editId&&!isA()){alert("Только администратор может редактировать");return}
var loc=document.getElementById("pl").value.trim();if(!loc){alert("Укажите место");return}
var cats={},tv=0;CATS.forEach(function(cat){var ch=[];cat.items.forEach(function(item,i){var cb=document.getElementById("cb-"+cat.id+"-"+i);if(cb&&cb.checked){var ov=item==="Другое"?(document.getElementById("other-"+cat.id).value.trim()||""):null;ch.push({item:item,other:ov})}});cats[cat.id]={items:ch,allSafe:ch.length===0};tv+=ch.length});
var e={id:editId||Date.now(),number:document.getElementById("pn").value.trim(),date:document.getElementById("pd").value,timeStart:document.getElementById("ps").value,timeEnd:document.getElementById("pe").value,location:loc,region:document.getElementById("pr").value,workType:document.getElementById("pw").value.trim(),workerCount:parseInt(document.getElementById("pc").value)||1,observer:document.getElementById("po").value.trim()||currentUser.name,observerRole:document.getElementById("por").value.trim(),supervisor:document.getElementById("psv").value.trim(),supervisorRole:document.getElementById("psr").value.trim(),email:document.getElementById("pem").value.trim()||currentUser.email||"",overallSafe:document.getElementById("os").classList.contains("selected"),categories:cats,totalViolations:tv,violations:getVR(),dialogue:getDL(),createdBy:currentUser.login,createdAt:new Date().toISOString()};
var audits=getA();if(editId){audits=audits.map(function(a){return a.id===editId?e:a});editId=null}else{audits.unshift(e)}
saveA(audits);resetF();lastSubmitted=e;showSuccess(e);if(!isA())checkSA()
};
document.getElementById("rb").onclick=function(){resetF()};
function resetF(){
document.getElementById("pn").value="";document.getElementById("pd").value=new Date().toISOString().split("T")[0];document.getElementById("ps").value="";document.getElementById("pe").value="";document.getElementById("pl").value="";document.getElementById("pr").value=currentUser.region||"";document.getElementById("pw").value="";document.getElementById("pc").value="1";document.getElementById("po").value=currentUser.name;document.getElementById("por").value="";document.getElementById("psv").value="";document.getElementById("psr").value="";document.getElementById("pem").value=currentUser.email||"";setO("safe");editId=null;
CATS.forEach(function(cat){document.getElementById("ascb-"+cat.id).checked=true;document.getElementById("as-"+cat.id).classList.add("active");cat.items.forEach(function(_,i){var cb=document.getElementById("cb-"+cat.id+"-"+i);if(cb){cb.checked=false;cb.parentElement.classList.remove("checked")}var o=document.getElementById("other-"+cat.id);if(o){o.value="";o.style.display="none";o.classList.remove("visible")}});updateCatTotal(cat.id)});
document.getElementById("vr").innerHTML="";vrc=6;initVR();document.getElementById("fs").style.display="none";setDL([])
}
function showSuccess(e){
var cv=CATS.filter(function(cat){var c=e.categories&&e.categories[cat.id];return c&&c.items.length>0}).map(function(c){return c.title}).join(", ")||"все категории безопасны";
document.getElementById("sd").innerHTML="<b>Бланк №"+(e.number||"—")+"</b> | "+e.date+" | "+(e.timeStart||"—")+"—"+(e.timeEnd||"—")+"<br>Место: "+e.location+" | Статус: <b>"+(e.overallSafe?"ВСЕ БЕЗОПАСНО":"ВЫЯВЛЕНО "+e.totalViolations+" НАРУШЕНИЙ")+"</b><br>Категории: "+cv;document.getElementById("fs").style.display="block";setTimeout(function(){document.getElementById("fs").style.display="none"},30000)
}
document.getElementById("em").onclick=function(){if(!lastSubmitted)return;var e=lastSubmitted,to=e.email||currentUser.email||"";if(!to||to.indexOf("@")<0){alert("Нет email");return}window.location.href="mailto:"+encodeURIComponent(to)+"?subject="+encodeURIComponent("ПАБ №"+(e.number||"—")+" от "+e.date)+"&body="+encodeURIComponent("ПОДТВЕРЖДЕНИЕ ПАБ\nБланк №: "+(e.number||"—")+"\nДата: "+e.date+"\nМесто: "+e.location+"\nНаблюдатель: "+e.observer+"\nСтатус: "+(e.overallSafe?"БЕЗОПАСНО":"НАРУШЕНИЙ: "+e.totalViolations)+"\n\nСистема ПАБ.")};
document.getElementById("prt").onclick=function(){if(!lastSubmitted)return;var e=lastSubmitted;var w=window.open("","_blank","width=700,height=800");w.document.write("<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>ПАБ</title><style>body{font:14px/1.5 Arial;max-width:700px;margin:40px auto;padding:20px}h1{font-size:20px}@media print{button{display:none}}</style></head><body><button onclick=\"window.print()\">🖨️ Печать</button><h1>БЛАНК ПАБ №"+(e.number||"—")+" от "+e.date+"</h1><p><b>Место:</b> "+e.location+" | <b>Наблюдатель:</b> "+e.observer+" | <b>Статус:</b> "+(e.overallSafe?"БЕЗОПАСНО":"НАРУШЕНИЙ: "+e.totalViolations)+"</p></body></html>");w.document.close()};
// NAV
function swP(n){
["NA","MS","DB","VL","HS"].forEach(function(id){document.getElementById("p"+id).classList.remove("active")});document.getElementById("p"+n).classList.add("active");
document.querySelectorAll("nav a").forEach(function(a){a.classList.toggle("active",a.getAttribute("data-p")===n)});
if(n==="DB")rd();if(n==="HS")rh();if(n==="MS")rms();if(n==="VL")rv();if(n==="NA")checkSA()
}
document.querySelectorAll("nav a").forEach(function(a){a.addEventListener("click",function(e){e.preventDefault();swP(this.getAttribute("data-p"))})});
// SCHEDULE
function checkSA(){if(!currentUser||isA()){document.getElementById("sa").classList.remove("show","danger");return}var q=gq(currentUser.role);if(!q.p)return;var p=gp(q.p);var done=getA().filter(function(a){return a.createdBy===currentUser.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");sa.classList.remove("show","danger");if(need>0){at.textContent="⚠️ Отставание ("+p.l+"): "+done+" из "+q.c+". Осталось: "+need;sa.classList.add("show");if(need>=q.c)sa.classList.add("danger")}}
document.getElementById("sr").onclick=function(){if(!currentUser)return;var q=gq(currentUser.role);var p=gp(q.p);var done=getA().filter(function(a){return a.createdBy===currentUser.login&&new Date(a.date)>=p.s}).length;var need=Math.max(0,q.c-done);var to=currentUser.email||"";if(!to||to.indexOf("@")<0){alert("Нет email");return}window.location.href="mailto:"+encodeURIComponent(to)+"?subject="+encodeURIComponent("График ПАБ — "+p.l)+"&body="+encodeURIComponent("Уважаемый(ая) "+currentUser.name+"!\n\nГрафик ПАБ: "+q.l+".\nПериод: "+p.l+".\nВыполнено: "+done+" из "+q.c+".\n"+(need>0?"Отставание: "+need+" ПАБ.":"График выполнен!")+"\n\nСистема ПАБ.")};
function rms(){if(!currentUser)return;document.getElementById("sa").classList.remove("show","danger");var q=gq(currentUser.role);var c=document.getElementById("msc");if(q.c===0){c.innerHTML="<div class=\"pc\"><h3>📅 Без графика</h3><p style=\"color:var(--gray-500);font-size:13px\">Для должности «"+currentUser.role+"» график не установлен.</p></div>";return}var p=gp(q.p);var audits=getA().filter(function(a){return a.createdBy===currentUser.login&&new Date(a.date)>=p.s});var done=audits.length;var pct=Math.min(100,Math.round(done/q.c*100));var cls="good";if(pct<50)cls="bad";else if(pct<100)cls="warn";var need=Math.max(0,q.c-done);var rh=audits.length>0?"<div style=\"margin-top:16px\"><h4 style=\"font-size:13px;margin-bottom:8px\">📋 Проведённые:</h4>"+audits.slice(0,5).map(function(a){return"<div style=\"background:var(--gray-100);padding:8px 12px;border-radius:var(--radius);margin-bottom:6px;font-size:13px\">"+a.date+" — "+a.location+" — <span class=\"badge "+(a.overallSafe?"bs":"bd")+"\">"+(a.overallSafe?"Безопасно":"Нарушений: "+a.totalViolations)+"</span></div>"}).join("")+"</div>":"";c.innerHTML="<div class=\"pc\"><h3>📅 "+p.l+"</h3><div class=\"qi\">Норматив: <b>"+q.l+"</b> | "+currentUser.role+"</div><div class=\"pb\"><div class=\"pf "+cls+"\" style=\"width:"+pct+"%\"></div></div><div class=\"ps\">Выполнено: <b>"+done+"</b> из <b>"+q.c+"</b> ("+pct+"%) "+(need>0?"— осталось: <span style=\"color:var(--red)\">"+need+"</span>":"— ✅ выполнено!")+"</div>"+(need>0?"<div style=\"margin-top:10px\"><button class=\"btn btn-p btn-s\" onclick=\"document.getElementById('sr').onclick()\">✉️ Напомнить</button></div>":"")+"</div><div class=\"pc\"><h3>👤 Данные</h3><div style=\"font-size:13px;display:grid;grid-template-columns:1fr 1fr;gap:6px\"><div><b>ФИО:</b> "+currentUser.name+"</div><div><b>Должность:</b> "+currentUser.role+"</div><div><b>Филиал:</b> "+(currentUser.branch||"—")+"</div><div><b>Подразделение:</b> "+(currentUser.dept||"—")+"</div><div><b>Регион:</b> "+(currentUser.region||"—")+"</div><div><b>Область:</b> "+(currentUser.oblast||"—")+"</div><div><b>Город:</b> "+(currentUser.city||"—")+"</div><div><b>Email:</b> "+(currentUser.email||"—")+"</div></div></div>"+rh}
// DASHBOARD
function gfa(){var all=allU();var audits=getA();var fr=document.getElementById("fr").value,fo=document.getElementById("fo").value,fb=document.getElementById("fb").value,fd=document.getElementById("fd").value,fc=document.getElementById("fc").value;var df=document.getElementById("df").value,dt=document.getElementById("dt").value;audits=audits.filter(function(a){var u=all[a.createdBy];if(!u)return true;if(fr!=="all"&&u.region!==fr)return false;if(fo!=="all"&&u.oblast!==fo)return false;if(fb!=="all"&&u.branch!==fb)return false;if(fd!=="all"&&u.dept!==fd)return false;if(fc!=="all"&&u.city!==fc)return false;return true});if(df)audits=audits.filter(function(a){return a.date>=df});if(dt)audits=audits.filter(function(a){return a.date<=dt});return audits}
function rd(){
document.getElementById("ab").style.display=isA()?"flex":"none";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||""});
function fs(id,vals){var s=document.getElementById(id);if(!s)return;var v=s.value;s.innerHTML="<option value=\"all\">Все</option>"+vals.sort().map(function(x){return"<option>"+x+"</option>"}).join("");s.value=v||"all"}
var regions=[],oblasts=[],branches=[],depts=[],cities=[];ul.forEach(function(u){if(u.region)regions.push(u.region);if(u.oblast)oblasts.push(u.oblast);if(u.branch)branches.push(u.branch);if(u.dept)depts.push(u.dept);if(u.city)cities.push(u.city)});
regions=[...new Set(regions)];oblasts=[...new Set(oblasts)];branches=[...new Set(branches)];depts=[...new Set(depts)];cities=[...new Set(cities)];
fs("fr",regions);fs("fo",oblasts);fs("fb",branches);fs("fd",depts);fs("fc",cities);
var audits=gfa();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++});
document.getElementById("s1").textContent=total;document.getElementById("s2").textContent=allSafe;document.getElementById("s3").textContent=wd;document.getElementById("s4").textContent=tv;document.getElementById("s5").textContent=ot;document.getElementById("s6").textContent=bh;
var sp=total>0?(allSafe/total*100):50,dp=total>0?(wd/total*100):50;document.getElementById("rsb").style.width=sp+"%";document.getElementById("rub").style.width=dp+"%";document.getElementById("rsl").textContent=allSafe;document.getElementById("rul").textContent=wd;
for(var ck in charts){try{charts[ck].destroy()}catch(e){}}charts={};
charts.cat=new Chart(document.getElementById("chCat"),{type:"bar",data:{labels:CATS.map(function(c){return c.title.split(". ")[1]}),datasets:[{label:"Нарушений",data: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={};audits.forEach(function(a){if(!dates[a.date])dates[a.date]=0;dates[a.date]+=(a.totalViolations||0)});var sd=Object.keys(dates).sort();
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 rc={};audits.forEach(function(a){var u=all[a.createdBy];var r=u?u.region||"Не указан":"Не указан";rc[r]=(rc[r]||0)+1});var rs=Object.entries(rc).sort(function(a,b){return b[1]-a[1]});
charts.reg=new Chart(document.getElementById("chRegions"),{type:"bar",data:{labels:rs.map(function(r){return r[0]}),datasets:[{label:"Аудитов",data:rs.map(function(r){return r[1]}),backgroundColor:["#00B4D8","#48CAE4","#90E0EF","#0077B6","#023E8A","#E63946","#E76F51","#2D6A4F"],borderRadius:6}]},options:{responsive:true,plugins:{legend:{display:false}},scales:{y:{beginAtZero:true,ticks:{stepSize:1}}}}});
var bc={};audits.forEach(function(a){var u=all[a.createdBy];var b=u?u.branch||"Не указан":"Не указан";bc[b]=(bc[b]||0)+1});var bs=Object.entries(bc).sort(function(a,b){return b[1]-a[1]}).slice(0,10);
charts.br=new Chart(document.getElementById("chBranches"),{type:"bar",data:{labels:bs.map(function(r){return r[0].length>25?r[0].slice(0,25)+"...":r[0]}),datasets:[{label:"Аудитов",data:bs.map(function(r){return r[1]}),backgroundColor:"#00B4D8",borderRadius:6}]},options:{indexAxis:"y",responsive:true,plugins:{legend:{display:false}},scales:{x:{beginAtZero:true,ticks:{stepSize:1}}}}});
var roleS={};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(!roleS[u.role])roleS[u.role]={total:0,done:0};roleS[u.role].total++;if(ua.length>=q.c)roleS[u.role].done++});var rl=Object.keys(roleS);
charts.roles=new Chart(document.getElementById("chRoles"),{type:"bar",data:{labels:rl,datasets:[{label:"Выполняют",data:rl.map(function(r){return roleS[r].done}),backgroundColor:"#2D6A4F",borderRadius:6},{label:"Отстают",data:rl.map(function(r){return roleS[r].total-roleS[r].done}),backgroundColor:"#E63946",borderRadius:6}]},options:{responsive:true,plugins:{legend:{position:"bottom"}},scales:{x:{stacked:true},y:{stacked:true,beginAtZero:true,ticks:{stepSize:1}}}}});
var ic={};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);
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}}}}});
}
["fr","fo","fb","fd","fc","df","dt"].forEach(function(id){var el=document.getElementById(id);if(el)el.addEventListener("change",function(){rd()})});
// VIOLATIONS
function rv(){var audits=gfa();var today=new Date().toISOString().split("T")[0];var fS=document.getElementById("vfs").value,fT=document.getElementById("vft").value;var allV=[];audits.forEach(function(a){if(!a.violations)return;a.violations.forEach(function(v){var dd=v.date||"",done=v.done&&v.done.trim();var st="pending";if(done)st="done";else if(dd&&dd<today)st="overdue";allV.push({nc:v.nc,executor:v.executor,type:v.type,measure:v.measure,responsible:v.responsible,date:dd,done:v.done||"",status:st,auditDate:a.date,auditNumber:a.number||"—"})})});document.getElementById("vt").textContent=allV.length;document.getElementById("vd").textContent=allV.filter(function(v){return v.status==="done"}).length;document.getElementById("vo").textContent=allV.filter(function(v){return v.status==="overdue"}).length;document.getElementById("vp").textContent=allV.filter(function(v){return v.status==="pending"}).length;if(fS!=="all")allV=allV.filter(function(v){return v.status===fS});if(fT!=="all")allV=allV.filter(function(v){return v.type===fT});var tbody=document.getElementById("vbdy"),nd=document.getElementById("vnd");if(allV.length===0){tbody.innerHTML="";nd.style.display="block";return}nd.style.display="none";tbody.innerHTML=allV.map(function(v,i){var sc=v.status==="done"?"bs":v.status==="overdue"?"bd":"bw";var sl=v.status==="done"?"Устранено":v.status==="overdue"?"Просрочено":"В работе";return"<tr><td>"+(i+1)+"</td><td style=\"max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\" title=\""+esc(v.nc)+"\">"+v.nc+"</td><td>"+v.auditDate+" (№"+v.auditNumber+")</td><td>"+v.executor+"</td><td>"+v.type+"</td><td style=\"max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\" title=\""+esc(v.measure||"")+"\">"+(v.measure||"—")+"</td><td>"+v.responsible+"</td><td>"+(v.date||"—")+"</td><td>"+(v.done||"—")+"</td><td><span class=\"badge "+sc+"\">"+sl+"</span></td></tr>"}).join("")}
["vfs","vft"].forEach(function(id){var el=document.getElementById(id);if(el)el.addEventListener("change",function(){rv()})});
// HISTORY
function rh(){var audits=getA();var fS=document.getElementById("hfs").value,fD=document.getElementById("hfd").value,fL=document.getElementById("hfl").value.toLowerCase();if(fS==="safe")audits=audits.filter(function(a){return a.overallSafe});if(fS==="danger")audits=audits.filter(function(a){return !a.overallSafe});if(fD)audits=audits.filter(function(a){return a.date===fD});if(fL)audits=audits.filter(function(a){return a.location.toLowerCase().indexOf(fL)>=0});var tbody=document.getElementById("hbdy"),nd=document.getElementById("hnd");if(audits.length===0){tbody.innerHTML="";nd.style.display="block";return}nd.style.display="none";tbody.innerHTML=audits.map(function(a){var ab=isA()?"<a class=\"vl\" data-edit=\""+a.id+"\">✏️</a><button class=\"btn btn-d btn-s\" data-del=\""+a.id+"\" style=\"margin-left:6px\">🗑️</button>":"<span style=\"color:var(--gray-500);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?"bs":"bd")+"\">"+(a.overallSafe?"Безопасно":"Нарушения")+"</span></td><td>"+(a.totalViolations||0)+"</td><td>"+ab+"</td></tr>"}).join("")}
document.getElementById("hbdy").addEventListener("click",function(e){var ed=e.target.closest("[data-edit]");if(ed){editA(parseInt(ed.getAttribute("data-edit")));return}var dl=e.target.closest("[data-del]");if(dl){delA(parseInt(dl.getAttribute("data-del")))}});
document.getElementById("ec").onclick=function(){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)};
["hfs","hfd"].forEach(function(id){var el=document.getElementById(id);if(el)el.addEventListener("change",function(){rh()})});document.getElementById("hfl").addEventListener("input",function(){rh()});
function editA(id){if(!isA()){alert("Только администратор");return}var a=getA().find(function(x){return x.id===id});if(!a)return;editId=id;document.getElementById("pn").value=a.number||"";document.getElementById("pd").value=a.date||"";document.getElementById("ps").value=a.timeStart||"";document.getElementById("pe").value=a.timeEnd||"";document.getElementById("pl").value=a.location||"";document.getElementById("pr").value=a.region||"";document.getElementById("pw").value=a.workType||"";document.getElementById("pc").value=a.workerCount||1;document.getElementById("po").value=a.observer||"";document.getElementById("por").value=a.observerRole||"";document.getElementById("psv").value=a.supervisor||"";document.getElementById("psr").value=a.supervisorRole||"";document.getElementById("pem").value=a.email||"";setO(a.overallSafe?"safe":"danger");CATS.forEach(function(cat){var cd=a.categories&&a.categories[cat.id];var it=cd?cd.items:[];it.forEach(function(i){var idx=cat.items.indexOf(i.item);if(idx>=0){var cb=document.getElementById("cb-"+cat.id+"-"+idx);if(cb){cb.checked=true;cb.parentElement.classList.add("checked")}if(i.other){var o=document.getElementById("other-"+cat.id);if(o){o.value=i.other;o.style.display="block";o.classList.add("visible")}}}});var as=it.length===0;document.getElementById("ascb-"+cat.id).checked=as;document.getElementById("as-"+cat.id).classList.toggle("active",as);updateCatTotal(cat.id)});document.getElementById("vr").innerHTML="";if(a.violations&&a.violations.length>0){vrc=a.violations.length;document.getElementById("vr").innerHTML=a.violations.map(function(v,i){return mkV(i+1,v)}).join("")}else{vrc=1;document.getElementById("vr").innerHTML=mkV(1)}setDL(a.dialogue||[]);swP("NA");window.scrollTo(0,0)}
function delA(id){if(!isA()){alert("Только администратор");return}if(!confirm("Удалить?"))return;saveA(getA().filter(function(a){return a.id!==id}));rh()}
// ADMIN
document.getElementById("d1").onclick=function(){var audits=gfa();if(audits.length===0){alert("Нет данных");return}var all=allU();var h="Бланк №;Дата;Время;Место;Тип;Наблюдатель;Должность;Руководитель;Филиал;Подразделение;Регион;Область;Город;Статус;Нарушений";var rows=audits.map(function(a){var u=all[a.createdBy]||{};var cv=CATS.filter(function(cat){var c=a.categories&&a.categories[cat.id];return c&&c.items.length>0}).map(function(c){return c.title.split(". ")[1]}).join(" | ");return(a.number||"")+";"+a.date+";"+(a.timeStart||"")+"-"+(a.timeEnd||"")+";\""+a.location+"\";\""+(a.workType||"")+"\";\""+a.observer+"\";\""+(a.observerRole||"")+"\";\""+(a.supervisor||"")+"\";\""+(u.branch||"")+"\";\""+(u.dept||"")+"\";\""+(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-full.csv";dl.click();URL.revokeObjectURL(url)};
document.getElementById("d2").onclick=function(){var audits=gfa();var all=allU();var ul=[];for(var k in all)if(k!=="admin")ul.push({login:k,name:all[k].name,role:all[k].role,branch:all[k].branch||"",dept:all[k].dept||"",region:all[k].region||"",oblast:all[k].oblast||"",city:all[k].city||"",email:all[k].email||""});if(ul.length===0){alert("Нет работников");return}var h="Логин;ФИО;Должность;Филиал;Подразделение;Регион;Область;Город;Email;График;Период;Выполнено;Нужно;Статус";var rows=ul.map(function(u){var q=gq(u.role);if(!q.p)return u.login+";\""+u.name+"\";\""+u.role+"\";\""+u.branch+"\";\""+u.dept+"\";\""+u.region+"\";\""+u.oblast+"\";\""+u.city+"\";\""+u.email+"\";Без графика;;;0;—";var p=gp(q.p);var done=audits.filter(function(a){return a.createdBy===u.login&&new Date(a.date)>=p.s}).length;var st=done>=q.c?"Выполнен":"Отстаёт ("+(q.c-done)+")";return u.login+";\""+u.name+"\";\""+u.role+"\";\""+u.branch+"\";\""+u.dept+"\";\""+u.region+"\";\""+u.oblast+"\";\""+u.city+"\";\""+u.email+"\";\""+q.l+"\";\""+p.l+"\";"+done+";"+q.c+";\""+st+"\""});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-workers.csv";dl.click();URL.revokeObjectURL(url)};
document.getElementById("d3").onclick=function(){var audits=gfa();var all=allU();var ul=[];for(var k in all)if(k!=="admin")ul.push({login:k,name:all[k].name,role:all[k].role,branch:all[k].branch||"",dept:all[k].dept||"",region:all[k].region||"",city:all[k].city||""});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){var q=gq(u.role);if(!q.p)return;var p=gp(q.p);var done=audits.filter(function(a){return a.createdBy===u.login&&new Date(a.date)>=p.s}).length;if(done>=q.c)ot++;else bh++});var wr=ul.map(function(u){var q=gq(u.role);if(!q.p)return"<tr><td>"+u.name+"</td><td>"+u.role+"</td><td>"+u.branch+"</td><td>"+u.dept+"</td><td>"+u.region+"</td><td>"+u.city+"</td><td>Без графика</td><td>—</td></tr>";var p=gp(q.p);var done=audits.filter(function(a){return a.createdBy===u.login&&new Date(a.date)>=p.s}).length;var c=done>=q.c?"green":"red";return"<tr><td>"+u.name+"</td><td>"+u.role+"</td><td>"+u.branch+"</td><td>"+u.dept+"</td><td>"+u.region+"</td><td>"+u.city+"</td><td style=\"color:"+c+"\">"+done+"/"+q.c+" ("+q.l+")</td><td>"+p.l+"</td></tr>"}).join("");var h="<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Отчёт ПАБ</title><style>body{font:14px/1.5 Arial;max-width:1000px;margin:30px auto;padding:20px;color:#0F1218}h1{font-size:24px;border-bottom:3px solid #00B4D8;padding-bottom:10px}h2{font-size:18px;margin-top:30px;color:#00B4D8}.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin:16px 0}.card{background:#F2F4F7;padding:16px;border-radius:10px;text-align:center}.card .val{font-size:28px;font-weight:800}.green{color:#2D6A4F}.red{color:#E63946}table{width:100%;border-collapse:collapse;margin:12px 0;font-size:13px}th{background:#0F1218;color:#fff;padding:10px 12px;text-align:left}td{padding:8px 12px;border-bottom:1px solid #E2E6EB}@media print{body{margin:0;padding:10px}button{display:none}}</style></head><body><button onclick=\"window.print()\" style=\"padding:10px 24px;font-size:15px;margin-bottom:16px\">🖨️ Печать</button><h1>📊 Сводный отчёт ПАБ</h1><p>Сформирован: "+new Date().toLocaleString("ru")+"</p><div class=\"cards\"><div class=\"card\"><div>Всего аудитов</div><div class=\"val\">"+total+"</div></div><div class=\"card\"><div>Безопасно</div><div class=\"val green\">"+allSafe+"</div></div><div class=\"card\"><div>С нарушениями</div><div class=\"val red\">"+wd+"</div></div><div class=\"card\"><div>Нарушений</div><div class=\"val red\">"+tv+"</div></div><div class=\"card\"><div>Выполняют график</div><div class=\"val green\">"+ot+"</div></div><div class=\"card\"><div>Отстают</div><div class=\"val red\">"+bh+"</div></div></div><h2>👥 График</h2><table><tr><th>ФИО</th><th>Должность</th><th>Филиал</th><th>Подразделение</th><th>Регион</th><th>Город</th><th>Прогресс</th><th>Период</th></tr>"+wr+"</table></body></html>";var w=window.open("","_blank","width=1000,height=800");w.document.write(h);w.document.close()};
document.getElementById("ca").onclick=function(){if(!isA())return;if(!confirm("Удалить ВСЕ аудиты?"))return;if(!confirm("Точно?"))return;localStorage.removeItem("pab_audits");alert("Удалено");rd();rh();rv()};
document.getElementById("cpd").onclick=function(){if(!isA())return;var from=prompt("С даты (ГГГГ-ММ-ДД):","");if(!from)return;var to=prompt("По дату (ГГГГ-ММ-ДД):","");var audits=getA();var before=audits.length;audits=audits.filter(function(a){if(a.date<from)return true;if(to&&a.date>to)return true;return false});if(before===audits.length){alert("Нет аудитов за период");return}if(!confirm("Удалить "+(before-audits.length)+"?"))return;saveA(audits);alert("Удалено "+(before-audits.length));rd();rh();rv()};
document.getElementById("su").onclick=function(){if(!isA())return;var all=allU();var h="<h2>👥 Пользователи</h2><table style=\"width:100%;border-collapse:collapse;font-size:13px\"><tr style=\"background:#0F1218;color:#fff\"><th>Логин</th><th>ФИО</th><th>Должность</th><th>Филиал</th><th>Подразделение</th><th>Регион</th><th>Область</th><th>Город</th><th>Email</th><th>График</th></tr>";for(var k in all){var u=all[k],q=gq(u.role);h+="<tr style=\"border-bottom:1px solid #E2E6EB\"><td>"+k+(k==="admin"?" ⭐":"")+"</td><td>"+u.name+"</td><td>"+u.role+"</td><td>"+(u.branch||"—")+"</td><td>"+(u.dept||"—")+"</td><td>"+(u.region||"—")+"</td><td>"+(u.oblast||"—")+"</td><td>"+(u.city||"—")+"</td><td>"+(u.email||"—")+"</td><td>"+q.l+"</td></tr>"}h+="</table>";var w=window.open("","_blank","width=1000,height=600");w.document.write("<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Пользователи</title><style>body{font:14px/1.5 Arial;max-width:1100px;margin:30px auto;padding:20px}@media print{button{display:none}}</style></head><body><button onclick=\"window.print()\">🖨️ Печать</button>"+h+"</body></html>");w.document.close()};
// LOGOUT
document.getElementById("lo").onclick=function(){sessionStorage.removeItem("pab_user");window.location.href="index.html"};
// INIT
document.getElementById("pd").value=new Date().toISOString().split("T")[0];
document.getElementById("po").value=currentUser.name;
document.getElementById("por").value=currentUser.role||"";
document.getElementById("pem").value=currentUser.email||"";
document.getElementById("pr").value=currentUser.region||"";
initVR();
checkSA();
</script>
</body>
</html>