259 lines
43 KiB
HTML
259 lines
43 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>ПАБ — Система</title>
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
||
<style>
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
body{font:15px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:#0F1218;background:#F2F4F7;min-height:100vh}
|
||
.ah{background:#0F1218;color:#fff;padding:0 24px;display:flex;align-items:center;justify-content:space-between;height:56px;position:sticky;top:0;z-index:100}
|
||
.ah nav a{color:#9aa3b2;text-decoration:none;padding:7px 14px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer}.ah nav a.ac,.ah nav a:hover{color:#fff;background:rgba(255,255,255,.08)}
|
||
.acont{max-width:1140px;margin:0 auto;padding:24px}
|
||
.pn{display:none}.pn.ac{display:block}
|
||
.ph{margin-bottom:20px}.ph h2{font-size:26px;font-weight:800}
|
||
.card{background:#fff;border-radius:14px;padding:20px;box-shadow:0 2px 12px rgba(0,0,0,.06);margin-bottom:14px}
|
||
.card h3{font-size:16px;font-weight:700;margin-bottom:10px}
|
||
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px;margin-bottom:14px}
|
||
.st{background:#fff;border-radius:14px;padding:16px;box-shadow:0 2px 12px rgba(0,0,0,.06);text-align:center}
|
||
.st .n{font-size:30px;font-weight:800}.st .l{font-size:11px;color:#5B6573;text-transform:uppercase;margin-top:4px}
|
||
.gr .n{color:#2D6A4F}.rd .n{color:#E63946}.bl .n{color:#00B4D8}
|
||
.btn{display:inline-flex;align-items:center;gap:6px;padding:9px 18px;border-radius:8px;font-size:13px;font-weight:700;border:none;cursor:pointer;font-family:inherit}
|
||
.bp{background:#00B4D8;color:#fff}.bo{background:transparent;border:2px solid #E2E6EB;color:#0F1218}.bd{background:#E63946;color:#fff}
|
||
table{width:100%;border-collapse:collapse;font-size:13px;background:#fff;border-radius:14px;overflow:hidden}
|
||
th{background:#0F1218;color:#fff;padding:10px 12px;text-align:left;font-size:11px;text-transform:uppercase}
|
||
td{padding:8px 12px;border-bottom:1px solid #F2F4F7}tr:hover td{background:#F2F4F7}
|
||
.badge{display:inline-block;padding:2px 8px;border-radius:20px;font-size:10px;font-weight:700}
|
||
.bs{background:#EDF7F0;color:#2D6A4F}.bd2{background:#FFEBED;color:#E63946}.bw{background:#FFF3EF;color:#E76F51}
|
||
.fg{margin-bottom:12px}.fg label{display:block;font-size:11px;font-weight:700;color:#5B6573;margin-bottom:3px;text-transform:uppercase}
|
||
.fg input,.fg select,.fg textarea{width:100%;padding:9px 10px;border:2px solid #E2E6EB;border-radius:8px;font-size:13px;font-family:inherit;outline:none}
|
||
.fg input:focus,.fg select:focus,.fg textarea:focus{border-color:#00B4D8}
|
||
.fg textarea{resize:vertical;min-height:60px}
|
||
.hg{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px}.hg.c2{grid-template-columns:1fr 1fr}
|
||
.ci{display:flex;align-items:flex-start;gap:8px;padding:4px 0;font-size:13px}
|
||
.ci input[type=checkbox]{margin-top:2px;width:15px;height:15px;accent-color:#E63946;cursor:pointer;flex-shrink:0}
|
||
.ci.ck label{color:#E63946;font-weight:600}
|
||
.ot{display:flex;gap:10px;margin-top:10px}
|
||
.tb{flex:1;padding:10px;border:2px solid #E2E6EB;border-radius:8px;text-align:center;cursor:pointer;font-size:13px;font-weight:700}
|
||
.tb.sf{background:#EDF7F0;border-color:#2D6A4F;color:#2D6A4F}
|
||
.tb.df{background:#FFEBED;border-color:#E63946;color:#E63946}
|
||
.fs{background:#EDF7F0;border:2px solid #2D6A4F;border-radius:8px;padding:20px;color:#2D6A4F;font-weight:600;margin-top:14px;display:none;text-align:center}
|
||
@media(max-width:768px){.ah{padding:0 10px;flex-wrap:wrap;height:auto;padding-top:8px}.ah nav{width:100%;overflow-x:auto}.acont{padding:12px}.hg{grid-template-columns:1fr 1fr}.stats{grid-template-columns:1fr 1fr}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header class="ah"><div style="font-weight:700">🛡️ ПАБ Система</div><nav><a onclick="showPanel('NA')" class="ac">Новый аудит</a><a onclick="showPanel('MS')">Мой график</a><a onclick="showPanel('DB')">Дашборд</a><a onclick="showPanel('VL')">Нарушения</a><a onclick="showPanel('HS')">История</a></nav><span style="font-size:13px"><span id="dn" style="color:#48CAE4;font-weight:600"></span> <button class="btn bo" style="color:#9aa3b2;border-color:#3a4452;font-size:12px;padding:4px 10px" onclick="doLogout()">Выход</button></span></header>
|
||
<div class="acont">
|
||
<div id="sa" style="background:#FFF3EF;border:1px solid #E76F51;border-radius:14px;padding:16px 20px;margin-bottom:20px;display:none" class=""><span id="sat" style="font-size:14px;font-weight:600;color:#E76F51"></span> <a onclick="sendScheduleReminder()" style="color:#00B4D8;cursor:pointer;font-weight:600;text-decoration:underline;margin-left:8px;white-space:nowrap">✉️ Напомнить</a></div>
|
||
<div id="pnNA" class="pn ac">
|
||
<div class="ph"><h2>📋 Бланк ПАБ</h2></div>
|
||
<div class="card"><h3>📝 Данные аудита</h3>
|
||
<div class="hg"><div class="fg"><label>Бланк №</label><input type="number" id="pn"></div><div class="fg"><label>Дата</label><input type="date" id="pd"></div><div class="fg"><label>Регион</label><select id="pr"><option value="">--</option><option>Центральный</option><option>Алматинский</option><option>Восточный</option><option>Западный</option><option>Северный</option><option>Южный</option></select></div></div>
|
||
<div class="hg"><div class="fg"><label>Область</label><input id="pob"></div><div class="fg"><label>Город / село</label><input id="pct"></div><div class="fg"></div></div>
|
||
<div class="hg"><div class="fg"><label>Время начала</label><input type="time" id="ps"></div><div class="fg"><label>Время конца</label><input type="time" id="pe"></div><div class="fg"></div></div>
|
||
<div class="hg"><div class="fg"><label>Место</label><input id="pl"></div><div class="fg"><label>Тип работы</label><input id="pw"></div><div class="fg"><label>Кол-во наблюдаемых</label><input type="number" id="pc" value="1"></div></div>
|
||
<div class="hg c2"><div class="fg"><label>ФИО наблюдателя</label><input id="po"></div><div class="fg"><label>Должность наблюдателя</label><input id="por"></div></div>
|
||
<div class="hg c2"><div class="fg"><label>ФИО руководителя работ</label><input id="psv"></div><div class="fg"><label>Должность руководителя</label><input id="psr"></div></div>
|
||
<div class="fg"><label>Отметка</label><div class="ot"><div class="tb sf" id="os" onclick="setO('safe')">✅ ВСЕ БЕЗОПАСНО</div><div class="tb" id="od" onclick="setO('danger')">⚠️ ЕСТЬ ОПАСНО</div></div></div>
|
||
<div class="fg" style="margin-top:10px"><label>📎 Прикрепить фото</label><input type="file" id="pfiles" multiple accept="image/*" onchange="var n=[];for(var i=0;i<this.files.length;i++)n.push(this.files[i].name);document.getElementById('fn').textContent=n.length?'📷 '+n.join(', '):''"><div id="fn" style="font-size:12px;color:#5B6573;margin-top:4px"></div></div></div>
|
||
<div class="card"><h3>📄 Категории наблюдения</h3><div id="cats"></div></div>
|
||
<div class="card"><h3>💬 Итог диалога</h3>
|
||
<div class="ci"><input type="checkbox" id="d0"><label for="d0">Работник привёл примеры безопасных действий</label></div>
|
||
<div class="ci"><input type="checkbox" id="d1"><label for="d1">Были обсуждены риски / проблемы</label></div>
|
||
<div class="ci"><input type="checkbox" id="d2"><label for="d2">Определены корректирующие меры</label></div>
|
||
<div class="ci"><input type="checkbox" id="d3"><label for="d3">Предложения работника зафиксированы</label></div>
|
||
</div>
|
||
<div class="card"><h3>📄 Несоответствия и корректирующие меры</h3>
|
||
<div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse;font-size:12px"><thead><tr style="background:#0F1218;color:#fff"><th>№</th><th>Несоответствие</th><th>Исполнитель</th><th>Вид нарушения</th><th>Меры</th><th>Ответственный</th><th>Срок</th><th>Завершение</th><th></th></tr></thead><tbody id="vioBody"><tr id="vioRow1"><td>1</td><td><input class="v-nc" placeholder="Описание" style="width:100%;padding:3px;border:1px solid #E2E6EB;border-radius:4px;font-size:12px"></td><td><input class="v-ex" placeholder="Исполнитель" style="width:100%;padding:3px;border:1px solid #E2E6EB;border-radius:4px;font-size:12px"></td><td><select class="v-tp" style="width:100%;padding:3px;border:1px solid #E2E6EB;border-radius:4px;font-size:12px"><option>Нарушение</option><option>Замечание</option><option>Риск</option></select></td><td><input class="v-ms" placeholder="Меры" style="width:100%;padding:3px;border:1px solid #E2E6EB;border-radius:4px;font-size:12px"></td><td><input class="v-rs" placeholder="Ответственный" style="width:100%;padding:3px;border:1px solid #E2E6EB;border-radius:4px;font-size:12px"></td><td><input type="date" class="v-dt" style="width:100%;padding:3px;border:1px solid #E2E6EB;border-radius:4px;font-size:12px"></td><td><input class="v-fn" placeholder="Завершение" style="width:100%;padding:3px;border:1px solid #E2E6EB;border-radius:4px;font-size:12px"></td><td><button onclick="removeVioRow(this)" style="background:none;border:none;color:#E63946;cursor:pointer;font-size:16px">×</button></td></tr></tbody></table></div>
|
||
<button class="btn bo bs" onclick="addVioRowFn()" style="margin-top:6px">+ Добавить строку</button></div>
|
||
<button class="btn bp" onclick="submitAudit()" style="margin-right:10px">💾 Сохранить аудит</button><button class="btn bo" onclick="resetF()">🗑️ Очистить</button>
|
||
<div class="fs" id="fs"><div style="font-size:24px">✅</div><div style="font-size:16px;font-weight:800">Аудит успешно отправлен!</div><div id="sd" style="font-size:13px;color:#0F1218;margin-top:8px"></div></div>
|
||
</div>
|
||
|
||
<div id="pnMS" class="pn">
|
||
<div class="ph"><h2>📅 Мой график</h2></div><div id="msc">Загрузка...</div>
|
||
</div>
|
||
|
||
<div id="pnDB" class="pn">
|
||
<div class="ph"><h2>📊 Дашборд</h2></div><div id="dbc">Загрузка...</div>
|
||
</div>
|
||
|
||
<div id="pnVL" class="pn">
|
||
<div class="ph"><h2>⚠️ Несоответствия</h2></div><div id="vlc">Загрузка...</div>
|
||
</div>
|
||
|
||
<div id="pnHS" class="pn">
|
||
<div class="ph"><h2>📁 История</h2></div>
|
||
<button class="btn bo" onclick="exportCSV()" style="margin-bottom:10px">📥 CSV</button><button class="btn bo" onclick="exportData()" style="margin-bottom:10px;margin-left:6px">📤 Экспорт</button>
|
||
<table><thead><tr><th>Бланк</th><th>Дата</th><th>Место</th><th>Область</th><th>Город</th><th>Наблюдатель</th><th>Статус</th><th>Нарушений</th><th></th></tr></thead><tbody id="hbd"><tr><td colspan="9" style="text-align:center;padding:20px;color:#5B6573">Нет записей</td></tr></tbody></table>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
var U,editId,lastSubmitted,vrc=6;
|
||
try{U=JSON.parse(sessionStorage.getItem("pab_user"));if(!U)location.href="index.html"}catch(e){location.href="index.html"}
|
||
document.getElementById("dn").textContent=U.login;
|
||
function isA(){return U&&U.login==="admin"}
|
||
function getU(){try{return JSON.parse(localStorage.getItem("pab_users")||"{}")}catch(e){return{}}}
|
||
function allU(){var r=getU();r.admin={pass:"admin",name:"Администратор",role:"Руководитель",email:"admin@telecom.kz",branch:"АО «Казахтелеком»",dept:"ЦА",region:"Центральный",oblast:"—",city:"г. Астана"};return r}
|
||
function getA(){try{return JSON.parse(localStorage.getItem("pab_audits")||"[]")}catch(e){return[]}}
|
||
function saveA(d){localStorage.setItem("pab_audits",JSON.stringify(d))}
|
||
function saveU(d){localStorage.setItem("pab_users",JSON.stringify(d))}
|
||
|
||
// Supabase — фоновая синхронизация, не трогает вкладки
|
||
function sbSync(){sbPullUsers();sbPullAudits()}
|
||
function sbPullUsers(){fetch(SBU+"/rest/v1/users?select=*",{headers:{"apikey":SBK,"Authorization":"Bearer "+SBK}}).then(function(r){return r.json()}).then(function(d){var um=getU();d.forEach(function(x){if(!um[x.login])um[x.login]={pass:x.pass,name:x.name,email:x.email,role:x.role,freq:x.freq,branch:x.branch,dept:x.dept,region:x.region,oblast:x.oblast,city:x.city}});saveU(um)}).catch(function(){})}
|
||
function sbPullAudits(){fetch(SBU+"/rest/v1/audits?select=*&order=created_at.desc",{headers:{"apikey":SBK,"Authorization":"Bearer "+SBK}}).then(function(r){return r.json()}).then(function(d){var am=[],ca=getA();d.forEach(function(x){am.push({id:x.id,number:x.number,date:x.date,location:x.location,region:x.region,workType:x.work_type,workerCount:x.worker_count,observer:x.observer,observerRole:x.observer_role,overallSafe:x.overall_safe,categories:x.categories,totalViolations:x.total_violations,dialogue:x.dialogue,photos:x.photos,docs:x.docs,createdBy:x.created_by,createdAt:x.created_at})});ca.forEach(function(a){var found=false;am.forEach(function(b){if(b.id===a.id)found=true});if(!found)am.push(a)});saveA(am)}).catch(function(){})}
|
||
function sbPushAudit(e){fetch(SBU+"/rest/v1/audits",{method:"POST",headers:{"apikey":SBK,"Authorization":"Bearer "+SBK,"Content-Type":"application/json","Prefer":"resolution=merge-duplicates"},body:JSON.stringify({id:e.id,number:e.number,date:e.date,location:e.location,region:e.region,work_type:e.workType,worker_count:e.workerCount,observer:e.observer,observer_role:e.observerRole,overall_safe:e.overallSafe,categories:e.categories,total_violations:e.totalViolations,dialogue:e.dialogue,photos:e.photos,docs:e.docs,created_by:e.createdBy,created_at:e.createdAt})}).catch(function(){})}
|
||
|
||
// Supabase для фото
|
||
var SBU="https://znexbjafkvyjffffbhlf.supabase.co";
|
||
var SBK="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InpuZXhiamFma3Z5amZmZmZiaGxmIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc4MDY0NDE4MiwiZXhwIjoyMDk2MjIwMTgyfQ.5pOYTkL5eCmpSHBY3EwKof6NVKt7tL4Fn8xUAKM8itE";
|
||
function gq(role){var q={c:1,p:"month",l:"1 раз в месяц"};return q}
|
||
function getUserQuota(u){if(u.freq){var p=u.freq.split(",");if(p.length===2)return{c:parseInt(p[0]),p:p[1],l:parseInt(p[0])+" раз(а) в "+(p[1]==="month"?"месяц":p[1]==="quarter"?"квартал":"полгода")}}return gq(u.role)}
|
||
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()}}return{s:new Date(n.getFullYear(),n.getMonth()<6?0:6,1),l:(n.getMonth()<6?1:2)+"-е полугодие "+n.getFullYear()}}
|
||
|
||
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:["Ремни безопасности","Опасное вождение","Состояние водителя","Телефон за рулём","Нарушение ПДД","Состояние ТС","Другое"]}];
|
||
|
||
// Build categories
|
||
var ch="";CATS.forEach(function(c){ch+="<div style=\"border:1px solid #E2E6EB;border-radius:8px;margin-bottom:8px;padding:12px;background:#F2F4F7\"><b>"+c.title+"</b><br>"+c.items.map(function(it,i){return"<div class=\"ci\"><input type=\"checkbox\" id=\"cb-"+c.id+"-"+i+"\" onchange=\"updateCT('"+c.id+"')\"><label for=\"cb-"+c.id+"-"+i+"\">"+it+"</label></div>"}).join("")+"<div style=\"margin-top:6px;font-size:12px;color:#5B6573\">Отмечено: <b id=\"cnt-"+c.id+"\">0</b></div></div>"});document.getElementById("cats").innerHTML=ch;
|
||
function updateCT(id){var cnt=0;CATS.find(function(c){return c.id===id}).items.forEach(function(_,i){if(document.getElementById("cb-"+id+"-"+i)&&document.getElementById("cb-"+id+"-"+i).checked)cnt++});document.getElementById("cnt-"+id).textContent=cnt}
|
||
|
||
function setO(t){var os=document.getElementById("os"),od=document.getElementById("od");os.className="tb"+(t==="safe"?" sf":"");od.className="tb"+(t==="danger"?" df":"")}
|
||
|
||
// Init form
|
||
document.getElementById("pd").value=new Date().toISOString().split("T")[0];
|
||
document.getElementById("po").value=U.name;document.getElementById("por").value=U.role||"";
|
||
document.getElementById("pr").value=U.region||"";document.getElementById("pob").value=U.oblast||"";document.getElementById("pct").value=U.city||"";
|
||
|
||
function showPanel(n){
|
||
["NA","MS","DB","VL","HS"].forEach(function(id){document.getElementById("pn"+id).classList.remove("ac")});
|
||
document.getElementById("pn"+n).classList.add("ac");
|
||
document.querySelectorAll("nav a").forEach(function(a){a.classList.toggle("ac",a.getAttribute("onclick").indexOf("'"+n+"'")>=0)});
|
||
if(n==="MS")rMS();if(n==="DB")rDB();if(n==="VL")rVL();if(n==="HS")rHS();sbSync();
|
||
}
|
||
function doLogout(){sessionStorage.removeItem("pab_user");location.href="index.html"}
|
||
|
||
function checkSA(){if(!U||isA()){document.getElementById("sa").style.display="none";return}var q=getUserQuota(U);if(!q.p)return;var p=gp(q.p);var done=getA().filter(function(a){return a.createdBy===U.login&&new Date(a.date)>=p.s}).length;var need=Math.max(0,q.c-done);var sa=document.getElementById("sa"),at=document.getElementById("sat");if(need>0){at.innerHTML="⚠️ Отставание: "+p.l+" — "+done+" из "+q.c+". Осталось: <b>"+need+"</b>.";sa.style.display="block";sa.style.background=need>=q.c?"#FFEBED":"#FFF3EF";sa.style.borderColor=need>=q.c?"#E63946":"#E76F51"}else{sa.style.display="none"}}
|
||
function sendScheduleReminder(){if(!U)return;var q=getUserQuota(U);var p=gp(q.p);var done=getA().filter(function(a){return a.createdBy===U.login&&new Date(a.date)>=p.s}).length;var need=Math.max(0,q.c-done);var to=U.email||"";if(!to||to.indexOf("@")<0){alert("Укажите email в профиле");return}location.href="mailto:"+encodeURIComponent(to)+"?subject="+encodeURIComponent("График ПАБ — "+p.l)+"&body="+encodeURIComponent("Уважаемый(ая) "+U.name+"!\n\nГрафик ПАБ: "+q.l+".\nПериод: "+p.l+".\nВыполнено: "+done+" из "+q.c+".\n"+(need>0?"Отставание: "+need+" ПАБ.":"График выполнен!")+"\n\nС уважением, Система ПАБ")}
|
||
|
||
function rMS(){
|
||
var c=document.getElementById("msc");if(!c)return;
|
||
if(isA()){
|
||
var all=allU();var rows="",wc=0;
|
||
for(var k in all){if(k==="admin")continue;wc++;var u=all[k];var q=getUserQuota(u);var p=gp(q.p);var d=getA().filter(function(a){return a.createdBy===k&&new Date(a.date)>=p.s}).length;var pct=Math.round(d/q.c*100);var st=d>q.c?"🔵 +"+(d-q.c):d>=q.c?"🟢 OK":"🔴 -"+(q.c-d);rows+="<tr><td>"+u.name+"</td><td>"+u.role+"</td><td>"+(u.branch||"—")+"</td><td>"+(u.region||"—")+"</td><td>"+q.l+"</td><td>"+d+"/"+q.c+"</td><td>"+st+"</td><td>"+p.l+"</td><td><button class=\"btn bd\" style=\"padding:2px 6px;font-size:10px\" onclick=\"delUser('"+k+"')\">🗑️</button></td></tr>"}
|
||
c.innerHTML=wc===0?"<div class=\"card\"><h3>👥 График работников</h3><p style=\"color:#5B6573\">Нет зарегистрированных работников. Зарегистрируйте их на странице входа.</p></div>":"<div class=\"card\"><h3>👥 График всех работников</h3></div><table><thead><tr><th>ФИО</th><th>Должность</th><th>Филиал</th><th>Регион</th><th>Норма</th><th>Прогресс</th><th>Статус</th><th>Период</th><th></th></tr></thead><tbody>"+rows+"</tbody></table>";
|
||
return;
|
||
}
|
||
var q=getUserQuota(U);var p=gp(q.p);var d=getA().filter(function(a){return a.createdBy===U.login&&new Date(a.date)>=p.s}).length;var pct=Math.round(d/q.c*100);var over=d>q.c;var cl=over?"#00B4D8":d>=q.c?"#2D6A4F":d>=q.c/2?"#E76F51":"#E63946";
|
||
c.innerHTML="<div class=\"card\"><h3>📅 "+p.l+"</h3><div style=\"font-size:12px;color:#5B6573;margin-bottom:8px\">Минимум: <b>"+q.l+"</b> | "+U.role+"</div><div style=\"height:14px;border-radius:7px;background:#E2E6EB;overflow:hidden;margin-bottom:6px\"><div style=\"height:100%;border-radius:7px;width:"+Math.min(pct,200)+"%;background:"+cl+";transition:width .5s\"></div></div><div style=\"font-size:13px;font-weight:600\">Проведено: <b>"+d+"</b> из <b>"+q.c+"</b>"+(over?" — <span style=\"color:#00B4D8\">✅ +"+(d-q.c)+" сверх плана!</span>":d>=q.c?" — ✅ план выполнен":" — <span style=\"color:#E63946\">осталось "+(q.c-d)+"</span>")+"</div></div><div class=\"card\" style=\"background:#E0F7FA;border:1px solid #00B4D8;margin-top:14px\"><h3>👤 Профиль</h3><div style=\"display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:13px\"><div><b>ФИО:</b> "+U.name+"</div><div><b>Должность:</b> "+U.role+"</div><div><b>Филиал:</b> "+(U.branch||"—")+"</div><div><b>Подразделение:</b> "+(U.dept||"—")+"</div><div><b>Регион:</b> "+(U.region||"—")+"</div><div><b>Область:</b> "+(U.oblast||"—")+"</div><div><b>Город:</b> "+(U.city||"—")+"</div><div><b>Email:</b> "+(U.email||"—")+"</div><div><b>График:</b> "+q.l+"</div><div><b>Проведено всего:</b> "+getA().filter(function(a){return a.createdBy===U.login}).length+"</div></div></div>"+(d>0?"<div class=\"card\"><h3>📋 Последние аудиты</h3>"+getA().filter(function(a){return a.createdBy===U.login}).slice(0,5).map(function(a){return"<div style=\"padding:6px 0;border-bottom:1px solid #F2F4F7;font-size:13px\">"+a.date+" — "+a.location+" — <span class=\"badge "+(a.overallSafe?"bs":"bd2")+"\">"+(a.overallSafe?"Безопасно":"Нарушений: "+a.totalViolations)+"</span></div>"}).join("")+"</div>":"");
|
||
}
|
||
|
||
function rDB(){
|
||
var c=document.getElementById("dbc");if(!c)return;
|
||
var a=getA(),all=allU();
|
||
// Date filter
|
||
var df=document.getElementById("df"),dt=document.getElementById("dt");
|
||
if(df&&df.value)a=a.filter(function(x){return x.date>=df.value});
|
||
if(dt&&dt.value)a=a.filter(function(x){return x.date<=dt.value});
|
||
var t=a.length,sf=a.filter(function(x){return x.overallSafe}).length,wd=a.filter(function(x){return !x.overallSafe}).length,tv=a.reduce(function(s,x){return s+(x.totalViolations||0)},0);
|
||
var ot=0,bh=0,ov=0;for(var k in all){if(k==="admin")continue;var u=all[k];var q=getUserQuota(u);if(!q.p)continue;var p=gp(q.p);var d=a.filter(function(x){return x.createdBy===k&&new Date(x.date)>=p.s}).length;if(d>q.c)ov++;else if(d>=q.c)ot++;else bh++}
|
||
var adb=isA()?"<div style=\"margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap\"><button class=\"btn bp\" onclick=\"downloadFullCSV()\">📥 CSV данные</button><button class=\"btn bo\" onclick=\"downloadSummaryHTML()\">📊 HTML отчёт</button><button class=\"btn bo\" onclick=\"showAllUsers()\">👥 Пользователи</button><span style=\"color:#E2E6EB;margin:0 4px\">|</span><button class=\"btn bp\" onclick=\"sbSync();alert('Синхронизировано!')\" style=\"background:#2D6A4F\">🔄 Синхронизировать</button><button class=\"btn bo\" onclick=\"importData()\">📥 Импорт</button><button class=\"btn bo\" onclick=\"exportData()\">📤 Экспорт</button></div>":"";
|
||
c.innerHTML=adb+"<div class=\"fb\"><span style=\"font-size:12px;color:#5B6573\">с</span><input type=\"date\" id=\"df\" style=\"width:140px\" onchange=\"rDB()\"><span style=\"font-size:12px;color:#5B6573\">по</span><input type=\"date\" id=\"dt\" style=\"width:140px\" onchange=\"rDB()\"><button class=\"btn bo bs\" onclick=\"document.getElementById('df').value='';document.getElementById('dt').value='';rDB()\" style=\"margin-left:6px\">✕ Сбросить</button></div>"+
|
||
"<div class=\"stats\"><div class=\"st\"><div class=\"n\">"+t+"</div><div class=\"l\">Всего аудитов</div></div><div class=\"st gr\"><div class=\"n\">"+sf+"</div><div class=\"l\">Безопасно</div></div><div class=\"st rd\"><div class=\"n\">"+wd+"</div><div class=\"l\">С нарушениями</div></div><div class=\"st rd\"><div class=\"n\">"+tv+"</div><div class=\"l\">Всего пунктов нарушений</div></div><div class=\"st gr\"><div class=\"n\">"+(ot+ov)+"</div><div class=\"l\">Выполняют график</div></div><div class=\"st rd\"><div class=\"n\">"+bh+"</div><div class=\"l\">Отстают от графика</div></div></div>"+
|
||
"<div style=\"display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:14px\"><div class=\"card\"><h3>📂 Нарушения по категориям</h3><canvas id=\"ch1\"></canvas></div><div class=\"card\"><h3>📅 Динамика по датам</h3><canvas id=\"ch2\"></canvas></div></div>"+
|
||
"<div class=\"card\"><h3>🔝 Топ-10 нарушений</h3><canvas id=\"ch3\"></canvas></div>";
|
||
setTimeout(function(){
|
||
if(window._charts){for(var ck in window._charts)try{window._charts[ck].destroy()}catch(e){}}window._charts={};
|
||
var ctx1=document.getElementById("ch1");if(ctx1)window._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 a.reduce(function(s,x){var c2=x.categories&&x.categories[cat.id];return s+(c2?c2.items.length:0)},0)}),backgroundColor:"#E63946",borderRadius:4}]},options:{responsive:true,plugins:{legend:{display:false}}}});
|
||
var dates={};a.forEach(function(x){if(!dates[x.date])dates[x.date]=0;dates[x.date]+=(x.totalViolations||0)});var sd=Object.keys(dates).sort();
|
||
var ctx2=document.getElementById("ch2");if(ctx2)window._charts.tl=new Chart(ctx2,{type:"line",data:{labels:sd,datasets:[{label:"Нарушений",data:sd.map(function(d){return dates[d]}),borderColor:"#E63946",tension:0.3,pointRadius:4}]},options:{responsive:true,plugins:{legend:{display:false}}}});
|
||
var ic={};a.forEach(function(x){if(x.categories){Object.values(x.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 ctx3=document.getElementById("ch3");if(ctx3)window._charts.top=new Chart(ctx3,{type:"bar",data:{labels:ti.map(function(i){return 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}}}});
|
||
},300);
|
||
}
|
||
|
||
function rVL(){var c=document.getElementById("vlc");if(!c)return;var a=getA(),td=new Date().toISOString().split("T")[0],av=[];
|
||
a.forEach(function(x){
|
||
if(x.violations&&x.violations.length>0){x.violations.forEach(function(v){var dd=v.date||"",dn=v.done&&v.done.trim();var st="pending";if(dn)st="done";else if(dd&&dd<td)st="overdue";av.push({nc:v.nc,ex:v.executor,ms:v.measure,rs:v.responsible,dt:dd,dn:v.done||"",st:st,ad:x.date,an:x.number||"—",ob:x.oblast||"",ct:x.city||""})})}
|
||
else if(x.totalViolations>0&&x.categories){Object.values(x.categories).forEach(function(cat){if(cat.items)cat.items.forEach(function(it){av.push({nc:it.item,ex:x.observer,ms:"",rs:"",dt:"",dn:"",st:"pending",ad:x.date,an:x.number||"—",ob:x.oblast||"",ct:x.city||""})})})}
|
||
});c.innerHTML="<div class=\"stats\"><div class=\"st\"><div class=\"n\">"+av.length+"</div><div class=\"l\">Всего</div></div><div class=\"st gr\"><div class=\"n\">"+av.filter(function(v){return v.st==="done"}).length+"</div><div class=\"l\">Устранено</div></div><div class=\"st rd\"><div class=\"n\">"+av.filter(function(v){return v.st==="overdue"}).length+"</div><div class=\"l\">Просрочено</div></div><div class=\"st\"><div class=\"n\" style=\"color:#E76F51\">"+av.filter(function(v){return v.st==="pending"}).length+"</div><div class=\"l\">В работе</div></div></div>"+(av.length>0?"<table><thead><tr><th>№</th><th>Несоответствие</th><th>Аудит</th><th>Область</th><th>Город</th><th>Исполнитель</th><th>Меры</th><th>Срок</th><th>Статус</th></tr></thead><tbody>"+av.map(function(v,i){var sc=v.st==="done"?"bs":v.st==="overdue"?"bd2":"bw";var sl=v.st==="done"?"Устранено":v.st==="overdue"?"Просрочено":"В работе";return"<tr><td>"+(i+1)+"</td><td>"+v.nc+"</td><td>"+v.ad+"</td><td>"+(v.ob||"—")+"</td><td>"+(v.ct||"—")+"</td><td>"+v.ex+"</td><td>"+(v.ms||"—")+"</td><td>"+(v.dt||"—")+"</td><td><span class=\"badge "+sc+"\">"+sl+"</span></td></tr>"}).join("")+"</tbody></table>":"<p style=\"color:#5B6573;padding:20px\">Несоответствий не найдено</p>")}
|
||
|
||
function rHS(){var a=getA(),tb=document.getElementById("hbd");if(!tb)return;tb.innerHTML=a.length===0?"<tr><td colspan=\"9\" style=\"text-align:center;padding:20px;color:#5B6573\">Нет записей</td></tr>":a.map(function(x){var ab="<a style=\"color:#00B4D8;cursor:pointer;font-weight:600\" onclick=\"viewA("+x.id+")\">👁️</a>"+(isA()?" <a style=\"color:#00B4D8;cursor:pointer;font-weight:600\" onclick=\"editA("+x.id+")\">✏️</a> <button class=\"btn bd\" style=\"padding:4px 8px;font-size:11px\" onclick=\"delA("+x.id+")\">🗑️</button>":"");return"<tr><td>"+(x.number||"—")+"</td><td>"+x.date+"</td><td>"+x.location+"</td><td>"+(x.oblast||"—")+"</td><td>"+(x.city||"—")+"</td><td>"+x.observer+"</td><td><span class=\"badge "+(x.overallSafe?"bs":"bd2")+"\">"+(x.overallSafe?"Безопасно":"Нарушения")+"</span></td><td>"+(x.totalViolations||0)+"</td><td>"+ab+"</td></tr>"}).join("")}
|
||
|
||
function uploadPhotos(files,callback){var urls=[];var done=0;function check(){done++;if(done>=files.length)callback(urls)}if(files.length===0){callback([]);return}for(var i=0;i<files.length;i++){(function(f){var fn=Date.now()+"_"+Math.random().toString(36).slice(2)+"_"+f.name.replace(/[^a-zA-Z0-9._-]/g,"_");fetch(SBU+"/storage/v1/object/photos/"+fn,{method:"POST",headers:{"apikey":SBK,"Authorization":"Bearer "+SBK,"Content-Type":f.type},body:f}).then(function(r){if(r.ok){urls.push(SBU+"/storage/v1/object/public/photos/"+fn)}check()}).catch(function(){check()})})(files[i])}}
|
||
|
||
// Violations table
|
||
var vioRC=1;
|
||
function addVioRowFn(){vioRC++;var t=document.getElementById("vioRow1").cloneNode(true);t.id="vioRow"+vioRC;t.querySelector("td").textContent=vioRC;t.querySelectorAll("input").forEach(function(i){i.value=""});document.getElementById("vioBody").appendChild(t)}
|
||
function removeVioRow(btn){var rows=document.querySelectorAll("#vioBody tr");if(rows.length<=1)return;btn.closest("tr").remove();document.querySelectorAll("#vioBody tr").forEach(function(r,i){r.querySelector("td").textContent=i+1});vioRC=document.querySelectorAll("#vioBody tr").length}
|
||
function getVioData(){var r=[];document.querySelectorAll("#vioBody tr").forEach(function(row){var nc=row.querySelector(".v-nc");if(!nc||!nc.value.trim())return;r.push({nc:nc.value.trim(),executor:row.querySelector(".v-ex").value.trim(),type:row.querySelector(".v-tp").value,measure:row.querySelector(".v-ms").value.trim(),responsible:row.querySelector(".v-rs").value.trim(),date:row.querySelector(".v-dt").value,done:row.querySelector(".v-fn").value.trim()})});return r}
|
||
|
||
function submitAudit(){
|
||
if(editId&&!isA()){alert("Только администратор может редактировать");return}
|
||
var loc=document.getElementById("pl").value.trim();if(!loc){alert("Укажите место проведения");return}
|
||
var pf=document.getElementById("pfiles");var files=pf&&pf.files?Array.from(pf.files):[];
|
||
uploadPhotos(files,function(photoUrls){
|
||
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)ch.push({item:item})});cats[cat.id]={items:ch,allSafe:ch.length===0};tv+=ch.length});
|
||
var dl=[];if(document.getElementById("d0").checked)dl.push("Работник привёл примеры безопасных действий");if(document.getElementById("d1").checked)dl.push("Были обсуждены риски / проблемы");if(document.getElementById("d2").checked)dl.push("Определены корректирующие меры");if(document.getElementById("d3").checked)dl.push("Предложения работника зафиксированы");
|
||
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()||U.name,observerRole:document.getElementById("por").value.trim(),supervisor:document.getElementById("psv").value.trim(),supervisorRole:document.getElementById("psr").value.trim(),oblast:document.getElementById("pob").value.trim(),city:document.getElementById("pct").value.trim(),overallSafe:document.getElementById("os").classList.contains("sf"),categories:cats,totalViolations:tv,violations:getVioData(),dialogue:dl,photos:photoUrls,createdBy:U.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);sbPushAudit(e);lastSubmitted=e;
|
||
document.getElementById("sd").innerHTML="<b>Бланк №"+(e.number||"—")+"</b> | "+e.date+" | "+(e.overallSafe?"БЕЗОПАСНО":"НАРУШЕНИЙ: "+e.totalViolations);document.getElementById("fs").style.display="block";setTimeout(function(){document.getElementById("fs").style.display="none"},20000);
|
||
resetF();rVL();
|
||
});
|
||
}
|
||
|
||
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("pr").value=U.region||"";document.getElementById("pl").value="";document.getElementById("pw").value="";document.getElementById("pc").value="1";document.getElementById("po").value=U.name;document.getElementById("por").value="";document.getElementById("psv").value="";document.getElementById("psr").value="";document.getElementById("pob").value=U.oblast||"";document.getElementById("pct").value=U.city||"";setO("safe");editId=null;
|
||
CATS.forEach(function(cat){cat.items.forEach(function(_,i){var cb=document.getElementById("cb-"+cat.id+"-"+i);if(cb)cb.checked=false});updateCT(cat.id)});
|
||
document.getElementById("d0").checked=false;document.getElementById("d1").checked=false;document.getElementById("d2").checked=false;document.getElementById("d3").checked=false;
|
||
document.getElementById("pfiles").value="";document.getElementById("fn").textContent="";
|
||
}
|
||
|
||
function exportCSV(){var a=getA();if(a.length===0){alert("Нет данных");return}var all=allU(),h="Бланк №;Дата;Место;Область;Город;Наблюдатель;Должность;Филиал;Регион;Статус;Нарушений",rs=a.map(function(x){var u=all[x.createdBy]||{};return(x.number||"")+";"+x.date+";"+x.location+";"+(x.oblast||"")+";"+(x.city||"")+";"+x.observer+";"+(x.observerRole||"")+";"+(u.branch||"")+";"+(u.region||"")+";"+(x.overallSafe?"Безопасно":"Нарушения")+";"+(x.totalViolations||0)});var bom="\uFEFF",csv=bom+h+"\n"+rs.join("\n"),bl=new Blob([csv],{type:"text/csv;charset=utf-8"}),ur=URL.createObjectURL(bl),dl=document.createElement("a");dl.href=ur;dl.download="pab.csv";dl.click();URL.revokeObjectURL(ur);alert("CSV в кодировке UTF-8. Откройте в Excel: Данные → Из текста → выберите файл → UTF-8")}
|
||
function editA(id){if(!isA()){alert("Только админ");return}alert("Редактирование: аудит #"+id)}
|
||
function delA(id){if(!isA()){alert("Только админ");return}if(!confirm("Удалить?"))return;saveA(getA().filter(function(a){return a.id!==id}));rHS()}
|
||
function delUser(login){if(!isA())return;if(!confirm("Удалить пользователя "+login+" и все его аудиты?"))return;var u=getU();delete u[login];saveU(u);var a=getA().filter(function(x){return x.createdBy!==login});saveA(a);fetch(SBU+"/rest/v1/users?login=eq."+encodeURIComponent(login),{method:"DELETE",headers:{"apikey":SBK,"Authorization":"Bearer "+SBK}}).catch(function(){});rMS();rHS();rDB()}
|
||
function downloadFullCSV(){var a=getA();if(a.length===0){alert("Нет данных");return}var all=allU(),h="Бланк №;Дата;Место;Наблюдатель;Филиал;Регион;Статус;Нарушений",rs=a.map(function(x){var u=all[x.createdBy]||{};return(x.number||"")+";"+x.date+";"+x.location+";"+x.observer+";"+(u.branch||"")+";"+(u.region||"")+";"+(x.overallSafe?"Безопасно":"Нарушения")+";"+(x.totalViolations||0)}),csv="\uFEFF"+h+"\n"+rs.join("\n"),bl=new Blob([csv],{type:"text/csv"}),ur=URL.createObjectURL(bl),dl=document.createElement("a");dl.href=ur;dl.download="pab-full.csv";dl.click();URL.revokeObjectURL(ur)}
|
||
function showAllUsers(){if(!isA())return;var all=allU(),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></tr>";for(var k in all){var u=all[k];h+="<tr><td>"+k+(k==="admin"?" ⭐":"")+"</td><td>"+u.name+"</td><td>"+u.role+"</td><td>"+(u.branch||"—")+"</td><td>"+(u.region||"—")+"</td><td>"+(u.city||"—")+"</td></tr>"}h+="</table>";var w=window.open("","_blank","width=800,height=500");w.document.write("<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Пользователи</title><style>body{font:14px/1.5 Arial;max-width:800px;margin:20px auto;padding:20px}</style></head><body>"+h+"</body></html>");w.document.close()}
|
||
function downloadSummaryHTML(){var a=getA(),all=allU(),t=a.length;var w=window.open("","_blank","width=800,height=600");w.document.write("<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Отчёт ПАБ</title><style>body{font:14px/1.5 Arial;max-width:900px;margin:20px auto;padding:20px}h1{font-size:22px}h2{font-size:16px;margin-top:20px}table{width:100%;border-collapse:collapse;font-size:12px}th{background:#0F1218;color:#fff;padding:8px}td{padding:6px 8px;border-bottom:1px solid #E2E6EB}@media print{button{display:none}}</style></head><body><button onclick=\"window.print()\" style=\"padding:8px 16px;margin-bottom:16px\">🖨️ Печать</button><h1>📊 Сводный отчёт ПАБ</h1><p>Сформирован: "+new Date().toLocaleString("ru")+" | Всего аудитов: "+t+"</p><h2>📋 Аудиты</h2><table><tr><th>№</th><th>Дата</th><th>Место</th><th>Наблюдатель</th><th>Статус</th><th>Нарушений</th></tr>"+a.map(function(x){return"<tr><td>"+(x.number||"—")+"</td><td>"+x.date+"</td><td>"+x.location+"</td><td>"+x.observer+"</td><td>"+(x.overallSafe?"Безопасно":"Нарушения")+"</td><td>"+(x.totalViolations||0)+"</td></tr>"}).join("")+"</table></body></html>");w.document.close()}
|
||
function exportData(){
|
||
var a=getA();if(a.length===0){alert("Нет данных");return}
|
||
var all=allU(),h="Бланк №;Дата;Время;Место;Область;Город;Наблюдатель;Должность;Руководитель;Тип работы;Регион;Филиал;Статус;Нарушений";
|
||
var rs=a.map(function(x){var u=all[x.createdBy]||{};return(x.number||"")+";"+x.date+";"+(x.timeStart||"")+"-"+(x.timeEnd||"")+";"+x.location+";"+(x.oblast||"")+";"+(x.city||"")+";"+x.observer+";"+(x.observerRole||"")+";"+(x.supervisor||"")+";"+(x.workType||"")+";"+(x.region||"")+";"+(u.branch||"")+";"+(x.overallSafe?"Безопасно":"Нарушения")+";"+(x.totalViolations||0)});
|
||
var csv="\uFEFF"+h+"\n"+rs.join("\n"),bl=new Blob([csv],{type:"text/csv;charset=utf-8"}),ur=URL.createObjectURL(bl),dl=document.createElement("a");
|
||
dl.href=ur;dl.download="pab-export-"+new Date().toISOString().split("T")[0]+".csv";dl.click();URL.revokeObjectURL(ur);
|
||
}
|
||
function importData(){
|
||
var input=document.createElement("input");input.type="file";input.accept=".json";
|
||
input.onchange=function(){
|
||
var file=this.files[0];if(!file)return;
|
||
var reader=new FileReader();
|
||
reader.onload=function(e){
|
||
try{
|
||
var data=JSON.parse(e.target.result);
|
||
if(data.audits&&confirm("Импортировать "+data.audits.length+" аудитов и пользователей? Текущие данные будут объединены.")){
|
||
var curA=getA(),curU=getU();
|
||
// Merge audits (avoid duplicates by id)
|
||
var ids={};curA.forEach(function(a){ids[a.id]=true});
|
||
data.audits.forEach(function(a){if(!ids[a.id])curA.push(a)});
|
||
saveA(curA);
|
||
// Merge users
|
||
for(var k in data.users){if(!curU[k])curU[k]=data.users[k]}
|
||
localStorage.setItem("pab_users",JSON.stringify(curU));
|
||
alert("Импортировано! Аудитов: "+curA.length+", пользователей: "+Object.keys(curU).length);
|
||
rDB();rHS();rVL();rMS();
|
||
}
|
||
}catch(ex){alert("Ошибка чтения файла")}
|
||
};
|
||
reader.readAsText(file);
|
||
};
|
||
input.click();
|
||
}
|
||
rHS();sbSync();checkSA();
|
||
</script>
|
||
</body>
|
||
</html>
|