Разделение: index.html=простой вход, app.html=приложение
This commit is contained in:
parent
e9c00d9406
commit
fe5aec4ba6
282
app.html
Normal file
282
app.html
Normal file
@ -0,0 +1,282 @@
|
||||
<!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="newAudit" class="active">Новый аудит</a><a data-p="mySchedule">Мой график</a><a data-p="dashboard">Дашборд</a><a data-p="violations">Нарушения</a><a data-p="history">История</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&¤tUser.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,">")}
|
||||
|
||||
// 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>
|
||||
593
index.html
593
index.html
@ -3,514 +3,107 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Поведенческий аудит безопасности (ПАБ)</title>
|
||||
<script>
|
||||
// Login logic — loaded first, guaranteed to work
|
||||
function doLoginNow(){
|
||||
var u = document.getElementById("loginUser").value.trim().toLowerCase();
|
||||
var p = document.getElementById("loginPass").value.trim();
|
||||
if (u === "admin" && p === "admin") {
|
||||
document.getElementById("loginScreen").style.display = "none";
|
||||
document.getElementById("appScreen").style.display = "block";
|
||||
return;
|
||||
}
|
||||
var users = {};
|
||||
try { users = JSON.parse(localStorage.getItem("pab_users") || "{}"); } catch(e) {}
|
||||
if (users[u] && users[u].pass === p) {
|
||||
document.getElementById("loginScreen").style.display = "none";
|
||||
document.getElementById("appScreen").style.display = "block";
|
||||
return;
|
||||
}
|
||||
document.getElementById("loginError").style.display = "block";
|
||||
}
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
||||
<title>Вход — ПАБ</title>
|
||||
<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}
|
||||
.login-screen{display:flex;align-items:flex-start;justify-content:center;min-height:100vh;background:linear-gradient(135deg,var(--ink)0%,#1a2332 100%);padding:40px 20px}
|
||||
.login-card{background:var(--white);border-radius:var(--radius-lg);padding:40px 36px;width:100%;max-width:520px;box-shadow:0 8px 40px rgba(0,0,0,.2)}
|
||||
.logo{text-align:center;margin-bottom:24px}
|
||||
.logo .icon{font-size:40px;display:block;margin-bottom:6px}
|
||||
.logo h1{font-size:20px;font-weight:800;color:var(--ink)}
|
||||
.logo p{font-size:13px;color:var(--gray-500);margin-top:2px}
|
||||
.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}
|
||||
.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)}
|
||||
.fg textarea{resize:vertical;min-height:60px}
|
||||
.fg-row{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
||||
.fg-row.col3{grid-template-columns:1fr 1fr 1fr}
|
||||
.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-primary{background:var(--cyan);color:var(--white)}.btn-primary:hover{background:var(--cyan-light)}
|
||||
.btn-danger{background:var(--red);color:var(--white)}.btn-danger:hover{background:#c1121f}
|
||||
.btn-outline{background:transparent;color:var(--ink);border:2px solid var(--gray-200)}.btn-outline:hover{border-color:var(--cyan);color:var(--cyan)}
|
||||
.btn-sm{padding:6px 14px;font-size:12px}
|
||||
.btn-block{width:100%}
|
||||
.err-msg{color:var(--red);font-size:12px;text-align:center;margin-top:10px;display:none}
|
||||
.ok-msg{background:var(--green-bg);border:1px solid var(--green);border-radius:var(--radius);padding:10px 14px;color:var(--green);font-weight:600;text-align:center;margin-top:10px;display:none}
|
||||
.login-tabs{display:flex;gap:0;margin-bottom:20px;border-radius:var(--radius);overflow:hidden;border:2px solid var(--gray-200)}
|
||||
.login-tab{flex:1;padding:8px;text-align:center;font-size:13px;font-weight:700;cursor:pointer;background:var(--white);color:var(--gray-500);transition:all .2s;border:none;font-family:inherit}
|
||||
.login-tab.active{background:var(--cyan);color:var(--white)}
|
||||
.login-tab:first-child{border-right:1px solid var(--gray-200)}
|
||||
.login-form{display:none}.login-form.active{display:block}
|
||||
.app-screen{display:none}
|
||||
.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 .logo-area{display:flex;align-items:center;gap:8px;font-weight:700;font-size:15px}
|
||||
.app-header .logo-area .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}
|
||||
.app-header nav a:hover,.app-header nav a.active{color:var(--white);background:rgba(255,255,255,.08)}
|
||||
.app-header .user-area{display:flex;align-items:center;gap:10px;font-size:13px}
|
||||
.app-header .user-area .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}
|
||||
.schedule-alert{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)}
|
||||
.schedule-alert.show{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px}
|
||||
.schedule-alert .alert-text{font-size:14px;font-weight:600;color:var(--orange)}
|
||||
.schedule-alert.danger{background:var(--red-bg);border-color:var(--red)}.schedule-alert.danger .alert-text{color:var(--red)}
|
||||
.progress-card{background:var(--white);border-radius:var(--radius-lg);padding:20px;box-shadow:var(--shadow);margin-bottom:16px}
|
||||
.progress-card h3{font-size:15px;font-weight:700;margin-bottom:4px}
|
||||
.progress-card .quota-info{font-size:12px;color:var(--gray-500);margin-bottom:10px}
|
||||
.progress-bar{height:16px;border-radius:8px;background:var(--gray-200);overflow:hidden;margin-bottom:6px}
|
||||
.progress-fill{height:100%;border-radius:8px;transition:width .5s}
|
||||
.progress-fill.good{background:var(--green)}.progress-fill.warn{background:var(--orange)}.progress-fill.bad{background:var(--red)}
|
||||
.progress-card .progress-stats{font-size:12px;font-weight:600}
|
||||
.audit-form{max-width:900px}
|
||||
.form-header{background:var(--white);border-radius:var(--radius-lg);padding:24px 28px;box-shadow:var(--shadow);margin-bottom:16px}
|
||||
.form-header h3{font-size:17px;font-weight:700;margin-bottom:16px}
|
||||
.hgrid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:14px}
|
||||
.hgrid.col2{grid-template-columns:1fr 1fr}.hgrid.col4{grid-template-columns:1fr 1fr 1fr 1fr}
|
||||
.hgrid .fg label{font-size:11px;font-weight:700;color:var(--gray-500);display:block;margin-bottom:3px;text-transform:uppercase}
|
||||
.hgrid .fg input,.hgrid .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)}
|
||||
.hgrid .fg input:focus,.hgrid .fg select:focus{border-color:var(--cyan)}
|
||||
.overall-toggle{display:flex;gap:12px;margin-top:12px}
|
||||
.toggle-btn{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}
|
||||
.toggle-btn.safe.selected{border-color:var(--green);background:var(--green-bg);color:var(--green)}
|
||||
.toggle-btn.danger.selected{border-color:var(--red);background:var(--red-bg);color:var(--red)}
|
||||
.cat-section{background:var(--white);border-radius:var(--radius-lg);box-shadow:var(--shadow);margin-bottom:12px;overflow:hidden}
|
||||
.cat-header{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}
|
||||
.cat-header:hover{background:var(--gray-200)}
|
||||
.cat-header .cat-title{font-size:15px;font-weight:700;display:flex;align-items:center;gap:8px}
|
||||
.cat-badge{font-size:11px;font-weight:700;padding:3px 10px;border-radius:20px;background:var(--red-bg);color:var(--red)}
|
||||
.cat-badge.all-safe{background:var(--green-bg);color:var(--green)}
|
||||
.cat-arrow{font-size:12px;transition:transform .3s;color:var(--gray-500)}
|
||||
.cat-header.open .cat-arrow{transform:rotate(180deg)}
|
||||
.cat-body{display:none;padding:16px 20px}.cat-body.open{display:block}
|
||||
.checklist{display:grid;grid-template-columns:1fr 1fr;gap:6px 24px}
|
||||
.checklist.col3{grid-template-columns:1fr 1fr 1fr}.checklist.col1{grid-template-columns:1fr}
|
||||
.check-item{display:flex;align-items:flex-start;gap:8px;padding:6px 0;font-size:13px;cursor:pointer}
|
||||
.check-item input[type=checkbox]{margin-top:2px;width:16px;height:16px;accent-color:var(--red);cursor:pointer;flex-shrink:0}
|
||||
.check-item.checked label{color:var(--red);font-weight:600}
|
||||
.check-item label{cursor:pointer;flex:1}
|
||||
.check-item .other-input{width:100%;margin-top:4px;padding:6px 8px;border:1px solid var(--gray-200);border-radius:4px;font-size:12px;display:none}
|
||||
.check-item.checked .other-input.visible{display:block}
|
||||
.cat-footer{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}
|
||||
.cat-footer .total-count{color:var(--red);font-weight:700}.cat-footer .total-count.zero{color:var(--green)}
|
||||
.all-safe-toggle{display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px;font-weight:700;padding:4px 12px;border-radius:20px;transition:all .2s}
|
||||
.all-safe-toggle.active{background:var(--green-bg);color:var(--green)}.all-safe-toggle input{display:none}
|
||||
.violations-block{background:var(--white);border-radius:var(--radius-lg);padding:20px 24px;box-shadow:var(--shadow);margin-top:16px}
|
||||
.violations-block h3{font-size:15px;font-weight:700;margin-bottom:14px}
|
||||
.vio-grid{display:grid;grid-template-columns:40px 1.3fr 1fr .8fr 1fr 1fr 1fr .8fr 30px;gap:6px;margin-bottom:6px;align-items:end}
|
||||
.vio-grid.header-row{font-size:11px;font-weight:700;color:var(--gray-500);text-transform:uppercase;margin-bottom:4px}
|
||||
.vio-grid input,.vio-grid select{padding:7px 8px;border:1px solid var(--gray-200);border-radius:var(--radius);font-size:12px;font-family:inherit;outline:none;width:100%}
|
||||
.vio-grid input:focus,.vio-grid select:focus{border-color:var(--cyan)}
|
||||
.vio-row-num{font-size:12px;font-weight:700;color:var(--gray-500);text-align:center;padding-top:8px}
|
||||
.remove-vio-btn{background:none;border:none;color:var(--red);cursor:pointer;font-size:18px;padding:4px}
|
||||
.form-actions{display:flex;gap:10px;margin-top:20px}
|
||||
.form-success{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}
|
||||
.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin-bottom:24px}
|
||||
.stat-card{background:var(--white);border-radius:var(--radius-lg);padding:20px;box-shadow:var(--shadow)}
|
||||
.stat-card .stat-label{font-size:12px;font-weight:700;color:var(--gray-500);text-transform:uppercase;margin-bottom:4px}
|
||||
.stat-card .stat-value{font-size:32px;font-weight:800;line-height:1}
|
||||
.stat-card.green .stat-value{color:var(--green)}.stat-card.red .stat-value{color:var(--red)}.stat-card.blue .stat-value{color:var(--cyan)}.stat-card.orange .stat-value{color:var(--orange)}
|
||||
.charts-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px;margin-bottom:16px}
|
||||
.chart-card{background:var(--white);border-radius:var(--radius-lg);padding:20px;box-shadow:var(--shadow)}
|
||||
.chart-card h3{font-size:15px;font-weight:700;margin-bottom:14px}
|
||||
.chart-card canvas{max-height:260px}
|
||||
.filter-bar{display:flex;gap:10px;margin-bottom:20px;flex-wrap:wrap;align-items:center}
|
||||
.filter-bar select,.filter-bar 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)}
|
||||
.filter-bar select:focus,.filter-bar input:focus{border-color:var(--cyan)}
|
||||
.table-wrap{overflow-x:auto}
|
||||
.data-table{width:100%;border-collapse:collapse;background:var(--white);border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow);font-size:13px}
|
||||
.data-table th{background:var(--ink);color:var(--white);padding:11px 14px;text-align:left;font-size:12px;font-weight:700;text-transform:uppercase}
|
||||
.data-table td{padding:10px 14px;border-bottom:1px solid var(--gray-100)}
|
||||
.data-table tr:hover td{background:var(--gray-100)}
|
||||
.badge{display:inline-block;padding:3px 10px;border-radius:20px;font-size:11px;font-weight:700}
|
||||
.badge-safe{background:var(--green-bg);color:var(--green)}.badge-danger{background:var(--red-bg);color:var(--red)}.badge-warn{background:var(--orange-bg);color:var(--orange)}
|
||||
.no-data{text-align:center;padding:40px 20px;color:var(--gray-500)}.no-data .icon{font-size:40px;display:block;margin-bottom:10px}
|
||||
.risk-bar{display:flex;height:20px;border-radius:10px;overflow:hidden;margin-top:6px}
|
||||
.risk-safe{background:var(--green);transition:width .5s}.risk-unsafe{background:var(--red);transition:width .5s}
|
||||
.risk-labels{display:flex;justify-content:space-between;font-size:11px;color:var(--gray-500);margin-top:3px}
|
||||
.view-link{color:var(--cyan);cursor:pointer;font-weight:600;text-decoration:none}.view-link:hover{text-decoration:underline}
|
||||
@media(max-width:768px){
|
||||
.login-card{padding:28px 20px;margin:0;max-width:100%}
|
||||
.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}
|
||||
.hgrid{grid-template-columns:1fr 1fr}.hgrid.col4{grid-template-columns:1fr 1fr}
|
||||
.checklist{grid-template-columns:1fr}.checklist.col3{grid-template-columns:1fr 1fr}
|
||||
.charts-grid{grid-template-columns:1fr}.stats-grid{grid-template-columns:1fr 1fr}
|
||||
.fg-row{grid-template-columns:1fr}.fg-row.col3{grid-template-columns:1fr 1fr}
|
||||
.vio-grid{grid-template-columns:30px 1fr 1fr;row-gap:4px}.vio-grid.header-row{display:none}
|
||||
}
|
||||
body{font:16px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;background:linear-gradient(135deg,#0F1218 0%,#1a2332 100%)}
|
||||
.card{background:#fff;padding:44px 36px;border-radius:14px;width:100%;max-width:400px;box-shadow:0 8px 40px rgba(0,0,0,0.25)}
|
||||
.logo{text-align:center;margin-bottom:28px}
|
||||
.logo .icon{font-size:44px;display:block;margin-bottom:6px}
|
||||
.logo h1{font-size:19px;font-weight:800;color:#0F1218}
|
||||
.logo p{font-size:13px;color:#5B6573;margin-top:3px}
|
||||
input{width:100%;padding:12px;margin-bottom:14px;border:2px solid #E2E6EB;border-radius:8px;font-size:14px;font-family:inherit;outline:none;transition:border-color .2s}
|
||||
input:focus{border-color:#00B4D8}
|
||||
button{width:100%;padding:12px;background:#00B4D8;color:#fff;border:none;border-radius:8px;font-size:15px;font-weight:700;cursor:pointer;font-family:inherit;transition:background .2s}
|
||||
button:hover{background:#48CAE4}
|
||||
.err{color:#E63946;font-size:13px;text-align:center;display:none;margin-top:10px}
|
||||
.info{text-align:center;margin-top:16px;font-size:12px;color:#5B6573}
|
||||
.info a{color:#00B4D8;text-decoration:none;font-weight:600;cursor:pointer}
|
||||
.info a:hover{text-decoration:underline}
|
||||
#regForm{display:none;margin-top:12px}
|
||||
#regForm.show{display:block}
|
||||
#regForm input,#regForm select{margin-bottom:10px}
|
||||
.green{background:#EDF7F0;border:1px solid #2D6A4F;color:#2D6A4F;padding:10px;border-radius:8px;font-size:13px;text-align:center;display:none;margin-top:10px;font-weight:600}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="loginScreen" class="login-screen">
|
||||
<div class="login-card">
|
||||
<div class="logo"><span class="icon">🛡️</span><h1>Поведенческий аудит безопасности</h1><p>Система учёта и аналитики ПАБ</p></div>
|
||||
<div class="login-tabs"><button class="login-tab active" id="tabLogin">Вход</button><button class="login-tab" id="tabRegister">Регистрация</button></div>
|
||||
<div id="formLogin" class="login-form active">
|
||||
<div class="fg"><label>Логин</label><input type="text" id="loginUser" placeholder="Введите логин"></div>
|
||||
<div class="fg"><label>Пароль</label><input type="password" id="loginPass" placeholder="Введите пароль"></div>
|
||||
<button class="btn btn-primary btn-block" id="loginBtn" onclick="doLoginNow()">Войти</button>
|
||||
<div class="err-msg" id="loginError">Неверный логин или пароль</div>
|
||||
<div class="card" id="loginCard">
|
||||
<div class="logo"><span class="icon">🛡️</span><h1>Поведенческий аудит безопасности</h1><p>Система ПАБ</p></div>
|
||||
<input type="text" id="loginUser" placeholder="Логин" autocomplete="username">
|
||||
<input type="password" id="loginPass" placeholder="Пароль">
|
||||
<button id="loginBtn">Войти</button>
|
||||
<div class="err" id="loginError">Неверный логин или пароль</div>
|
||||
<div class="info"><a onclick="document.getElementById('regForm').classList.toggle('show');this.textContent=document.getElementById('regForm').classList.contains('show')?'Скрыть регистрацию':'Зарегистрироваться'">Зарегистрироваться</a></div>
|
||||
<div id="regForm">
|
||||
<input type="text" id="regLogin" placeholder="Придумайте логин">
|
||||
<input type="password" id="regPass" placeholder="Придумайте пароль (мин. 3 символа)">
|
||||
<input type="text" id="regName" placeholder="ФИО">
|
||||
<input type="email" id="regEmail" placeholder="Email">
|
||||
<select id="regRole" style="width:100%;padding:10px;border:2px solid #E2E6EB;border-radius:8px;font-size:13px;margin-bottom:10px;font-family:inherit"><option value="">-- Должность --</option><option>Директор департамента ЦА</option><option>Директор департамента филиала</option><option>Региональный директор филиала</option><option>Директор ДЭСД</option><option>Начальник ТУСМ</option><option>Руководитель структурного подразделения</option><option>Начальник центра/службы/цеха</option><option>Начальник участка</option><option>Инженер БиОТ</option><option>Работник отдела БиОТ</option><option>Сотрудник</option></select>
|
||||
<input type="text" id="regBranch" placeholder="Филиал (АО «Казахтелеком»)">
|
||||
<input type="text" id="regDept" placeholder="Подразделение (ЦТО МС)">
|
||||
<input type="text" id="regRegion" placeholder="Регион (Алматинский)">
|
||||
<input type="text" id="regOblast" placeholder="Область">
|
||||
<input type="text" id="regCity" placeholder="Город / село">
|
||||
<button id="regBtn">Зарегистрироваться</button>
|
||||
<div class="err" id="regError"></div>
|
||||
<div class="green" id="regSuccess">Регистрация успешна! Теперь войдите.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="formRegister" class="login-form">
|
||||
<div class="fg-row"><div class="fg"><label>Логин</label><input type="text" id="regLogin" placeholder="ivanov"></div><div class="fg"><label>Пароль</label><input type="password" id="regPass" placeholder="Минимум 3 символа"></div></div>
|
||||
<div class="fg-row"><div class="fg"><label>ФИО</label><input type="text" id="regName" placeholder="Фамилия И.О."></div><div class="fg"><label>Email</label><input type="email" id="regEmail" placeholder="email@telecom.kz"></div></div>
|
||||
<div class="fg"><label>Должность</label><select id="regRole"><option value="">-- Выберите --</option><option>Директор департамента ЦА</option><option>Директор департамента филиала</option><option>Региональный директор филиала</option><option>Директор ДЭСД</option><option>Начальник ТУСМ</option><option>Руководитель структурного подразделения</option><option>Начальник центра/службы/цеха</option><option>Начальник участка</option><option>Инженер БиОТ</option><option>Работник отдела БиОТ</option><option>Сотрудник</option></select><div id="roleHint" style="font-size:11px;color:var(--gray-500);margin-top:4px"></div></div>
|
||||
<div class="fg-row"><div class="fg"><label>Филиал</label><input type="text" id="regBranch" placeholder="АО «Казахтелеком»"></div><div class="fg"><label>Подразделение</label><input type="text" id="regDept" placeholder="ЦТО МС"></div></div>
|
||||
<div class="fg-row"><div class="fg"><label>Регион</label><input type="text" id="regRegion" placeholder="Алматинский"></div><div class="fg"><label>Область</label><input type="text" id="regOblast" placeholder="Алматинская обл."></div></div>
|
||||
<div class="fg"><label>Город / село</label><input type="text" id="regCity" placeholder="г. Алматы"></div>
|
||||
<button class="btn btn-primary btn-block" id="regBtn">Зарегистрироваться</button>
|
||||
<div class="err-msg" id="regError"></div><div class="ok-msg" id="regSuccess">Регистрация успешна! Сейчас войдите.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="appScreen" class="app-screen">
|
||||
<header class="app-header"><div class="logo-area"><span class="icon">🛡️</span> ПАБ Система</div><nav><a href="#" data-panel="newAudit" class="active">Новый аудит</a><a href="#" data-panel="mySchedule">Мой график</a><a href="#" data-panel="dashboard">Дашборд</a><a href="#" data-panel="violations">Нарушения</a><a href="#" data-panel="history">История</a></nav><div class="user-area"><span class="role" id="displayName"></span><button class="btn btn-outline btn-sm" style="color:#9aa3b2;border-color:#3a4452" id="logoutBtn">Выход</button></div></header>
|
||||
<div class="app-content">
|
||||
<div class="schedule-alert" id="scheduleAlert"><span class="alert-text" id="scheduleAlertText"></span><button class="btn btn-primary btn-sm alert-btn" id="scheduleRemindBtn">✉️ Напомнить себе</button></div>
|
||||
|
||||
<div id="panelNewAudit" class="panel active">
|
||||
<div class="page-header"><h2>📋 Бланк поведенческого аудита безопасности</h2><p>Заполните все категории наблюдения</p></div>
|
||||
<div class="audit-form">
|
||||
<div class="form-header"><h3>📝 Данные аудита</h3>
|
||||
<div class="hgrid col4"><div class="fg"><label>Бланк ПАБ №</label><input type="number" id="pabNumber" placeholder="Номер" min="1"></div><div class="fg"><label>Дата</label><input type="date" id="pabDate"></div><div class="fg"><label>Начало</label><input type="time" id="pabTimeStart"></div><div class="fg"><label>Конец</label><input type="time" id="pabTimeEnd"></div></div>
|
||||
<div class="hgrid" style="margin-top:12px"><div class="fg"><label>Место проведения</label><input id="pabLocation" placeholder="Цех, участок"></div><div class="fg"><label>Тип работы</label><input id="pabWorkType" placeholder="Ремонт линий связи"></div><div class="fg"><label>Регион</label><select id="pabRegion"><option value="">-- Выберите --</option><option>Центральный</option><option>Алматинский</option><option>Восточный</option><option>Западный</option><option>Северный</option><option>Южный</option><option>Другое</option></select></div></div>
|
||||
<div class="hgrid" style="margin-top:12px"><div class="fg"><label>Кол-во наблюдаемых</label><input type="number" id="pabWorkerCount" min="1" value="1"></div><div class="fg"></div><div class="fg"></div></div>
|
||||
<div class="hgrid col2" style="margin-top:12px"><div class="fg"><label>ФИО наблюдателя</label><input id="pabObserver"></div><div class="fg"><label>Должность наблюдателя</label><input id="pabObserverRole"></div></div>
|
||||
<div class="hgrid col2" style="margin-top:12px"><div class="fg"><label>ФИО руководителя</label><input id="pabSupervisor"></div><div class="fg"><label>Должность руководителя</label><input id="pabSupervisorRole"></div></div>
|
||||
<div class="hgrid" style="margin-top:12px"><div class="fg"><label>Email для уведомления</label><input type="email" id="pabEmail" 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="overall-toggle"><div class="toggle-btn safe selected" id="overallSafe">✅ ВСЕ БЕЗОПАСНО</div><div class="toggle-btn danger" id="overallDanger">☐ ЕСТЬ ОПАСНО</div></div></div>
|
||||
</div>
|
||||
<div id="categorySections"></div>
|
||||
<div class="violations-block"><h3>📄 Несоответствия и корректирующие меры</h3><div class="vio-grid header-row" style="display:grid"><span>№</span><span>Несоответствие</span><span>Исполнитель</span><span>Вид</span><span>Меры</span><span>Ответственный</span><span>Дата</span><span>Завершение</span><span></span></div><div id="vioRows"></div><button class="btn btn-outline btn-sm" id="addVioBtn" style="margin-top:8px">+ Добавить строку</button></div>
|
||||
<div class="violations-block"><h3>💬 Итог диалога безопасности с работником</h3><p style="font-size:12px;color:var(--gray-500);margin-bottom:14px">Зафиксируйте конкретные результаты диалога</p><div class="checklist col1" id="dialogueChecks"><div class="check-item"><input type="checkbox" id="dcb-0"><div><label for="dcb-0">Работник привёл примеры безопасных действий</label></div></div><div class="check-item"><input type="checkbox" id="dcb-1"><div><label for="dcb-1">Были обсуждены риски / проблемы</label></div></div><div class="check-item"><input type="checkbox" id="dcb-2"><div><label for="dcb-2">Определены корректирующие меры</label></div></div><div class="check-item"><input type="checkbox" id="dcb-3"><div><label for="dcb-3">Предложения работника зафиксированы</label></div></div><div class="check-item"><input type="checkbox" id="dcb-4"><div><label for="dcb-4">Другое</label><input class="other-input" id="dialogueOther" 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="form-actions"><button class="btn btn-primary" id="submitAuditBtn">💾 Сохранить аудит</button><button class="btn btn-outline" id="resetAuditBtn">🗑️ Очистить форму</button></div>
|
||||
<div class="form-success" id="formSuccess"><div style="font-size:18px;margin-bottom:8px">✅ Аудит сохранён!</div><div id="successDetail" style="font-size:13px;color:var(--ink);margin-bottom:14px"></div><div style="display:flex;gap:10px;flex-wrap:wrap"><button class="btn btn-primary btn-sm" id="emailConfirmBtn">✉️ Отправить на email</button><button class="btn btn-outline btn-sm" id="printConfirmBtn">🖨️ Распечатать</button></div></div>
|
||||
</div></div>
|
||||
|
||||
<div id="panelMySchedule" class="panel"><div class="page-header"><h2>📅 Мой график ПАБ</h2><p>Выполнение норматива по должности</p></div><div id="myScheduleContent"></div></div>
|
||||
|
||||
<div id="panelDashboard" class="panel">
|
||||
<div class="page-header"><h2>📊 Дашборд статистики</h2><p>Аналитика по всем аудитам</p></div>
|
||||
<div class="filter-bar"><div id="adminBar" style="display:none;gap:8px;flex-wrap:wrap"><button class="btn btn-primary btn-sm" id="dlFullCsv">📥 Все данные (CSV)</button><button class="btn btn-outline btn-sm" id="dlWorkerCsv">👥 По работникам (CSV)</button><button class="btn btn-outline btn-sm" id="dlSummaryHtml">📊 Сводный отчёт (HTML)</button><span style="color:var(--gray-200);margin:0 4px">|</span><button class="btn btn-danger btn-sm" id="clearAllBtn">🗑️ Очистить всё</button><button class="btn btn-outline btn-sm" id="clearPeriodBtn">📅 Очистить период</button><button class="btn btn-outline btn-sm" id="showUsersBtn">👥 Пользователи</button></div></div>
|
||||
<div class="filter-bar"><select id="dfRegion"><option value="all">Все регионы</option></select><select id="dfOblast"><option value="all">Все области</option></select><select id="dfBranch"><option value="all">Все филиалы</option></select><select id="dfDept"><option value="all">Все подразделения</option></select><select id="dfCity"><option value="all">Все города</option></select><span style="font-size:12px;color:var(--gray-500)">с</span><input type="date" id="dfDateFrom" style="width:140px"><span style="font-size:12px;color:var(--gray-500)">по</span><input type="date" id="dfDateTo" style="width:140px"></div>
|
||||
<div class="stats-grid"><div class="stat-card"><div class="stat-label">Всего аудитов</div><div class="stat-value" id="statTotal">0</div></div><div class="stat-card green"><div class="stat-label">Безопасно</div><div class="stat-value" id="statAllSafe">0</div></div><div class="stat-card red"><div class="stat-label">С нарушениями</div><div class="stat-value" id="statWithDanger">0</div></div><div class="stat-card blue"><div class="stat-label">Нарушений</div><div class="stat-value" id="statViolations">0</div></div><div class="stat-card orange"><div class="stat-label">Выполняют график</div><div class="stat-value" id="statOnTrack">0</div></div><div class="stat-card red"><div class="stat-label">Отстают</div><div class="stat-value" id="statBehind">0</div></div></div>
|
||||
<div class="chart-card" style="margin-bottom:16px"><h3>🟢🔴 Соотношение аудитов</h3><div class="risk-bar"><div class="risk-safe" id="riskSafeBar" style="width:50%"></div><div class="risk-unsafe" id="riskUnsafeBar" style="width:50%"></div></div><div class="risk-labels"><span>Безопасные: <span id="riskSafeLabel">0</span></span><span>С нарушениями: <span id="riskUnsafeLabel">0</span></span></div></div>
|
||||
<div class="charts-grid"><div class="chart-card"><h3>📂 Нарушения по категориям</h3><canvas id="chartCategories"></canvas></div><div class="chart-card"><h3>📅 Динамика по датам</h3><canvas id="chartTimeline"></canvas></div><div class="chart-card"><h3>🏢 По регионам</h3><canvas id="chartRegions"></canvas></div><div class="chart-card"><h3>🏭 По филиалам</h3><canvas id="chartBranches"></canvas></div><div class="chart-card"><h3>👤 График по должностям</h3><canvas id="chartRoles"></canvas></div><div class="chart-card"><h3>🔝 Топ-10 нарушений</h3><canvas id="chartTopItems"></canvas></div></div>
|
||||
</div>
|
||||
|
||||
<div id="panelViolations" class="panel">
|
||||
<div class="page-header"><h2>⚠️ Отслеживание несоответствий</h2><p>Контроль исполнения корректирующих мер</p></div>
|
||||
<div class="stats-grid"><div class="stat-card"><div class="stat-label">Всего несоответствий</div><div class="stat-value" id="vTotal">0</div></div><div class="stat-card green"><div class="stat-label">Устранено</div><div class="stat-value" id="vDone">0</div></div><div class="stat-card red"><div class="stat-label">Просрочено</div><div class="stat-value" id="vOverdue">0</div></div><div class="stat-card orange"><div class="stat-label">В работе</div><div class="stat-value" id="vPending">0</div></div></div>
|
||||
<div class="filter-bar"><select id="vfStatus"><option value="all">Все</option><option value="done">Устранено</option><option value="overdue">Просрочено</option><option value="pending">В работе</option></select><select id="vfType"><option value="all">Все виды</option><option>Нарушение</option><option>Замечание</option><option>Риск</option></select></div>
|
||||
<div class="table-wrap"><table class="data-table"><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="violationsBody"></tbody></table></div>
|
||||
<div class="no-data" id="vioNoData" style="display:none"><span class="icon">✅</span><p>Несоответствий не найдено</p></div>
|
||||
</div>
|
||||
|
||||
<div id="panelHistory" class="panel">
|
||||
<div class="page-header"><h2>📁 История аудитов</h2><p>Архив всех проведённых ПАБ</p></div>
|
||||
<div class="filter-bar"><select id="filterOverall"><option value="all">Все</option><option value="safe">Безопасно</option><option value="danger">С нарушениями</option></select><input type="date" id="filterDate"><input type="text" id="filterLocation" placeholder="Поиск по месту..."><button class="btn btn-outline btn-sm" id="exportCsvBtn">📥 Экспорт CSV</button></div>
|
||||
<div class="table-wrap"><table class="data-table"><thead><tr><th>Бланк №</th><th>Дата</th><th>Время</th><th>Место</th><th>Наблюдатель</th><th>Статус</th><th>Нарушений</th><th></th></tr></thead><tbody id="historyBody"></tbody></table></div>
|
||||
<div class="no-data" id="noDataRow" style="display:none"><span class="icon">📭</span><p>Нет записей</p></div>
|
||||
</div>
|
||||
|
||||
</div></div>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
// ===== DATA =====
|
||||
var PREDEFINED = {admin:{pass:"admin",name:"Администратор",role:"Руководитель",email:"admin@telecom.kz",branch:"АО «Казахтелеком»",dept:"ЦА",region:"Центральный",oblast:"—",city:"г. Астана"}};
|
||||
var PAB_QUOTA={"Директор департамента ЦА":{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 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 currentUser=null,currentPanel="newAudit",editId=null,charts={},lastSubmitted=null,vioRowCount=6;
|
||||
|
||||
// ===== HELPERS =====
|
||||
function isAdmin(){return currentUser&¤tUser.login==="admin"}
|
||||
function getUsers(){try{return JSON.parse(localStorage.getItem("pab_users")||"{}")}catch(e){return{}}}
|
||||
function saveUsers(d){localStorage.setItem("pab_users",JSON.stringify(d))}
|
||||
function allUsers(){var r=getUsers();for(var k in PREDEFINED)r[k]=PREDEFINED[k];return r}
|
||||
function getAudits(){try{return JSON.parse(localStorage.getItem("pab_audits")||"[]")}catch(e){return[]}}
|
||||
function saveAudits(d){localStorage.setItem("pab_audits",JSON.stringify(d))}
|
||||
function getQuota(r){return PAB_QUOTA[r]||PAB_QUOTA["Сотрудник"]}
|
||||
function getPeriod(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,">")}
|
||||
|
||||
// ===== LOGIN =====
|
||||
function switchLoginTab(t){
|
||||
var login=document.getElementById("formLogin"),reg=document.getElementById("formRegister");
|
||||
var tl=document.getElementById("tabLogin"),tr=document.getElementById("tabRegister");
|
||||
if(t==="login"){login.classList.add("active");reg.classList.remove("active");tl.classList.add("active");tr.classList.remove("active")}
|
||||
else{login.classList.remove("active");reg.classList.add("active");tl.classList.remove("active");tr.classList.add("active")}
|
||||
document.getElementById("loginError").style.display="none";document.getElementById("regError").style.display="none";document.getElementById("regSuccess").style.display="none";
|
||||
}
|
||||
function doLogin(){
|
||||
var u=document.getElementById("loginUser").value.trim().toLowerCase();
|
||||
var p=document.getElementById("loginPass").value.trim();
|
||||
var err=document.getElementById("loginError");
|
||||
var all=allUsers();
|
||||
if(!all[u]||all[u].pass!==p){err.style.display="block";return}
|
||||
err.style.display="none";currentUser={login:u,name:all[u].name,role:all[u].role,email:all[u].email||"",branch:all[u].branch||"",dept:all[u].dept||"",region:all[u].region||"",oblast:all[u].oblast||"",city:all[u].city||""};
|
||||
localStorage.setItem("pab_current",JSON.stringify(currentUser));
|
||||
showApp();
|
||||
}
|
||||
function doRegister(){
|
||||
var login=document.getElementById("regLogin").value.trim().toLowerCase();
|
||||
var pass=document.getElementById("regPass").value.trim();
|
||||
var name=document.getElementById("regName").value.trim();
|
||||
var email=document.getElementById("regEmail").value.trim();
|
||||
var role=document.getElementById("regRole").value;
|
||||
var branch=document.getElementById("regBranch").value.trim();
|
||||
var dept=document.getElementById("regDept").value.trim();
|
||||
var region=document.getElementById("regRegion").value.trim();
|
||||
var oblast=document.getElementById("regOblast").value.trim();
|
||||
var city=document.getElementById("regCity").value.trim();
|
||||
var err=document.getElementById("regError"),ok=document.getElementById("regSuccess");ok.style.display="none";
|
||||
if(!login||login.length<2){err.textContent="Логин минимум 2 символа";err.style.display="block";return}
|
||||
if(!pass||pass.length<3){err.textContent="Пароль минимум 3 символа";err.style.display="block";return}
|
||||
if(!name){err.textContent="Укажите ФИО";err.style.display="block";return}
|
||||
if(!role){err.textContent="Выберите должность";err.style.display="block";return}
|
||||
if(!email||email.indexOf("@")<0){err.textContent="Укажите Email";err.style.display="block";return}
|
||||
if(!branch){err.textContent="Укажите филиал";err.style.display="block";return}
|
||||
if(!region){err.textContent="Укажите регион";err.style.display="block";return}
|
||||
if(!city){err.textContent="Укажите город";err.style.display="block";return}
|
||||
if(!dept){err.textContent="Укажите подразделение";err.style.display="block";return}
|
||||
var all=allUsers();if(all[login]){err.textContent="Логин занят";err.style.display="block";return}
|
||||
err.style.display="none";
|
||||
var users=getUsers();users[login]={pass:pass,name:name,email:email,role:role,branch:branch,dept:dept,region:region,oblast:oblast,city:city};saveUsers(users);
|
||||
ok.style.display="block";document.getElementById("regLogin").value="";document.getElementById("regPass").value="";document.getElementById("regName").value="";document.getElementById("regEmail").value="";document.getElementById("regBranch").value="";document.getElementById("regDept").value="";document.getElementById("regRegion").value="";document.getElementById("regOblast").value="";document.getElementById("regCity").value="";
|
||||
document.getElementById("loginUser").value=login;
|
||||
setTimeout(function(){switchLoginTab("login");ok.style.display="none"},2000);
|
||||
}
|
||||
function doLogout(){localStorage.removeItem("pab_current");currentUser=null;document.getElementById("loginScreen").style.display="flex";document.getElementById("appScreen").style.display="none";document.getElementById("scheduleAlert").classList.remove("show","danger");document.getElementById("loginUser").value="";document.getElementById("loginPass").value=""}
|
||||
function showApp(){document.getElementById("loginScreen").style.display="none";document.getElementById("appScreen").style.display="block";document.getElementById("displayName").textContent=currentUser.login+" ("+currentUser.role+")";document.getElementById("pabObserver").value=currentUser.name;document.getElementById("pabEmail").value=currentUser.email||"";document.getElementById("pabObserverRole").value=currentUser.role||"";document.getElementById("pabRegion").value=currentUser.region||"";switchPanel("newAudit");checkScheduleAlert()}
|
||||
|
||||
// ===== SCHEDULE =====
|
||||
function checkScheduleAlert(){if(!currentUser||isAdmin()){document.getElementById("scheduleAlert").classList.remove("show","danger");return}var q=getQuota(currentUser.role);if(!q.p)return;var p=getPeriod(q.p);var audits=getAudits().filter(function(a){return a.createdBy===currentUser.login&&new Date(a.date)>=p.s});var done=audits.length,need=Math.max(0,q.c-done);var alert=document.getElementById("scheduleAlert"),text=document.getElementById("scheduleAlertText");alert.classList.remove("show","danger");if(need>0){text.textContent="⚠️ Отставание от графика ("+p.l+"): "+done+" из "+q.c+" ("+q.l+"). Осталось: "+need;alert.classList.add("show");if(need>=q.c)alert.classList.add("danger")}}
|
||||
function sendScheduleReminder(){if(!currentUser)return;var q=getQuota(currentUser.role);var p=getPeriod(q.p);var audits=getAudits().filter(function(a){return a.createdBy===currentUser.login&&new Date(a.date)>=p.s});var done=audits.length,need=Math.max(0,q.c-done);var to=currentUser.email||"";if(!to||to.indexOf("@")<0){alert("Не указан email");return}var subj="Напоминание: график ПАБ — "+p.l;var body="Уважаемый(ая) "+currentUser.name+"!\n\nПо графику ПАБ необходимо провести "+(need>0?need:0)+" наблюдений до конца периода: "+p.l+".\nНорматив: "+q.l+".\nВыполнено: "+done+" из "+q.c+".\n"+(need>0?"Вы отстаёте на "+need+" ПАБ.":"График выполнен!")+"\n\nСистема ПАБ.";window.location.href="mailto:"+encodeURIComponent(to)+"?subject="+encodeURIComponent(subj)+"&body="+encodeURIComponent(body)}
|
||||
|
||||
// ===== CATEGORY UI =====
|
||||
function buildCategorySections(){var c=document.getElementById("categorySections");var h="";CATS.forEach(function(cat){var col=(cat.items.length>7?"col3":(cat.items.length<=4?"col1":""));h+="<div class=\"cat-section\"><div class=\"cat-header\" data-cat=\""+cat.id+"\"><span class=\"cat-title\">"+cat.title+"</span><span class=\"cat-badge all-safe\" id=\"badge-"+cat.id+"\">ВСЕ БЕЗОПАСНО</span><span class=\"cat-arrow\">▼</span></div><div class=\"cat-body open\" id=\"body-"+cat.id+"\"><div class=\"all-safe-toggle active\" id=\"as-"+cat.id+"\"><input type=\"checkbox\" checked id=\"ascb-"+cat.id+"\"> ВСЕ БЕЗОПАСНО</div><div class=\"checklist "+col+"\">"+cat.items.map(function(item,i){return"<div class=\"check-item\" id=\"item-"+cat.id+"-"+i+"\"><input type=\"checkbox\" id=\"cb-"+cat.id+"-"+i+"\"><div><label for=\"cb-"+cat.id+"-"+i+"\">"+item+"</label>"+(item==="Другое"?"<input class=\"other-input\" id=\"other-"+cat.id+"\" placeholder=\"Укажите...\">":"")+"</div></div>"}).join("")+"</div></div><div class=\"cat-footer\"><span>Итого: <span class=\"total-count zero\" id=\"total-"+cat.id+"\">0</span></span></div></div>"});c.innerHTML=h}
|
||||
function toggleCat(id){document.getElementById("body-"+id).classList.toggle("open");var h=document.querySelector("#cat-"+id+" .cat-header");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("all-safe")}else{b.textContent="Нарушений: "+cnt;b.classList.remove("all-safe")}}}
|
||||
function setOverall(t){document.getElementById("overallSafe").classList.toggle("selected",t==="safe");document.getElementById("overallDanger").classList.toggle("selected",t==="danger")}
|
||||
|
||||
// ===== VIO ROWS =====
|
||||
function initVioRows(){var h="";for(var i=0;i<vioRowCount;i++)h+=makeVioRow(i+1);document.getElementById("vioRows").innerHTML=h}
|
||||
function makeVioRow(num,data){var d=data||{};return"<div class=\"vio-grid\" id=\"vioRow"+num+"\" style=\"display:grid\"><span class=\"vio-row-num\">"+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=\"remove-vio-btn\" data-row=\""+num+"\" title=\"Удалить\">×</button></div>"}
|
||||
function addVioRow(){vioRowCount++;var d=document.getElementById("vioRows");d.insertAdjacentHTML("beforeend",makeVioRow(vioRowCount));d.lastElementChild.querySelector(".remove-vio-btn").addEventListener("click",function(){removeVioRow(this.getAttribute("data-row"))})}
|
||||
function removeVioRow(num){if(vioRowCount<=1)return;var r=document.getElementById("vioRow"+num);if(r)r.remove();var rows=document.querySelectorAll("#vioRows .vio-grid");vioRowCount=rows.length;rows.forEach(function(row,i){row.id="vioRow"+(i+1);row.querySelector(".vio-row-num").textContent=i+1;var btn=row.querySelector(".remove-vio-btn");if(btn)btn.setAttribute("data-row",(i+1))})}
|
||||
function getVioRows(){var rows=[];document.querySelectorAll("#vioRows .vio-grid").forEach(function(r){var nc=r.querySelector(".v-nc").value.trim();if(!nc)return;rows.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 rows}
|
||||
|
||||
// ===== DIALOGUE =====
|
||||
function getDialogue(){var items=[];var labels=["Работник привёл примеры безопасных действий","Были обсуждены риски / проблемы","Определены корректирующие меры","Предложения работника зафиксированы","Другое"];labels.forEach(function(l,i){if(document.getElementById("dcb-"+i)&&document.getElementById("dcb-"+i).checked){items.push({item:l,other:l==="Другое"?(document.getElementById("dialogueOther").value.trim()||""):null})}});return items}
|
||||
function setDialogue(items){for(var i=0;i<5;i++){var cb=document.getElementById("dcb-"+i);if(cb)cb.checked=false}var o=document.getElementById("dialogueOther");if(o){o.value="";o.style.display="none";o.classList.remove("visible")}items.forEach(function(di){var idx=["Работник привёл примеры безопасных действий","Были обсуждены риски / проблемы","Определены корректирующие меры","Предложения работника зафиксированы","Другое"].indexOf(di.item);if(idx>=0){var cb=document.getElementById("dcb-"+idx);if(cb){cb.checked=true;if(di.item==="Другое"&&di.other){var oo=document.getElementById("dialogueOther");oo.value=di.other;oo.style.display="block";oo.classList.add("visible")}}}})}
|
||||
|
||||
// ===== AUDIT SUBMIT =====
|
||||
function submitAudit(){
|
||||
if(editId&&!isAdmin()){alert("Только администратор может редактировать");return}
|
||||
var loc=document.getElementById("pabLocation").value.trim();if(!loc){alert("Укажите место проведения");return}
|
||||
var cats={};var 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 overallSafe=document.getElementById("overallSafe").classList.contains("selected");
|
||||
var entry={id:editId||Date.now(),number:document.getElementById("pabNumber").value.trim(),date:document.getElementById("pabDate").value,timeStart:document.getElementById("pabTimeStart").value,timeEnd:document.getElementById("pabTimeEnd").value,location:loc,region:document.getElementById("pabRegion").value,workType:document.getElementById("pabWorkType").value.trim(),workerCount:parseInt(document.getElementById("pabWorkerCount").value)||1,observer:document.getElementById("pabObserver").value.trim()||currentUser.name,observerRole:document.getElementById("pabObserverRole").value.trim(),supervisor:document.getElementById("pabSupervisor").value.trim(),supervisorRole:document.getElementById("pabSupervisorRole").value.trim(),email:document.getElementById("pabEmail").value.trim()||currentUser.email||"",overallSafe:overallSafe,categories:cats,totalViolations:tv,violations:getVioRows(),dialogue:getDialogue(),createdBy:currentUser.login,createdAt:new Date().toISOString()};
|
||||
var audits=getAudits();if(editId){audits=audits.map(function(a){return a.id===editId?entry:a});editId=null}else{audits.unshift(entry)}
|
||||
saveAudits(audits);resetAuditForm();lastSubmitted=entry;showSuccess(entry);if(!isAdmin())checkScheduleAlert();
|
||||
}
|
||||
function resetAuditForm(){
|
||||
document.getElementById("pabNumber").value="";document.getElementById("pabDate").value=new Date().toISOString().split("T")[0];document.getElementById("pabTimeStart").value="";document.getElementById("pabTimeEnd").value="";document.getElementById("pabLocation").value="";document.getElementById("pabRegion").value=currentUser?currentUser.region||"":"";document.getElementById("pabWorkType").value="";document.getElementById("pabWorkerCount").value="1";document.getElementById("pabObserver").value=currentUser?currentUser.name:"";document.getElementById("pabObserverRole").value="";document.getElementById("pabSupervisor").value="";document.getElementById("pabSupervisorRole").value="";document.getElementById("pabEmail").value=currentUser?currentUser.email||"":"";setOverall("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("vioRows").innerHTML="";vioRowCount=6;initVioRows();document.getElementById("formSuccess").style.display="none";
|
||||
for(var i=0;i<5;i++){var cb=document.getElementById("dcb-"+i);if(cb)cb.checked=false}var o=document.getElementById("dialogueOther");if(o){o.value="";o.style.display="none";o.classList.remove("visible")}
|
||||
}
|
||||
function showSuccess(entry){
|
||||
var cv=CATS.filter(function(cat){var c=entry.categories&&entry.categories[cat.id];return c&&c.items.length>0}).map(function(c){return c.title}).join(", ")||"все категории безопасны";
|
||||
document.getElementById("successDetail").innerHTML="<b>Бланк №"+(entry.number||"—")+"</b> | "+entry.date+" | "+(entry.timeStart||"—")+"—"+(entry.timeEnd||"—")+"<br>Место: "+entry.location+" | Статус: <b>"+(entry.overallSafe?"ВСЕ БЕЗОПАСНО":"ВЫЯВЛЕНО "+entry.totalViolations+" НАРУШЕНИЙ")+"</b><br>Категории с нарушениями: "+cv;document.getElementById("formSuccess").style.display="block";setTimeout(function(){document.getElementById("formSuccess").style.display="none"},30000)
|
||||
}
|
||||
function sendEmailConfirm(){
|
||||
if(!lastSubmitted)return;var e=lastSubmitted;var to=e.email||currentUser.email||"";if(!to||to.indexOf("@")<0){alert("Не указан email");return}var subj="ПАБ №"+(e.number||"—")+" от "+e.date;var body="ПОДТВЕРЖДЕНИЕ ПАБ\nБланк №: "+(e.number||"—")+"\nДата: "+e.date+"\nМесто: "+e.location+"\nНаблюдатель: "+e.observer+"\nСтатус: "+(e.overallSafe?"БЕЗОПАСНО":"НАРУШЕНИЙ: "+e.totalViolations)+"\n\nСистема ПАБ.";window.location.href="mailto:"+encodeURIComponent(to)+"?subject="+encodeURIComponent(subj)+"&body="+encodeURIComponent(body)
|
||||
}
|
||||
function printConfirm(){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>ПАБ №"+(e.number||"—")+"</title><style>body{font:14px/1.5 Arial;max-width:700px;margin:40px auto;padding:20px}h1{font-size:20px}h2{font-size:16px;border-bottom:1px solid #ccc}table{width:100%;border-collapse:collapse;margin:12px 0}td{padding:6px 8px;border:1px solid #ddd}@media print{body{margin:0;padding:10px}button{display:none}}</style></head><body><button onclick=\"window.print()\" style=\"padding:8px 20px;font-size:16px;margin-bottom:20px\">🖨️ Печать</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()}
|
||||
|
||||
// ===== MY SCHEDULE =====
|
||||
function renderMySchedule(){
|
||||
if(!currentUser)return;document.getElementById("scheduleAlert").classList.remove("show","danger");var q=getQuota(currentUser.role);var c=document.getElementById("myScheduleContent");if(q.c===0){c.innerHTML="<div class=\"progress-card\"><h3>📅 Без обязательного графика</h3><p style=\"color:var(--gray-500);font-size:13px\">Для должности «"+currentUser.role+"» график не установлен.</p></div>";return}
|
||||
var p=getPeriod(q.p);var audits=getAudits().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="";if(audits.length>0){rh="<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?"badge-safe":"badge-danger")+"\">"+(a.overallSafe?"Безопасно":"Нарушений: "+a.totalViolations)+"</span></div>"}).join("")+"</div>"}
|
||||
c.innerHTML="<div class=\"progress-card\"><h3>📅 "+p.l+"</h3><div class=\"quota-info\">Норматив: <b>"+q.l+"</b> | Должность: "+currentUser.role+"</div><div class=\"progress-bar\"><div class=\"progress-fill "+cls+"\" style=\"width:"+pct+"%\"></div></div><div class=\"progress-stats\">Выполнено: <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-primary btn-sm\" id=\"remindBtn2\">✉️ Напомнить себе</button></div>":"")+"</div><div class=\"progress-card\"><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;
|
||||
}
|
||||
|
||||
// ===== NAVIGATION =====
|
||||
function switchPanel(name){
|
||||
currentPanel=name;document.querySelectorAll(".panel").forEach(function(p){p.classList.remove("active")});var panel=document.getElementById(name==="newAudit"?"panelNewAudit":name==="mySchedule"?"panelMySchedule":name==="dashboard"?"panelDashboard":name==="violations"?"panelViolations":"panelHistory");if(panel)panel.classList.add("active");document.querySelectorAll("nav a").forEach(function(a){a.classList.toggle("active",a.getAttribute("data-panel")===name)});
|
||||
if(name==="dashboard")renderDashboard();if(name==="history")renderHistory();if(name==="mySchedule")renderMySchedule();if(name==="violations")renderViolations();
|
||||
}
|
||||
|
||||
// ===== DASHBOARD =====
|
||||
function getFilteredAudits(){
|
||||
var all=allUsers();var audits=getAudits();var fr=document.getElementById("dfRegion").value,fo=document.getElementById("dfOblast").value,fb=document.getElementById("dfBranch").value,fd=document.getElementById("dfDept").value,fc=document.getElementById("dfCity").value;var df=document.getElementById("dfDateFrom").value,dt=document.getElementById("dfDateTo").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 renderDashboard(){
|
||||
document.getElementById("adminBar").style.display=isAdmin()?"flex":"none";var all=allUsers();var userList=[];for(var k in all)userList.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 fillSelect(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=[];userList.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)];
|
||||
fillSelect("dfRegion",regions);fillSelect("dfOblast",oblasts);fillSelect("dfBranch",branches);fillSelect("dfDept",depts);fillSelect("dfCity",cities);
|
||||
var audits=getFilteredAudits();var total=audits.length;var allSafe=audits.filter(function(a){return a.overallSafe}).length;var withDanger=audits.filter(function(a){return !a.overallSafe}).length;var totalVio=audits.reduce(function(s,a){return s+(a.totalViolations||0)},0);
|
||||
var onTrack=0,behind=0;userList.forEach(function(u){if(u.login==="admin")return;var q=getQuota(u.role);if(!q.p)return;var p=getPeriod(q.p);var ua=getAudits().filter(function(a){return a.createdBy===u.login&&new Date(a.date)>=p.s});if(ua.length>=q.c)onTrack++;else behind++});
|
||||
document.getElementById("statTotal").textContent=total;document.getElementById("statAllSafe").textContent=allSafe;document.getElementById("statWithDanger").textContent=withDanger;document.getElementById("statViolations").textContent=totalVio;document.getElementById("statOnTrack").textContent=onTrack;document.getElementById("statBehind").textContent=behind;
|
||||
var sp=total>0?(allSafe/total*100):50,dp=total>0?(withDanger/total*100):50;document.getElementById("riskSafeBar").style.width=sp+"%";document.getElementById("riskUnsafeBar").style.width=dp+"%";document.getElementById("riskSafeLabel").textContent=allSafe;document.getElementById("riskUnsafeLabel").textContent=withDanger;
|
||||
for(var ck in charts){try{charts[ck].destroy()}catch(e){}}charts={};
|
||||
var ctx1=document.getElementById("chartCategories").getContext("2d");charts.cat=new Chart(ctx1,{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();
|
||||
var ctx2=document.getElementById("chartTimeline").getContext("2d");charts.tl=new Chart(ctx2,{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]});
|
||||
var ctx3=document.getElementById("chartRegions").getContext("2d");charts.reg=new Chart(ctx3,{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);
|
||||
var ctx4=document.getElementById("chartBranches").getContext("2d");charts.br=new Chart(ctx4,{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={};userList.forEach(function(u){if(u.login==="admin")return;var q=getQuota(u.role);if(!q.p)return;var p=getPeriod(q.p);var ua=getAudits().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);
|
||||
var ctx5=document.getElementById("chartRoles").getContext("2d");charts.roles=new Chart(ctx5,{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);
|
||||
var ctx6=document.getElementById("chartTopItems").getContext("2d");charts.top=new Chart(ctx6,{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}}}}});
|
||||
}
|
||||
|
||||
// ===== VIOLATIONS =====
|
||||
function renderViolations(){
|
||||
var all=allUsers();var audits=getFilteredAudits();var today=new Date().toISOString().split("T")[0];var fStatus=document.getElementById("vfStatus").value,fType=document.getElementById("vfType").value;
|
||||
var allVios=[];audits.forEach(function(a){if(!a.violations)return;var u=all[a.createdBy]||{};a.violations.forEach(function(v,i){var dd=v.date||"",done=v.done&&v.done.trim();var st="pending";if(done)st="done";else if(dd&&dd<today)st="overdue";allVios.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("vTotal").textContent=allVios.length;document.getElementById("vDone").textContent=allVios.filter(function(v){return v.status==="done"}).length;document.getElementById("vOverdue").textContent=allVios.filter(function(v){return v.status==="overdue"}).length;document.getElementById("vPending").textContent=allVios.filter(function(v){return v.status==="pending"}).length;
|
||||
if(fStatus!=="all")allVios=allVios.filter(function(v){return v.status===fStatus});if(fType!=="all")allVios=allVios.filter(function(v){return v.type===fType});
|
||||
var tbody=document.getElementById("violationsBody"),nd=document.getElementById("vioNoData");if(allVios.length===0){tbody.innerHTML="";nd.style.display="block";return}nd.style.display="none";
|
||||
tbody.innerHTML=allVios.map(function(v,i){var sc=v.status==="done"?"badge-safe":v.status==="overdue"?"badge-danger":"badge-warn";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("")
|
||||
}
|
||||
|
||||
// ===== HISTORY =====
|
||||
function renderHistory(){
|
||||
var audits=getAudits();var fOverall=document.getElementById("filterOverall").value,fDate=document.getElementById("filterDate").value,fLoc=document.getElementById("filterLocation").value.toLowerCase();if(fOverall==="safe")audits=audits.filter(function(a){return a.overallSafe});if(fOverall==="danger")audits=audits.filter(function(a){return !a.overallSafe});if(fDate)audits=audits.filter(function(a){return a.date===fDate});if(fLoc)audits=audits.filter(function(a){return a.location.toLowerCase().indexOf(fLoc)>=0});
|
||||
var tbody=document.getElementById("historyBody"),nd=document.getElementById("noDataRow");if(audits.length===0){tbody.innerHTML="";nd.style.display="block";return}nd.style.display="none";
|
||||
tbody.innerHTML=audits.map(function(a){var ab=isAdmin()?"<a class=\"view-link\" id=\"edit-"+a.id+"\">✏️</a><button class=\"btn btn-danger btn-sm\" id=\"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?"badge-safe":"badge-danger")+"\">"+(a.overallSafe?"Безопасно":"Нарушения")+"</span></td><td>"+(a.totalViolations||0)+"</td><td>"+ab+"</td></tr>"}).join("")
|
||||
}
|
||||
function editAudit(id){if(!isAdmin()){alert("Только администратор");return}var a=getAudits().find(function(x){return x.id===id});if(!a)return;editId=id;document.getElementById("pabNumber").value=a.number||"";document.getElementById("pabDate").value=a.date||"";document.getElementById("pabTimeStart").value=a.timeStart||"";document.getElementById("pabTimeEnd").value=a.timeEnd||"";document.getElementById("pabLocation").value=a.location||"";document.getElementById("pabRegion").value=a.region||"";document.getElementById("pabWorkType").value=a.workType||"";document.getElementById("pabWorkerCount").value=a.workerCount||1;document.getElementById("pabObserver").value=a.observer||"";document.getElementById("pabObserverRole").value=a.observerRole||"";document.getElementById("pabSupervisor").value=a.supervisor||"";document.getElementById("pabSupervisorRole").value=a.supervisorRole||"";document.getElementById("pabEmail").value=a.email||"";setOverall(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("vioRows").innerHTML="";if(a.violations&&a.violations.length>0){vioRowCount=a.violations.length;document.getElementById("vioRows").innerHTML=a.violations.map(function(v,i){return makeVioRow(i+1,v)}).join("")}else{vioRowCount=1;document.getElementById("vioRows").innerHTML=makeVioRow(1)}setDialogue(a.dialogue||[]);switchPanel("newAudit");window.scrollTo(0,0)}
|
||||
function deleteAudit(id){if(!isAdmin()){alert("Только администратор");return}if(!confirm("Удалить?"))return;saveAudits(getAudits().filter(function(a){return a.id!==id}));renderHistory()}
|
||||
|
||||
// ===== ADMIN =====
|
||||
function clearAllAudits(){if(!isAdmin())return;if(!confirm("Удалить ВСЕ аудиты?"))return;if(!confirm("Точно?"))return;localStorage.removeItem("pab_audits");alert("Удалено");renderDashboard();renderHistory();renderViolations()}
|
||||
function clearAuditsByDate(){if(!isAdmin())return;var from=prompt("С даты (ГГГГ-ММ-ДД):","");if(!from)return;var to=prompt("По дату (ГГГГ-ММ-ДД):","");var audits=getAudits();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;saveAudits(audits);alert("Удалено "+(before-audits.length));renderDashboard();renderHistory();renderViolations()}
|
||||
function showAllUsers(){if(!isAdmin())return;var all=allUsers();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=getQuota(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}table{width:100%;border-collapse:collapse}td{padding:8px 10px}@media print{body{margin:0;padding:10px}}</style></head><body><button onclick=\"window.print()\" style=\"padding:8px 20px;font-size:14px;margin-bottom:16px\">🖨️ Печать</button>"+h+"</body></html>");w.document.close()}
|
||||
|
||||
// ===== CSV EXPORTS =====
|
||||
function exportCSV(){var audits=getAudits();if(audits.length===0){alert("Нет данных");return}var all=allUsers();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-audit.csv";dl.click();URL.revokeObjectURL(url)}
|
||||
function downloadFullCSV(){var audits=getFilteredAudits();if(audits.length===0){alert("Нет данных");return}var all=allUsers();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-report.csv";dl.click();URL.revokeObjectURL(url)}
|
||||
function downloadWorkerReport(){var audits=getFilteredAudits();var all=allUsers();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=getQuota(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=getPeriod(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-worker-report.csv";dl.click();URL.revokeObjectURL(url)}
|
||||
function downloadSummaryHTML(){var audits=getFilteredAudits();var all=allUsers();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,withDanger=audits.filter(function(a){return !a.overallSafe}).length,totalVio=audits.reduce(function(s,a){return s+(a.totalViolations||0)},0);var onTrack=0,behind=0;ul.forEach(function(u){var q=getQuota(u.role);if(!q.p)return;var p=getPeriod(q.p);var done=audits.filter(function(a){return a.createdBy===u.login&&new Date(a.date)>=p.s}).length;if(done>=q.c)onTrack++;else behind++});var wr=ul.map(function(u){var q=getQuota(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=getPeriod(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\">"+withDanger+"</div></div><div class=\"card\"><div>Нарушений</div><div class=\"val red\">"+totalVio+"</div></div><div class=\"card\"><div>Выполняют график</div><div class=\"val green\">"+onTrack+"</div></div><div class=\"card\"><div>Отстают</div><div class=\"val red\">"+behind+"</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()}
|
||||
|
||||
// ===== INIT =====
|
||||
function init(){
|
||||
// LOGIN - using onclick like the test page
|
||||
var loginBtn = document.getElementById("loginBtn");
|
||||
if (loginBtn) {
|
||||
loginBtn.onclick = function(){
|
||||
var u = document.getElementById("loginUser").value.trim().toLowerCase();
|
||||
var p = document.getElementById("loginPass").value.trim();
|
||||
var err = document.getElementById("loginError");
|
||||
var all = allUsers();
|
||||
if (!all[u] || all[u].pass !== p) { err.style.display = "block"; return; }
|
||||
err.style.display = "none";
|
||||
currentUser = {login: u, name: all[u].name, role: all[u].role, email: all[u].email || "", branch: all[u].branch || "", dept: all[u].dept || "", region: all[u].region || "", oblast: all[u].oblast || "", city: all[u].city || ""};
|
||||
localStorage.setItem("pab_current", JSON.stringify(currentUser));
|
||||
showApp();
|
||||
};
|
||||
document.getElementById('loginBtn').onclick = function(){
|
||||
var u = document.getElementById('loginUser').value.trim().toLowerCase();
|
||||
var p = document.getElementById('loginPass').value.trim();
|
||||
var err = document.getElementById('loginError');
|
||||
if (u === 'admin' && p === 'admin') {
|
||||
sessionStorage.setItem('pab_user', JSON.stringify({login:'admin',name:'Администратор',role:'Руководитель',email:'admin@telecom.kz',branch:'АО «Казахтелеком»',dept:'ЦА',region:'Центральный',oblast:'—',city:'г. Астана'}));
|
||||
window.location.href = 'app.html';
|
||||
return;
|
||||
}
|
||||
document.getElementById("regBtn").addEventListener("click",function(){doRegister()});
|
||||
document.getElementById("loginUser").addEventListener("keydown",function(e){if(e.key==="Enter")doLogin()});
|
||||
document.getElementById("loginPass").addEventListener("keydown",function(e){if(e.key==="Enter")doLogin()});
|
||||
document.getElementById("logoutBtn").addEventListener("click",function(){doLogout()});
|
||||
document.getElementById("regRole").addEventListener("change",function(){var r=document.getElementById("regRole").value;var q=getQuota(r);document.getElementById("roleHint").textContent=q.c>0?"📅 График: "+q.l:"📅 Без графика"});
|
||||
|
||||
// Tab switching
|
||||
document.getElementById("tabLogin").addEventListener("click",function(){switchLoginTab("login")});
|
||||
document.getElementById("tabRegister").addEventListener("click",function(){switchLoginTab("register")});
|
||||
|
||||
// Register
|
||||
document.getElementById("regBtn").addEventListener("click",function(){doRegister()});
|
||||
|
||||
// Audit form listeners
|
||||
document.getElementById("submitAuditBtn").addEventListener("click",function(){submitAudit()});
|
||||
document.getElementById("resetAuditBtn").addEventListener("click",function(){resetAuditForm()});
|
||||
document.getElementById("emailConfirmBtn").addEventListener("click",function(){sendEmailConfirm()});
|
||||
document.getElementById("printConfirmBtn").addEventListener("click",function(){printConfirm()});
|
||||
document.getElementById("addVioBtn").addEventListener("click",function(){addVioRow()});
|
||||
document.getElementById("overallSafe").addEventListener("click",function(){setOverall("safe")});
|
||||
document.getElementById("overallDanger").addEventListener("click",function(){setOverall("danger")});
|
||||
document.getElementById("scheduleRemindBtn").addEventListener("click",function(){sendScheduleReminder()});
|
||||
|
||||
// Dialogue checkboxes
|
||||
for(var i=0;i<5;i++){(function(idx){var cb=document.getElementById("dcb-"+idx);if(cb)cb.addEventListener("change",function(){if(idx===4){var o=document.getElementById("dialogueOther");if(o){o.style.display=this.checked?"block":"none";o.classList.toggle("visible",this.checked)}}})})(i)}
|
||||
|
||||
// Dashboard filter listeners
|
||||
["dfRegion","dfOblast","dfBranch","dfDept","dfCity","dfDateFrom","dfDateTo"].forEach(function(id){var el=document.getElementById(id);if(el)el.addEventListener("change",function(){renderDashboard()})});
|
||||
|
||||
// Violations filter listeners
|
||||
["vfStatus","vfType"].forEach(function(id){var el=document.getElementById(id);if(el)el.addEventListener("change",function(){renderViolations()})});
|
||||
|
||||
// History filter listeners
|
||||
["filterOverall","filterDate","filterLocation"].forEach(function(id){var el=document.getElementById(id);if(el)el.addEventListener("input",function(){renderHistory()});else{var el2=document.getElementById(id);if(el2)el2.addEventListener("change",function(){renderHistory()})}});
|
||||
document.getElementById("exportCsvBtn").addEventListener("click",function(){exportCSV()});
|
||||
|
||||
// Admin buttons
|
||||
document.getElementById("dlFullCsv").addEventListener("click",function(){downloadFullCSV()});
|
||||
document.getElementById("dlWorkerCsv").addEventListener("click",function(){downloadWorkerReport()});
|
||||
document.getElementById("dlSummaryHtml").addEventListener("click",function(){downloadSummaryHTML()});
|
||||
document.getElementById("clearAllBtn").addEventListener("click",function(){clearAllAudits()});
|
||||
document.getElementById("clearPeriodBtn").addEventListener("click",function(){clearAuditsByDate()});
|
||||
document.getElementById("showUsersBtn").addEventListener("click",function(){showAllUsers()});
|
||||
|
||||
// Nav listeners
|
||||
document.querySelectorAll("nav a").forEach(function(a){a.addEventListener("click",function(e){e.preventDefault();var panel=this.getAttribute("data-panel");if(panel)switchPanel(panel)})});
|
||||
|
||||
// Category header toggle listeners (delegated)
|
||||
document.getElementById("categorySections").addEventListener("click",function(e){var h=e.target.closest(".cat-header");if(h){var id=h.getAttribute("data-cat");if(id)toggleCat(id);return}var as=e.target.closest(".all-safe-toggle");if(as){var id=as.id.replace("as-","");toggleAllSafe(id);return}});
|
||||
|
||||
// Checkbox listeners for categories (delegated)
|
||||
document.getElementById("categorySections").addEventListener("change",function(e){if(e.target.type==="checkbox"&&e.target.id&&e.target.id.indexOf("cb-")===0){var parts=e.target.id.split("-");if(parts.length===3)onCheckItem(parts[1],parseInt(parts[2]))}});
|
||||
|
||||
// Vio row remove (delegated)
|
||||
document.getElementById("vioRows").addEventListener("click",function(e){if(e.target.classList.contains("remove-vio-btn")){var row=e.target.getAttribute("data-row");if(row)removeVioRow(parseInt(row))}});
|
||||
|
||||
// History edit/delete (delegated)
|
||||
document.getElementById("historyBody").addEventListener("click",function(e){var ed=e.target.closest("a.view-link");if(ed){var id=parseInt(ed.id.replace("edit-",""));if(id)editAudit(id);return}var dl=e.target.closest("button.btn-danger");if(dl){var id=parseInt(dl.id.replace("del-",""));if(id)deleteAudit(id)}});
|
||||
|
||||
// Init data
|
||||
document.getElementById("pabDate").value=new Date().toISOString().split("T")[0];
|
||||
buildCategorySections();
|
||||
initVioRows();
|
||||
|
||||
// Restore session
|
||||
var saved=localStorage.getItem("pab_current");if(saved){try{currentUser=JSON.parse(saved);showApp()}catch(e){localStorage.removeItem("pab_current")}}
|
||||
}
|
||||
init();
|
||||
var users = {};
|
||||
try { users = JSON.parse(localStorage.getItem('pab_users') || '{}'); } catch(e) {}
|
||||
if (users[u] && users[u].pass === p) {
|
||||
sessionStorage.setItem('pab_user', JSON.stringify({login:u,name:users[u].name,role:users[u].role,email:users[u].email||'',branch:users[u].branch||'',dept:users[u].dept||'',region:users[u].region||'',oblast:users[u].oblast||'',city:users[u].city||''}));
|
||||
window.location.href = 'app.html';
|
||||
return;
|
||||
}
|
||||
err.style.display = 'block';
|
||||
};
|
||||
document.getElementById('loginPass').onkeydown = function(e){ if(e.key==='Enter') document.getElementById('loginBtn').onclick(); };
|
||||
document.getElementById('regBtn').onclick = function(){
|
||||
var login = document.getElementById('regLogin').value.trim().toLowerCase();
|
||||
var pass = document.getElementById('regPass').value.trim();
|
||||
var name = document.getElementById('regName').value.trim();
|
||||
var email = document.getElementById('regEmail').value.trim();
|
||||
var role = document.getElementById('regRole').value;
|
||||
var branch = document.getElementById('regBranch').value.trim();
|
||||
var dept = document.getElementById('regDept').value.trim();
|
||||
var region = document.getElementById('regRegion').value.trim();
|
||||
var oblast = document.getElementById('regOblast').value.trim();
|
||||
var city = document.getElementById('regCity').value.trim();
|
||||
var err = document.getElementById('regError'), ok = document.getElementById('regSuccess'); ok.style.display = 'none';
|
||||
if (!login || login.length < 2) { err.textContent = 'Логин минимум 2 символа'; err.style.display = 'block'; return; }
|
||||
if (!pass || pass.length < 3) { err.textContent = 'Пароль минимум 3 символа'; err.style.display = 'block'; return; }
|
||||
if (!name) { err.textContent = 'Укажите ФИО'; err.style.display = 'block'; return; }
|
||||
if (!role) { err.textContent = 'Выберите должность'; err.style.display = 'block'; return; }
|
||||
if (!email || email.indexOf('@') < 0) { err.textContent = 'Укажите Email'; err.style.display = 'block'; return; }
|
||||
if (!branch) { err.textContent = 'Укажите филиал'; err.style.display = 'block'; return; }
|
||||
if (!region) { err.textContent = 'Укажите регион'; err.style.display = 'block'; return; }
|
||||
if (!city) { err.textContent = 'Укажите город'; err.style.display = 'block'; return; }
|
||||
if (!dept) { err.textContent = 'Укажите подразделение'; err.style.display = 'block'; return; }
|
||||
var users = {};
|
||||
try { users = JSON.parse(localStorage.getItem('pab_users') || '{}'); } catch(e) {}
|
||||
if (users[login]) { err.textContent = 'Логин занят'; err.style.display = 'block'; return; }
|
||||
err.style.display = 'none';
|
||||
users[login] = {pass:pass,name:name,email:email,role:role,branch:branch,dept:dept,region:region,oblast:oblast,city:city};
|
||||
localStorage.setItem('pab_users', JSON.stringify(users));
|
||||
ok.style.display = 'block';
|
||||
document.getElementById('loginUser').value = login;
|
||||
document.getElementById('regLogin').value = ''; document.getElementById('regPass').value = ''; document.getElementById('regName').value = ''; document.getElementById('regEmail').value = '';
|
||||
document.getElementById('regBranch').value = ''; document.getElementById('regDept').value = ''; document.getElementById('regRegion').value = ''; document.getElementById('regOblast').value = ''; document.getElementById('regCity').value = '';
|
||||
document.getElementById('regForm').classList.remove('show');
|
||||
setTimeout(function(){ ok.style.display = 'none'; }, 3000);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user