samruk-ai-agent/viewer.html

117 lines
7.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Просмотр бекапа ИИ-Агент ПБ</title>
<style>
:root{--ink:#0F1218;--cyan:#00E5FF;--cyan-50:#E8FCFF;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7;--gray-200:#E5E7EB;--green:#10B981;--red:#EF4444;--amber:#F59E0B;--blue:#3B82F6}
*{box-sizing:border-box;margin:0;padding:0}
body{font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--gray-100);min-height:100vh;padding:40px 24px}
.container{max-width:900px;margin:0 auto}
h1{font-size:24px;font-weight:800;margin-bottom:8px}h1 span{color:var(--cyan)}
p.sub{color:var(--gray-500);margin-bottom:24px}
.drop-zone{border:3px dashed var(--cyan);border-radius:16px;padding:60px 24px;text-align:center;cursor:pointer;background:var(--white);margin-bottom:24px;transition:.15s}
.drop-zone:hover{background:var(--cyan-50)}
.drop-zone .icon{font-size:48px;display:block;margin-bottom:8px}
.drop-zone .txt{font-size:18px;font-weight:600;color:var(--ink);margin-bottom:4px}
.drop-zone .hint{font-size:14px;color:var(--gray-500)}
.ev{border:1px solid var(--gray-200);border-radius:12px;padding:20px;margin-bottom:16px;background:var(--white)}
.ev h3{font-size:16px;margin-bottom:8px}
.meta{display:flex;gap:16px;flex-wrap:wrap;font-size:12px;color:var(--gray-500);margin-bottom:8px}
.meta strong{color:var(--ink)}
.badge{display:inline-block;padding:3px 8px;border-radius:4px;font-size:11px;font-weight:700}
.g{background:#D1FAE5;color:#065F46}.a{background:#FEF3C7;color:#92400E}.r{background:#FEE2E2;color:#991B1B}.w{background:#eee;color:#666}
.month{background:var(--gray-100);padding:10px 14px;border-radius:8px;margin:8px 0}
.month strong{font-size:13px;color:var(--ink)}
.month .rep{font-size:13px;color:var(--gray-500);margin:4px 0}
.file-item{display:flex;align-items:center;gap:8px;padding:4px 0;font-size:13px}
.file-item a{color:var(--ink);font-weight:600;cursor:pointer;text-decoration:underline}
.file-item a:hover{color:var(--cyan)}
.file-item .sz{font-size:11px;color:var(--gray-500)}
.sub-blk{border-left:3px solid var(--cyan);padding-left:14px;margin:8px 0 8px 12px}
.sub-blk strong{font-size:13px}
.no-data{text-align:center;padding:40px;color:var(--gray-500);font-size:16px}
</style>
</head>
<body>
<div class="container">
<h1><span>📂 Просмотр бекапа</span> ИИ-Агент ПБ</h1>
<p class="sub">Загрузите файл backup_pb_*.json, сохранённый из платформы</p>
<div class="drop-zone" id="dropZone" onclick="document.getElementById('fileInput').click()">
<span class="icon">📥</span>
<div class="txt">Нажмите или перетащите файл бекапа</div>
<div class="hint">Файл backup_pb_YYYY-MM-DD.json</div>
</div>
<input type="file" id="fileInput" accept=".json" style="display:none" onchange="loadBackup(this.files[0])">
<div id="content"></div>
</div>
<script>
var statusMap={done:"Исполнено",warn:"На контроле",late:"Просрочено",wait:"В процессе"};
var sections=["I. Люди","II. Оборудование","III. Аварии и ЧС","IV. Информ. работа","V. ИИ и цифровизация"];
var branches=["Дирекция ПБ","Дивизион «Сеть»","Корпоративный бизнес","Розничный бизнес","Сервисная фабрика","Телеком Комплект","Корпоративный университет","Управление проектами","Цифровой бизнес"];
var monthNames=["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"];
function esc(s){return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}
function M(k){var p=k.split("-");return monthNames[parseInt(p[1])-1]+" "+p[0]}
var dropZone=document.getElementById("dropZone");
dropZone.addEventListener("dragover",function(e){e.preventDefault();this.style.background="var(--cyan-50)"});
dropZone.addEventListener("dragleave",function(e){this.style.background="var(--white)"});
dropZone.addEventListener("drop",function(e){e.preventDefault();this.style.background="var(--white)";loadBackup(e.dataTransfer.files[0])});
function loadBackup(file){
if(!file){alert("Выберите файл");return}
var reader=new FileReader();
reader.onload=function(ev){
try{
var data=JSON.parse(ev.target.result);
if(!data.events||!data.files){alert("❌ Неверный формат файла");return}
renderBackup(data);
}catch(e){alert("❌ Ошибка чтения файла: "+e.message)}
};
reader.readAsText(file);
}
function getMD(data,id,si){var k=si>=0?'sf_'+id+'_s'+si:'sf_'+id;var raw=data.files[k];return raw?JSON.parse(raw):{}}
function renderBackup(data){
var h='';
if(data.date)h+='<p style="margin-bottom:16px;color:var(--gray-500)">Дата бекапа: <strong>'+new Date(data.date).toLocaleString()+'</strong> · Мероприятий: '+data.events.length+'</p>';
data.events.forEach(function(e){
var scls={done:"g",warn:"a",late:"r",wait:"w"}[e.s];
h+='<div class="ev"><h3>'+e.id+'. '+esc(e.t)+'</h3>';
h+='<div class="meta"><span>Раздел: <strong>'+sections[e.sec]+'</strong></span><span>Дивизион: <strong>'+branches[e.b]+'</strong></span><span>Срок: <strong>'+e.due+'</strong></span><span>Факт: <strong>'+(e.done||"—")+'</strong></span><span>Прогресс: <strong>'+e.p+'%</strong></span><span class="badge '+scls+'">'+statusMap[e.s]+'</span></div>';
// Main event files
var md=getMD(data,e.id,-1);
for(var key in md){if(md.hasOwnProperty(key)&&md[key]){
var d=md[key];if(d.report||(d.files&&d.files.length)){
h+='<div class="month"><strong>'+M(key)+'</strong>';
if(d.report)h+='<div class="rep">'+esc(d.report)+'</div>';
if(d.files&&d.files.length)d.files.forEach(function(f){h+='<div class="file-item"><a href="'+f.data+'" download="'+esc(f.name)+'">📄 '+esc(f.name)+'</a><span class="sz">'+esc(f.desc||'')+' ('+(f.size/1024).toFixed(0)+' КБ)</span></div>'});
h+='</div>'}}}}
// Sub-items
if(e.sub)e.sub.forEach(function(s,i){
var sd=getMD(data,e.id,i),hasSub=false;
for(var k in sd){if(sd.hasOwnProperty(k)&&sd[k]&&(sd[k].report||(sd[k].files&&sd[k].files.length)))hasSub=true}
if(!hasSub)return;
h+='<div class="sub-blk"><strong>'+s.l+') '+esc(s.t)+'</strong>';
for(var key in sd){if(sd.hasOwnProperty(key)&&sd[key]){
var d=sd[key];if(d.report||(d.files&&d.files.length)){
h+='<div class="month"><strong>'+M(key)+'</strong>';
if(d.report)h+='<div class="rep">'+esc(d.report)+'</div>';
if(d.files&&d.files.length)d.files.forEach(function(f){h+='<div class="file-item"><a href="'+f.data+'" download="'+esc(f.name)+'">📄 '+esc(f.name)+'</a><span class="sz">'+esc(f.desc||'')+' ('+(f.size/1024).toFixed(0)+' КБ)</span></div>'});
h+='</div>'}}}
h+='</div>'});
h+='</div>';
});
document.getElementById("content").innerHTML=h||'<div class="no-data">Нет данных для отображения</div>';
document.getElementById("dropZone").style.display="none";
}
</script>
</body>
</html>