Админ: скачивание полного CSV, отчёта по работникам, сводного HTML-отчёта
This commit is contained in:
parent
2fbd2f4a10
commit
c9ac12a3a4
98
index.html
98
index.html
@ -319,6 +319,13 @@ body{font:15px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,s
|
||||
<!-- ========== DASHBOARD ========== -->
|
||||
<div id="panelDashboard" class="panel">
|
||||
<div class="page-header"><h2>📊 Дашборд статистики</h2><p>Аналитика по всем аудитам</p></div>
|
||||
<div class="filter-bar">
|
||||
<div id="adminBar" style="display:none;margin-right:auto;display:flex;gap:8px;flex-wrap:wrap">
|
||||
<button class="btn btn-primary btn-sm" onclick="downloadFullCSV()">📥 Скачать все данные (CSV)</button>
|
||||
<button class="btn btn-outline btn-sm" onclick="downloadWorkerReport()">👥 Отчёт по работникам (CSV)</button>
|
||||
<button class="btn btn-outline btn-sm" onclick="downloadSummaryHTML()">📊 Сводный отчёт (HTML)</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-bar" id="dashboardFilters">
|
||||
<select id="dfRegion" onchange="renderDashboard()"><option value="all">Все регионы</option></select>
|
||||
<select id="dfOblast" onchange="renderDashboard()"><option value="all">Все области</option></select>
|
||||
@ -698,6 +705,7 @@ function switchPanel(name,el){
|
||||
|
||||
// ========== DASHBOARD ==========
|
||||
function renderDashboard(){
|
||||
document.getElementById('adminBar').style.display=isAdmin()?'flex':'none';
|
||||
const allUsers=getAllUsers();
|
||||
const userList=Object.entries(allUsers).map(([login,data])=>({login,...data}));
|
||||
|
||||
@ -846,6 +854,96 @@ function exportCSV(){
|
||||
const rows=audits.map(a=>{const u=all[a.createdBy]||{};return `${a.number||''};${a.date};${a.timeStart||''}-${a.timeEnd||''};"${a.location}";"${a.workType||''}";"${a.observer}";"${a.observerRole||''}";"${u.branch||''}";"${u.region||''}";"${u.oblast||''}";"${u.city||''}";${a.overallSafe?'Безопасно':'Нарушения'};${a.totalViolations||0}`});
|
||||
const csv='\uFEFF'+header+'\n'+rows.join('\n');const blob=new Blob([csv],{type:'text/csv;charset=utf-8'});const url=URL.createObjectURL(blob);const a=document.createElement('a');a.href=url;a.download='pab-audit.csv';a.click();URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// ========== ADMIN DOWNLOADS ==========
|
||||
function downloadFullCSV(){
|
||||
const audits=getAudits();if(audits.length===0){alert('Нет данных');return}
|
||||
const all=getAllUsers();
|
||||
const header='Бланк №;Дата;Время;Место;Тип работы;Наблюдатель;Должность;Руководитель;Филиал;Подразделение;Регион;Область;Город;Статус;Нарушений;Категории с нарушениями';
|
||||
const rows=audits.map(a=>{
|
||||
const u=all[a.createdBy]||{};
|
||||
const catsWithVio=CATEGORIES.filter(cat=>{const c=a.categories&&a.categories[cat.id];return c&&c.items.length>0}).map(c=>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};"${catsWithVio}"`;
|
||||
});
|
||||
const csv='\uFEFF'+header+'\n'+rows.join('\n');const blob=new Blob([csv],{type:'text/csv;charset=utf-8'});const url=URL.createObjectURL(blob);const a=document.createElement('a');a.href=url;a.download='pab-full-report.csv';a.click();URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function downloadWorkerReport(){
|
||||
const all=getAllUsers();const audits=getAudits();const userList=Object.entries(all).filter(([l])=>l!=='admin');
|
||||
if(userList.length===0){alert('Нет зарегистрированных работников');return}
|
||||
const header='Логин;ФИО;Должность;Филиал;Подразделение;Регион;Область;Город;Email;График;Период;Выполнено;Нужно;Статус';
|
||||
const rows=userList.map(([login,u])=>{
|
||||
const q=getQuota(u.role);
|
||||
if(!q.period)return `${login};"${u.name}";"${u.role}";"${u.branch||''}";"${u.dept||''}";"${u.region||''}";"${u.oblast||''}";"${u.city||''}";"${u.email||''}";Без графика;;;0;—`;
|
||||
const p=getCurrentPeriod(q.period);const done=audits.filter(a=>a.createdBy===login&&new Date(a.date)>=p.start).length;
|
||||
const status=done>=q.count?'Выполнен':'Отстаёт ('+(q.count-done)+')';
|
||||
return `${login};"${u.name}";"${u.role}";"${u.branch||''}";"${u.dept||''}";"${u.region||''}";"${u.oblast||''}";"${u.city||''}";"${u.email||''}";"${q.label}";"${p.label}";${done};${q.count};"${status}"`;
|
||||
});
|
||||
const csv='\uFEFF'+header+'\n'+rows.join('\n');const blob=new Blob([csv],{type:'text/csv;charset=utf-8'});const url=URL.createObjectURL(blob);const a=document.createElement('a');a.href=url;a.download='pab-worker-report.csv';a.click();URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function downloadSummaryHTML(){
|
||||
const audits=getAudits();const all=getAllUsers();const userList=Object.entries(all).filter(([l])=>l!=='admin');
|
||||
const total=audits.length;const allSafe=audits.filter(a=>a.overallSafe).length;
|
||||
const withDanger=audits.filter(a=>!a.overallSafe).length;const totalVio=audits.reduce((s,a)=>s+(a.totalViolations||0),0);
|
||||
|
||||
// Schedule stats
|
||||
let onTrack=0,behind=0,noQuota=0;
|
||||
userList.forEach(([login,u])=>{const q=getQuota(u.role);if(!q.period){noQuota++;return}const p=getCurrentPeriod(q.period);const done=audits.filter(a=>a.createdBy===login&&new Date(a.date)>=p.start).length;if(done>=q.count)onTrack++;else behind++});
|
||||
|
||||
// Category stats
|
||||
const catStats=CATEGORIES.map(cat=>({name:cat.title,count:audits.reduce((s,a)=>{const c=a.categories&&a.categories[cat.id];return s+(c?c.items.length:0)},0)})).sort((a,b)=>b.count-a.count);
|
||||
|
||||
// Region stats
|
||||
const regStats={};audits.forEach(a=>{const u=all[a.createdBy];const r=u?u.region||'Не указан':'Не указан';if(!regStats[r])regStats[r]={total:0,safe:0,danger:0};regStats[r].total++;if(a.overallSafe)regStats[r].safe++;else regStats[r].danger++});
|
||||
const regRows=Object.entries(regStats).sort((a,b)=>b[1].total-a[1].total).map(([r,s])=>`<tr><td>${r}</td><td>${s.total}</td><td>${s.safe}</td><td>${s.danger}</td><td>${Math.round(s.safe/s.total*100)}%</td></tr>`).join('');
|
||||
|
||||
// Branch stats
|
||||
const brStats={};audits.forEach(a=>{const u=all[a.createdBy];const b=u?u.branch||'Не указан':'Не указан';brStats[b]=(brStats[b]||0)+1});
|
||||
const brRows=Object.entries(brStats).sort((a,b)=>b[1]-a[1]).map(([b,c])=>`<tr><td>${b}</td><td>${c}</td></tr>`).join('');
|
||||
|
||||
// Top violations
|
||||
const itemCounts={};audits.forEach(a=>{if(a.categories){Object.values(a.categories).forEach(cat=>{if(cat.items){cat.items.forEach(it=>{itemCounts[it.item]=(itemCounts[it.item]||0)+1})})}});
|
||||
const topItems=Object.entries(itemCounts).sort((a,b)=>b[1]-a[1]).slice(0,15).map(([i,c])=>`<tr><td>${i}</td><td>${c}</td></tr>`).join('');
|
||||
|
||||
// Worker schedule
|
||||
const workerRows=userList.map(([login,u])=>{
|
||||
const q=getQuota(u.role);if(!q.period)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>`;
|
||||
const p=getCurrentPeriod(q.period);const done=audits.filter(a=>a.createdBy===login&&new Date(a.date)>=p.start).length;
|
||||
const cls=done>=q.count?'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:${cls}">${done}/${q.count} (${q.label})</td><td>${p.label}</td></tr>`;
|
||||
}).join('');
|
||||
|
||||
const html=`<!DOCTYPE html><html><head><meta charset="utf-8"><title>Сводный отчёт ПАБ</title>
|
||||
<style>body{font:14px/1.5 Arial,sans-serif;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}
|
||||
tr:hover td{background:#F2F4F7}
|
||||
.footer{margin-top:30px;font-size:12px;color:#5B6573;text-align:center}
|
||||
@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></tr>${catStats.map(c=>`<tr><td>${c.name}</td><td>${c.count}</td></tr>`).join('')}</table>
|
||||
<h2>🏢 По регионам</h2><table><tr><th>Регион</th><th>Всего</th><th>Безопасно</th><th>С наруш.</th><th>% Безоп.</th></tr>${regRows}</table>
|
||||
<h2>🏭 По филиалам</h2><table><tr><th>Филиал</th><th>Аудитов</th></tr>${brRows}</table>
|
||||
<h2>👥 Выполнение графика по работникам</h2><table><tr><th>ФИО</th><th>Должность</th><th>Филиал</th><th>Подразделение</th><th>Регион</th><th>Город</th><th>Прогресс</th><th>Период</th></tr>${workerRows}</table>
|
||||
<h2>🔝 Топ-15 нарушений</h2><table><tr><th>Нарушение</th><th>Кол-во</th></tr>${topItems}</table>
|
||||
<div class="footer">Отчёт системы ПАБ © ${new Date().getFullYear()}</div>
|
||||
</body></html>`;
|
||||
const w=window.open('','_blank','width=1000,height=800');w.document.write(html);w.document.close();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user