MVP polish: KPI bar, demo hints, rename Jarvis→HSE-assistant, empty states, button labels, MVP limitations block
This commit is contained in:
parent
fcbe32064b
commit
014fe0f81b
127
index.html
127
index.html
@ -134,6 +134,31 @@ th:nth-child(6){min-width:105px;text-align:center}
|
|||||||
.demo-note{background:#fefce8;border:1px solid #fde047;border-radius:6px;padding:8px 12px;font-size:11.5px;color:#854d0e;display:flex;align-items:start;gap:8px;margin-bottom:14px;line-height:1.45}
|
.demo-note{background:#fefce8;border:1px solid #fde047;border-radius:6px;padding:8px 12px;font-size:11.5px;color:#854d0e;display:flex;align-items:start;gap:8px;margin-bottom:14px;line-height:1.45}
|
||||||
.demo-note::before{content:"ℹ";flex-shrink:0;width:18px;height:18px;background:#facc15;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:#713f12}
|
.demo-note::before{content:"ℹ";flex-shrink:0;width:18px;height:18px;background:#facc15;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:#713f12}
|
||||||
|
|
||||||
|
/* KPI BAR (events page) */
|
||||||
|
.kpi-bar{display:flex;gap:0;align-items:stretch;text-align:center}
|
||||||
|
.kpi-item{flex:1;padding:10px 8px;border-right:1px solid #E2E8F0}
|
||||||
|
.kpi-item:last-child{border:none}
|
||||||
|
.kpi-item .kpi-num{font-size:24px;font-weight:800;line-height:1.2;letter-spacing:-.5px}
|
||||||
|
.kpi-item .kpi-lbl{font-size:10.5px;color:#64748B;text-transform:uppercase;letter-spacing:.2px;margin-top:2px;font-weight:500}
|
||||||
|
.kpi-item.kpi-total .kpi-num{color:#334155}
|
||||||
|
.kpi-item.kpi-done .kpi-num{color:#059669}
|
||||||
|
.kpi-item.kpi-warn .kpi-num{color:#d97706}
|
||||||
|
.kpi-item.kpi-late .kpi-num{color:#dc2626}
|
||||||
|
.kpi-item.kpi-pct .kpi-num{color:#2563eb}
|
||||||
|
|
||||||
|
/* EMPTY STATE */
|
||||||
|
.empty-state{text-align:center;padding:40px 20px;color:#94a3b8}
|
||||||
|
.empty-state .empty-icon{font-size:40px;margin-bottom:10px;opacity:.4}
|
||||||
|
.empty-state .empty-text{font-size:14px;color:#64748B}
|
||||||
|
.empty-state .empty-hint{font-size:11.5px;color:#94a3b8;margin-top:4px}
|
||||||
|
|
||||||
|
/* MVP LIMITATIONS BLOCK */
|
||||||
|
.mvp-block{background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:16px 18px;margin-top:14px}
|
||||||
|
.mvp-block h4{font-size:12px;font-weight:600;color:#475569;margin-bottom:8px;text-transform:uppercase;letter-spacing:.2px}
|
||||||
|
.mvp-block ul{list-style:none;padding:0;margin:0}
|
||||||
|
.mvp-block li{font-size:11.5px;color:#64748B;padding:3px 0 3px 18px;position:relative;line-height:1.45}
|
||||||
|
.mvp-block li::before{content:"→";position:absolute;left:0;color:#94a3b8;font-size:10px}
|
||||||
|
|
||||||
/* MOBILE RESPONSIVE */
|
/* MOBILE RESPONSIVE */
|
||||||
@media(max-width:900px){
|
@media(max-width:900px){
|
||||||
#sidebar{width:64px}
|
#sidebar{width:64px}
|
||||||
@ -159,6 +184,8 @@ th:nth-child(6){min-width:105px;text-align:center}
|
|||||||
.mobile-header{display:flex!important;align-items:center;justify-content:space-between;padding:10px 14px;background:#fff;border-bottom:1px solid #E2E8F0;position:sticky;top:0;z-index:99}
|
.mobile-header{display:flex!important;align-items:center;justify-content:space-between;padding:10px 14px;background:#fff;border-bottom:1px solid #E2E8F0;position:sticky;top:0;z-index:99}
|
||||||
.mobile-menu-btn{display:flex!important;padding:8px;border:none;background:none;font-size:20px;cursor:pointer;color:#334155}
|
.mobile-menu-btn{display:flex!important;padding:8px;border:none;background:none;font-size:20px;cursor:pointer;color:#334155}
|
||||||
.stat-grid{grid-template-columns:1fr 1fr}
|
.stat-grid{grid-template-columns:1fr 1fr}
|
||||||
|
.kpi-bar{flex-wrap:wrap}
|
||||||
|
.kpi-item{flex:1 1 20%;min-width:80px;border-right:none;padding:8px 4px}
|
||||||
}
|
}
|
||||||
.mobile-header{display:none}
|
.mobile-header{display:none}
|
||||||
.mobile-menu-btn{display:none}
|
.mobile-menu-btn{display:none}
|
||||||
@ -171,7 +198,8 @@ th:nth-child(6){min-width:105px;text-align:center}
|
|||||||
<p>План производственной безопасности 2026</p>
|
<p>План производственной безопасности 2026</p>
|
||||||
<input id="lem" placeholder="curator@telecom.kz">
|
<input id="lem" placeholder="curator@telecom.kz">
|
||||||
<input id="lpw" type="password" placeholder="Пароль">
|
<input id="lpw" type="password" placeholder="Пароль">
|
||||||
<p id="lerr" style="color:#DC2626;font-size:12px;display:none;margin-top:-4px;margin-bottom:10px">Неверные учётные данные</p>
|
<p id="lerr" style="color:#DC2626;font-size:12px;display:none;margin-top:-4px;margin-bottom:8px">Неверные учётные данные</p>
|
||||||
|
<p style="font-size:11.5px;color:#94a3b8;margin-bottom:14px;background:#f8fafc;padding:7px 10px;border-radius:6px;line-height:1.45">Демо-доступ: <strong>curator@telecom.kz</strong> / любой пароль<br><span style="font-size:10.5px">Данные хранятся в браузере (localStorage)</span></p>
|
||||||
<button class="btn" style="width:100%;padding:12px;border-radius:8px" onclick="doLogin()">Войти в систему</button>
|
<button class="btn" style="width:100%;padding:12px;border-radius:8px" onclick="doLogin()">Войти в систему</button>
|
||||||
</div></div>
|
</div></div>
|
||||||
<div id="toast"></div>
|
<div id="toast"></div>
|
||||||
@ -179,7 +207,7 @@ th:nth-child(6){min-width:105px;text-align:center}
|
|||||||
<div class="mobile-header">
|
<div class="mobile-header">
|
||||||
<button class="mobile-menu-btn" onclick="document.getElementById('sidebar').classList.toggle('open')">☰</button>
|
<button class="mobile-menu-btn" onclick="document.getElementById('sidebar').classList.toggle('open')">☰</button>
|
||||||
<strong style="font-size:14px;color:#0f172a">QAZAQtelecom HSE</strong>
|
<strong style="font-size:14px;color:#0f172a">QAZAQtelecom HSE</strong>
|
||||||
<span></span>
|
<span class="demo-badge" style="font-size:8px;padding:1px 6px">MVP</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar">
|
<div id="sidebar">
|
||||||
<div class="logo">QAZAQtelecom <span>HSE</span></div>
|
<div class="logo">QAZAQtelecom <span>HSE</span></div>
|
||||||
@ -188,21 +216,37 @@ th:nth-child(6){min-width:105px;text-align:center}
|
|||||||
<a class="active" id="snav_events" onclick="switchTab('events');document.getElementById('sidebar').classList.remove('open')"><span>Мероприятия</span></a>
|
<a class="active" id="snav_events" onclick="switchTab('events');document.getElementById('sidebar').classList.remove('open')"><span>Мероприятия</span></a>
|
||||||
<a id="snav_analytics" onclick="switchTab('analytics');document.getElementById('sidebar').classList.remove('open')"><span>Аналитика</span></a>
|
<a id="snav_analytics" onclick="switchTab('analytics');document.getElementById('sidebar').classList.remove('open')"><span>Аналитика</span></a>
|
||||||
<a id="snav_reports" onclick="switchTab('reports');document.getElementById('sidebar').classList.remove('open')"><span>Отчётность</span></a>
|
<a id="snav_reports" onclick="switchTab('reports');document.getElementById('sidebar').classList.remove('open')"><span>Отчётность</span></a>
|
||||||
<a id="snav_ai" onclick="switchTab('ai');document.getElementById('sidebar').classList.remove('open')"><span>Джарвис</span></a>
|
<a id="snav_ai" onclick="switchTab('ai');document.getElementById('sidebar').classList.remove('open')"><span>HSE-ассистент</span></a>
|
||||||
<a id="snav_hse" onclick="switchTab('hse');document.getElementById('sidebar').classList.remove('open')"><span>HSE.sk.kz</span></a>
|
<a id="snav_hse" onclick="switchTab('hse');document.getElementById('sidebar').classList.remove('open')"><span>HSE.sk.kz</span></a>
|
||||||
<a id="snav_users" onclick="switchTab('users');document.getElementById('sidebar').classList.remove('open')"><span>Учётные записи</span></a>
|
<a id="snav_users" onclick="switchTab('users');document.getElementById('sidebar').classList.remove('open')"><span>Учётные записи</span></a>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="logout"><button class="btn btn-sm btn-r" style="width:100%;border-radius:6px" onclick="doLogout()">Выйти</button></div>
|
<div class="logout"><button class="btn btn-sm btn-r" style="width:100%;border-radius:6px" onclick="doLogout()">Выйти</button></div>
|
||||||
|
<div style="padding:12px 14px 16px;border-top:1px solid rgba(255,255,255,.05);font-size:9.5px;color:#475569;line-height:1.5">
|
||||||
|
<div style="color:#64748B;font-weight:600;margin-bottom:4px;font-size:10px;cursor:pointer" onclick="var d=this.nextElementSibling;d.style.display=d.style.display==='block'?'none':'block'">⚡ MVP — ограничения ▾</div>
|
||||||
|
<div style="display:none;color:#64748B;margin-top:6px;padding-top:6px;border-top:1px solid rgba(255,255,255,.04);line-height:1.55">
|
||||||
|
<div>Для промышленного запуска потребуется:</div>
|
||||||
|
<div style="margin-top:4px;padding-left:10px">
|
||||||
|
• Серверная авторизация (LDAP/SSO)<br>
|
||||||
|
• Централизованное хранилище данных<br>
|
||||||
|
• Ролевая модель и права доступа<br>
|
||||||
|
• Журнал изменений (audit log)<br>
|
||||||
|
• Вложение подтверждающих документов<br>
|
||||||
|
• Интеграция уведомлений (email/Telegram)<br>
|
||||||
|
• API-шлюз HSE.sk.kz
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<div class="top"><h2 id="page_title">Мероприятия</h2>
|
<div class="top"><div style="display:flex;align-items:center;gap:10px"><h2 id="page_title">Мероприятия</h2><span class="demo-badge" style="font-size:9.5px;padding:2px 8px;margin:0">MVP</span></div>
|
||||||
<div class="top-actions">
|
<div class="top-actions">
|
||||||
<span id="stor_ind" class="stor-bar"></span>
|
<span id="stor_ind" class="stor-bar"></span>
|
||||||
<button class="btn btn-sm btn-o" onclick="saveBackup()" title="Создать резервную копию">Резерв</button>
|
<button class="btn btn-sm btn-o" onclick="saveBackup()" title="Создать резервную копию данных">Резерв. копия</button>
|
||||||
<button class="btn btn-sm btn-g" onclick="document.getElementById('fu').click()" title="Восстановить из файла">Восст.</button>
|
<button class="btn btn-sm btn-g" onclick="document.getElementById('fu').click()" title="Восстановить из файла backup">Восстановить</button>
|
||||||
<input type="file" id="fu" accept=".json" style="display:none" onchange="loadBackup(this)">
|
<input type="file" id="fu" accept=".json" style="display:none" onchange="loadBackup(this)">
|
||||||
</div></div>
|
</div></div>
|
||||||
<div id="tab_events">
|
<div id="tab_events">
|
||||||
|
<div class="card" id="ev_kpi" style="padding:14px 18px;margin-bottom:12px"></div>
|
||||||
<div class="fr">
|
<div class="fr">
|
||||||
<input id="sea" placeholder="Поиск..." oninput="renderEv()">
|
<input id="sea" placeholder="Поиск..." oninput="renderEv()">
|
||||||
<select id="fs" onchange="renderEv()"><option value="">Все статусы</option><option value="warn">В процессе</option><option value="done">Исполнено</option><option value="late">Просрочено</option></select>
|
<select id="fs" onchange="renderEv()"><option value="">Все статусы</option><option value="warn">В процессе</option><option value="done">Исполнено</option><option value="late">Просрочено</option></select>
|
||||||
@ -299,7 +343,7 @@ th:nth-child(6){min-width:105px;text-align:center}
|
|||||||
</div></div>
|
</div></div>
|
||||||
<div id="tab_hse" style="display:none">
|
<div id="tab_hse" style="display:none">
|
||||||
<div class="card"><h3>Интеграция с HSE.sk.kz</h3>
|
<div class="card"><h3>Интеграция с HSE.sk.kz</h3>
|
||||||
<div class="demo-note">Для отправки отчётов в HSE.sk.kz требуется локальный интеграционный сервис (localhost:5000). В демо-режиме функция имитирует отправку.</div>
|
<div class="demo-note">Для отправки отчётов в HSE.sk.kz требуется локальный интеграционный сервис (localhost:5000). В текущем MVP-режиме функция имитирует отправку и показывает формат передаваемых данных.</div>
|
||||||
<p style="font-size:13px;color:#64748B;margin-bottom:16px">Направление подписанного сводного отчёта по месяцам в систему hse.sk.kz</p>
|
<p style="font-size:13px;color:#64748B;margin-bottom:16px">Направление подписанного сводного отчёта по месяцам в систему hse.sk.kz</p>
|
||||||
<div style="margin-bottom:12px"><strong>Месяц:</strong>
|
<div style="margin-bottom:12px"><strong>Месяц:</strong>
|
||||||
<input type="month" id="hse_month" style="padding:6px 10px;border:1px solid #E2E8F0;border-radius:6px;margin-left:8px">
|
<input type="month" id="hse_month" style="padding:6px 10px;border:1px solid #E2E8F0;border-radius:6px;margin-left:8px">
|
||||||
@ -1194,7 +1238,7 @@ function saveEv(){
|
|||||||
function switchTab(t){
|
function switchTab(t){
|
||||||
tab=t;
|
tab=t;
|
||||||
var tabs=["events","analytics","reports","ai","users","hse"];
|
var tabs=["events","analytics","reports","ai","users","hse"];
|
||||||
var tn={events:"Мероприятия",analytics:"Аналитика",reports:"Отчётность",ai:"Джарвис",users:"Учётные записи",hse:"HSE.sk.kz"};
|
var tn={events:"Мероприятия",analytics:"Аналитика",reports:"Отчётность",ai:"HSE-ассистент",users:"Учётные записи",hse:"HSE.sk.kz"};
|
||||||
for(var i=0;i<tabs.length;i++){
|
for(var i=0;i<tabs.length;i++){
|
||||||
var el=document.getElementById("tab_"+tabs[i]);
|
var el=document.getElementById("tab_"+tabs[i]);
|
||||||
if(el)el.style.display="none";
|
if(el)el.style.display="none";
|
||||||
@ -1220,6 +1264,25 @@ function daysRem(due){
|
|||||||
now.setHours(0,0,0,0);
|
now.setHours(0,0,0,0);
|
||||||
return Math.floor((d-now)/86400000)
|
return Math.floor((d-now)/86400000)
|
||||||
}
|
}
|
||||||
|
function renderKPI(list){
|
||||||
|
var el=document.getElementById("ev_kpi");
|
||||||
|
if(!el)return;
|
||||||
|
var t=list||evs;
|
||||||
|
var total=t.length,done=0,warn=0,late=0,sumPct=0;
|
||||||
|
for(var i=0;i<t.length;i++){
|
||||||
|
if(t[i].s==="done"){done++;sumPct+=100}
|
||||||
|
else if(t[i].s==="late")late++;
|
||||||
|
else{warn++;sumPct+=(t[i].p||0)}
|
||||||
|
}
|
||||||
|
var avgPct=t.length?Math.round(sumPct/t.length):0;
|
||||||
|
el.innerHTML="<div class='kpi-bar'>"+
|
||||||
|
"<div class='kpi-item kpi-total'><div class='kpi-num'>"+total+"</div><div class='kpi-lbl'>Всего</div></div>"+
|
||||||
|
"<div class='kpi-item kpi-done'><div class='kpi-num'>"+done+"</div><div class='kpi-lbl'>Исполнено</div></div>"+
|
||||||
|
"<div class='kpi-item kpi-warn'><div class='kpi-num'>"+warn+"</div><div class='kpi-lbl'>В процессе</div></div>"+
|
||||||
|
"<div class='kpi-item kpi-late'><div class='kpi-num'>"+late+"</div><div class='kpi-lbl'>Просрочено</div></div>"+
|
||||||
|
"<div class='kpi-item kpi-pct'><div class='kpi-num'>"+avgPct+"%</div><div class='kpi-lbl'>Ср. прогресс</div></div>"+
|
||||||
|
"</div>"
|
||||||
|
}
|
||||||
function renderEv(){
|
function renderEv(){
|
||||||
var sea=document.getElementById("sea").value.toLowerCase().trim();
|
var sea=document.getElementById("sea").value.toLowerCase().trim();
|
||||||
var fs=document.getElementById("fs").value;
|
var fs=document.getElementById("fs").value;
|
||||||
@ -1236,6 +1299,35 @@ function renderEv(){
|
|||||||
if(fs)fl=fl.filter(function(e){return e.s===fs});
|
if(fs)fl=fl.filter(function(e){return e.s===fs});
|
||||||
if(fb!=="")fl=fl.filter(function(e){return String(e.b)===fb});
|
if(fb!=="")fl=fl.filter(function(e){return String(e.b)===fb});
|
||||||
document.getElementById("sc").textContent="Показано: "+fl.length+" из "+evs.length;
|
document.getElementById("sc").textContent="Показано: "+fl.length+" из "+evs.length;
|
||||||
|
renderKPI(fl);
|
||||||
|
var h="";
|
||||||
|
var lastSec=-1;
|
||||||
|
for(var i=0;i<fl.length;i++){
|
||||||
|
var e=fl[i];
|
||||||
|
if(e.sec!==lastSec){
|
||||||
|
if(lastSec!==-1)h+="<tr><td colspan='7' style='padding:4px;border:none'></td></tr>";
|
||||||
|
h+="<tr><td colspan='7' style='padding:0;border:none'><div class='sec-h'>"+esc(secs[e.sec])+"</div></td></tr>";
|
||||||
|
lastSec=e.sec
|
||||||
|
}
|
||||||
|
var dr=daysRem(e.due);
|
||||||
|
var rowCl=e.s==="done"?"tr-green":dr<=0&&e.s!=="done"?"tr-red":dr<=14?"tr-amber":"";
|
||||||
|
var cl=stc[e.s]||"w";
|
||||||
|
var drText=dr<=0&&e.s!=="done"?"Просрочено на "+Math.abs(dr)+" дн.":e.s==="done"?"Готово":dr===999?"\u2014":dr+" дн.";
|
||||||
|
var hasSub=e.sub&&e.sub.length>0;
|
||||||
|
var shortT=cleanTitle(e);
|
||||||
|
h+="<tr class='"+rowCl+"'>";
|
||||||
|
h+="<td style='font-weight:700;font-size:12px;color:#475569'>"+e.id+"</td>";
|
||||||
|
h+="<td><div style='font-size:12px;line-height:1.45;color:#1e293b;max-width:380px'>"+esc(shortT)+"</div><div style='font-size:10.5px;color:#94a3b8;margin-top:3px'>"+esc(e.dname)+(shortT!==e.t?" · <span style='color:#94a3b8'>развернуть</span>":"")+"</div></td>";
|
||||||
|
h+="<td style='font-size:11.5px;color:#475569;white-space:normal;max-width:150px;line-height:1.35'>"+(hasSub?"<span style='color:#0284c7;font-weight:600'>"+e.sub.length+" подпунктов</span>":esc(nl2c(e.r)))+"</td>";
|
||||||
|
h+="<td style='font-size:12px;white-space:nowrap'><strong>"+e.due+"</strong> <span style='font-size:10.5px;color:#94a3b8'>("+drText+")</span></td>";
|
||||||
|
if(hasSub){h+="<td style='text-align:center'><span class='badge b'>"+(e.sub.length+" п/п")+"</span></td><td style='text-align:right'>"+(cu&&cu.bg===0?"<button class='act-btn act-btn-edit' title='Редактировать мероприятие' onclick='editEventText("+e.id+")'>Редакт.</button>":"")+"</td>"}
|
||||||
|
else{
|
||||||
|
h+="<td style='text-align:center'><span class='badge "+cl+"'>"+stn[e.s]+"</span></td>";
|
||||||
|
var eb=cu&&cu.bg===0?"<button class='act-btn act-btn-edit' title='Редактировать мероприятие' onclick='editEventText("+e.id+")'>Редакт.</button>":"";h+="<td style='text-align:right'><div class='act-group'>"+eb+"<button class='act-btn act-btn-open' title='Открыть карточку мероприятия' onclick='openEv("+e.id+")'>Открыть</button></div></td>"
|
||||||
|
}
|
||||||
|
if(fs)fl=fl.filter(function(e){return e.s===fs});
|
||||||
|
if(fb!=="")fl=fl.filter(function(e){return String(e.b)===fb});
|
||||||
|
document.getElementById("sc").textContent="Показано: "+fl.length+" из "+evs.length;
|
||||||
var h="";
|
var h="";
|
||||||
var lastSec=-1;
|
var lastSec=-1;
|
||||||
for(var i=0;i<fl.length;i++){
|
for(var i=0;i<fl.length;i++){
|
||||||
@ -1267,15 +1359,15 @@ function renderEv(){
|
|||||||
var ss=e.s==="wait"?"warn":e.s;
|
var ss=e.s==="wait"?"warn":e.s;
|
||||||
var scl=stc[ss]||"a";
|
var scl=stc[ss]||"a";
|
||||||
var subR=subResp(e.r,e.sub[si].l);if(!subR||subR.length<2)subR=nl2c(e.r);h+="<tr class='"+rowCl+" sub-item-row'><td style='padding-left:24px;font-size:11px;font-weight:700;color:#0284c7'>"+e.id+"."+esc(e.sub[si].l)+"</td>";
|
var subR=subResp(e.r,e.sub[si].l);if(!subR||subR.length<2)subR=nl2c(e.r);h+="<tr class='"+rowCl+" sub-item-row'><td style='padding-left:24px;font-size:11px;font-weight:700;color:#0284c7'>"+e.id+"."+esc(e.sub[si].l)+"</td>";
|
||||||
h+="<td style='font-size:11.5px;line-height:1.4;color:#334155;max-width:360px'>"+esc(e.sub[si].t)+"</td>";
|
h+="<td style='font-size:11.5px;line-height:1.4;color:#334155;max-width:340px'>"+esc(e.sub[si].t)+"</td>";
|
||||||
h+="<td style='font-size:11px;color:#64748B;line-height:1.35'>"+esc(e.sub[si].r||sr)+"</td>";
|
h+="<td style='font-size:11px;color:#64748B;line-height:1.35'>"+esc(e.sub[si].r||sr)+"</td>";
|
||||||
h+="<td style='font-size:11.5px;color:#475569'>"+(e.sub[si].due||e.due)+"</td>";
|
h+="<td style='font-size:11.5px;color:#475569'>"+(e.sub[si].due||e.due)+"</td>";
|
||||||
h+="<td style='text-align:center'><span class='badge "+scl+"' style='font-size:10px'>"+stn[ss]+"</span></td>";
|
h+="<td style='text-align:center'><span class='badge "+scl+"' style='font-size:10px'>"+stn[ss]+"</span></td>";
|
||||||
h+="<td style='text-align:right'><button class='act-btn act-btn-open' onclick='openEv("+e.id+","+si+")'>Откр.</button></td></tr>"
|
h+="<td style='text-align:right'><button class='act-btn act-btn-open' title='Открыть карточку' onclick='openEv("+e.id+","+si+")'>Открыть</button></td></tr>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!h)h="<p style='color:#64748B;padding:20px;text-align:center'>Нет мероприятий</p>";
|
if(!h)h="<div class='empty-state'><div class='empty-icon'>📋</div><div class='empty-text'>По выбранным фильтрам мероприятий не найдено</div><div class='empty-hint'>Попробуйте изменить параметры поиска или сбросить фильтры</div></div>";
|
||||||
document.getElementById("ev_content").innerHTML="<table><tr><th>N</th><th>Мероприятие</th><th>Ответственные</th><th>Срок</th><th>Статус</th><th></th></tr>"+h+"</table>"
|
document.getElementById("ev_content").innerHTML="<table><tr><th>N</th><th>Мероприятие</th><th>Ответственные</th><th>Срок</th><th>Статус</th><th></th></tr>"+h+"</table>"
|
||||||
}
|
}
|
||||||
function togSub(id){
|
function togSub(id){
|
||||||
@ -1455,7 +1547,10 @@ function renderReports(){
|
|||||||
var dd=document.getElementById("rp_month_dd");if(dd&&!dd.options.length){var mn=["\u042F\u043D\u0432\u0430\u0440\u044C","\u0424\u0435\u0432\u0440\u0430\u043B\u044C","\u041C\u0430\u0440\u0442","\u0410\u043F\u0440\u0435\u043B\u044C","\u041C\u0430\u0439","\u0418\u044E\u043D\u044C","\u0418\u044E\u043B\u044C","\u0410\u0432\u0433\u0443\u0441\u0442","\u0421\u0435\u043D\u0442\u044F\u0431\u0440\u044C","\u041E\u043A\u0442\u044F\u0431\u0440\u044C","\u041D\u043E\u044F\u0431\u0440\u044C","\u0414\u0435\u043A\u0430\u0431\u0440\u044C"];var now=new Date();var cm=now.getMonth();var cy=now.getFullYear();var h="";var sv=cy+"-"+String(cm+1).padStart(2,"0");for(var y=2026;y<=2027;y++){for(var m=0;m<12;m++){var val=y+"-"+String(m+1).padStart(2,"0");h+="<option value=\""+val+"\""+(val===sv?" selected":"")+">"+mn[m]+" "+y+"</option>"}}dd.innerHTML=h}
|
var dd=document.getElementById("rp_month_dd");if(dd&&!dd.options.length){var mn=["\u042F\u043D\u0432\u0430\u0440\u044C","\u0424\u0435\u0432\u0440\u0430\u043B\u044C","\u041C\u0430\u0440\u0442","\u0410\u043F\u0440\u0435\u043B\u044C","\u041C\u0430\u0439","\u0418\u044E\u043D\u044C","\u0418\u044E\u043B\u044C","\u0410\u0432\u0433\u0443\u0441\u0442","\u0421\u0435\u043D\u0442\u044F\u0431\u0440\u044C","\u041E\u043A\u0442\u044F\u0431\u0440\u044C","\u041D\u043E\u044F\u0431\u0440\u044C","\u0414\u0435\u043A\u0430\u0431\u0440\u044C"];var now=new Date();var cm=now.getMonth();var cy=now.getFullYear();var h="";var sv=cy+"-"+String(cm+1).padStart(2,"0");for(var y=2026;y<=2027;y++){for(var m=0;m<12;m++){var val=y+"-"+String(m+1).padStart(2,"0");h+="<option value=\""+val+"\""+(val===sv?" selected":"")+">"+mn[m]+" "+y+"</option>"}}dd.innerHTML=h}
|
||||||
var fl=getFilteredEvs();
|
var fl=getFilteredEvs();
|
||||||
var cnt=document.getElementById("rp_count");
|
var cnt=document.getElementById("rp_count");
|
||||||
if(cnt)cnt.textContent="Выбрано мероприятий: "+fl.length;
|
if(cnt){
|
||||||
|
var pct=fl.length&&evs.length?Math.round(fl.length/evs.length*100):0;
|
||||||
|
cnt.innerHTML="Будет выгружено: <strong>"+fl.length+"</strong> мероприятий за выбранный период "+(pct?"("+pct+"% от всех)":"")
|
||||||
|
}
|
||||||
document.getElementById("rp_preview").innerHTML=""
|
document.getElementById("rp_preview").innerHTML=""
|
||||||
}
|
}
|
||||||
function dlCSV(){
|
function dlCSV(){
|
||||||
@ -1532,7 +1627,7 @@ function hseSend(){
|
|||||||
fetch("http://localhost:5000/api/hse/send",{method:"POST",headers:{"Content-Type":"application/json","Authorization":"Bearer hse-integration"},body:JSON.stringify({month:month,api_key:apiKey,format:fmt,report:payload})}).then(function(r){return r.json()}).then(function(d){
|
fetch("http://localhost:5000/api/hse/send",{method:"POST",headers:{"Content-Type":"application/json","Authorization":"Bearer hse-integration"},body:JSON.stringify({month:month,api_key:apiKey,format:fmt,report:payload})}).then(function(r){return r.json()}).then(function(d){
|
||||||
if(d.ok){result.innerHTML="<span style='color:#10B981'>Отчёт за "+month+" отправлен в HSE.sk.kz</span>"}
|
if(d.ok){result.innerHTML="<span style='color:#10B981'>Отчёт за "+month+" отправлен в HSE.sk.kz</span>"}
|
||||||
else{result.innerHTML="<span style='color:#EF4444'>Ошибка: "+(d.error||"соединение")+"</span>"}
|
else{result.innerHTML="<span style='color:#EF4444'>Ошибка: "+(d.error||"соединение")+"</span>"}
|
||||||
}).catch(function(e){result.innerHTML="<span style='color:#EF4444'>Сервер не запущен. Запустите <code>python3 server.py</code></span>"}).finally(function(){btn.disabled=false;btn.textContent="Отправить отчёт в HSE.sk.kz"})
|
}).catch(function(e){result.innerHTML="<span style='color:#d97706'>⚠ Интеграционный сервис недоступен (localhost:5000). Для промышленного запуска требуется развёрнутый шлюз HSE.sk.kz.</span>"}).finally(function(){btn.disabled=false;btn.textContent="Отправить отчёт в HSE.sk.kz"})
|
||||||
}catch(e){}
|
}catch(e){}
|
||||||
}
|
}
|
||||||
var aiGreeted=false;
|
var aiGreeted=false;
|
||||||
@ -1542,7 +1637,7 @@ function renderAI(){
|
|||||||
var box=document.getElementById("ai_chat");
|
var box=document.getElementById("ai_chat");
|
||||||
if(box){
|
if(box){
|
||||||
box.innerHTML="";
|
box.innerHTML="";
|
||||||
addMsg("b","Джарвис к вашим услугам. Я анализирую 35 мероприятий ПБ по 9 филиалам. Спросите: сводка, просроченные, риски, рейтинг, аудит, прогноз, советник.","Джарвис")
|
addMsg("b","HSE-ассистент к вашим услугам. Я анализирую 35 мероприятий ПБ по 9 филиалам. Спросите: сводка, просроченные, риски, рейтинг, аудит, прогноз, советник.","HSE-ассистент")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1666,9 +1761,9 @@ function aiResp(q){
|
|||||||
}else{ans="Пункт N"+num+" не найден"}
|
}else{ans="Пункт N"+num+" не найден"}
|
||||||
}else{ans="Напиши номер пункта, например: пункт 5"}
|
}else{ans="Напиши номер пункта, например: пункт 5"}
|
||||||
}else{
|
}else{
|
||||||
ans="Я — Джарвис, ваш аналитический ассистент. Могу ответить:\n\n• сводка — общая статистика\n• просроченные — список просрочек\n• риски — зона риска (<30 дней)\n• рейтинг — рейтинг филиалов\n• аудит — полный аудит\n• прогноз — прогноз исполнения\n• статус — состояние по разделам\n• план — план действий и рекомендации\n• пункт N — детали конкретного мероприятия"
|
ans="Я — HSE-ассистент, ваш аналитический помощник. Могу ответить:\n\n• сводка — общая статистика\n• просроченные — список просрочек\n• риски — зона риска (<30 дней)\n• рейтинг — филиалы\n• аудит — полный аудит\n• прогноз — прогноз исполнения\n• статус — состояние по разделам\n• план — рекомендации\n• пункт N — детали мероприятия"
|
||||||
}
|
}
|
||||||
addMsg("b",ans,"Джарвис")
|
addMsg("b",ans,"HSE-ассистент")
|
||||||
}
|
}
|
||||||
function renderUsers(){
|
function renderUsers(){
|
||||||
if(!cu||cu.bg!==0){document.getElementById("tab_users").innerHTML="<div class='card'><p style='color:#EF4444'>\u0414\u043E\u0441\u0442\u0443\u043F \u0437\u0430\u043F\u0440\u0435\u0449\u0451\u043D</p></div>";return}
|
if(!cu||cu.bg!==0){document.getElementById("tab_users").innerHTML="<div class='card'><p style='color:#EF4444'>\u0414\u043E\u0441\u0442\u0443\u043F \u0437\u0430\u043F\u0440\u0435\u0449\u0451\u043D</p></div>";return}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user