ya-zagruzil-fayly-sk-glpi-pd/index.html

392 lines
18 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>Дашборд GLPI — Заявки Service Desk</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<style>
:root{--ink:#0F1218;--cyan:#00E5FF;--cyan-50:#E8FCFF;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7;--success:#10B981;--warning:#F59E0B;--danger:#EF4444}
*{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)}
.header{background:var(--ink);color:var(--white);padding:24px 0}
.header h1{font-size:28px;font-weight:700;margin-bottom:8px}
.header p{color:#9aa3b2;font-size:14px}
.container{max-width:1400px;margin:0 auto;padding:24px}
.metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin-bottom:24px}
.metric{background:var(--white);border-radius:12px;padding:20px;box-shadow:0 1px 3px rgba(0,0,0,0.08)}
.metric-label{font-size:13px;color:var(--gray-500);margin-bottom:8px}
.metric-value{font-size:32px;font-weight:700;color:var(--ink)}
.metric-sub{font-size:12px;color:var(--gray-500);margin-top:4px}
.metric.success .metric-value{color:var(--success)}
.metric.warning .metric-value{color:var(--warning)}
.metric.danger .metric-value{color:var(--danger)}
.charts{display:grid;grid-template-columns:repeat(auto-fit,minmax(400px,1fr));gap:24px;margin-bottom:24px}
.chart-card{background:var(--white);border-radius:12px;padding:20px;box-shadow:0 1px 3px rgba(0,0,0,0.08)}
.chart-card h3{font-size:16px;font-weight:600;margin-bottom:16px}
.chart-container{position:relative;height:300px}
.filters{background:var(--white);border-radius:12px;padding:16px;margin-bottom:24px;box-shadow:0 1px 3px rgba(0,0,0,0.08)}
.filters-row{display:flex;gap:16px;flex-wrap:wrap;align-items:center}
.filter-group{display:flex;flex-direction:column;gap:4px}
.filter-group label{font-size:12px;color:var(--gray-500)}
.filter-group select,.filter-group input{padding:8px 12px;border:1px solid var(--gray-100);border-radius:6px;font-size:14px;background:var(--white)}
.top-lists{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-bottom:24px}
.top-card{background:var(--white);border-radius:12px;padding:20px;box-shadow:0 1px 3px rgba(0,0,0,0.08)}
.top-card h3{font-size:16px;font-weight:600;margin-bottom:16px}
.top-item{display:flex;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--gray-100)}
.top-item:last-child{border-bottom:none}
.top-name{font-size:13px;flex:1}
.top-val{font-size:13px;font-weight:600;color:var(--cyan);background:var(--ink);padding:2px 8px;border-radius:4px}
.table-card{background:var(--white);border-radius:12px;padding:20px;box-shadow:0 1px 3px rgba(0,0,0,0.08)}
.table-card h3{font-size:16px;font-weight:600;margin-bottom:16px}
.table-wrap{overflow-x:auto}
table{width:100%;border-collapse:collapse;font-size:13px}
th{background:var(--gray-100);padding:10px;text-align:left;font-weight:600;font-size:12px}
td{padding:10px;border-bottom:1px solid var(--gray-100)}
tr:hover{background:var(--cyan-50)}
.status{padding:4px 8px;border-radius:4px;font-size:11px;font-weight:600;display:inline-block}
.status-closed{background:var(--success);color:var(--white)}
.status-assigned{background:var(--warning);color:var(--ink)}
.status-planned{background:#3B82F6;color:var(--white)}
.status-waiting{background:var(--gray-500);color:var(--white)}
.badge{padding:2px 6px;border-radius:3px;font-size:11px;background:var(--gray-100)}
漏斗{display:flex;gap:8px;margin-bottom:24px}
.funnel-step{flex:1;background:var(--white);border-radius:12px;padding:16px;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,0.08);position:relative}
.funnel-step:not(:last-child)::after{content:"→";position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-size:20px;color:var(--gray-500)}
.funnel-num{font-size:24px;font-weight:700;color:var(--cyan)}
.funnel-label{font-size:12px;color:var(--gray-500);margin-top:4px}
@media(max-width:768px){.charts{grid-template-columns:1fr}.top-lists{grid-template-columns:1fr}.漏斗{flex-wrap:wrap}.funnel-step{min-width:140px}}
</style>
</head>
<body>
<section class="header">
<div class="container">
<h1>📊 Дашборд заявок Service Desk</h1>
<p>Аналитика по системе GLPI | Данные от 20.05.2026</p>
</div>
</section>
<div class="container">
<div class="filters">
<div class="filters-row">
<div class="filter-group">
<label>Период</label>
<select id="periodFilter">
<option value="all">Весь период</option>
<option value="7">Последние 7 дней</option>
<option value="30">Последние 30 дней</option>
<option value="year">2026 год</option>
</select>
</div>
<div class="filter-group">
<label>Статус</label>
<select id="statusFilter">
<option value="all">Все статусы</option>
<option value="closed">Закрыто</option>
<option value="assigned">Назначена</option>
<option value="planned">Запланирована</option>
<option value="waiting">В ожидании</option>
</select>
</div>
<div class="filter-group">
<label>Департамент</label>
<select id="deptFilter">
<option value="all">Все</option>
<option value="dit">ДИТ</option>
<option value="duis">ДУИС</option>
<option value="doc">Документооборот</option>
</select>
</div>
<div class="filter-group">
<label>Поиск</label>
<input type="text" id="searchInput" placeholder="Поиск по заявкам...">
</div>
</div>
</div>
<div class="metrics">
<div class="metric">
<div class="metric-label">ВСЕГО ЗАЯВОК</div>
<div class="metric-value">992</div>
<div class="metric-sub">за период</div>
</div>
<div class="metric success">
<div class="metric-label">ЗАКРЫТО</div>
<div class="metric-value">990</div>
<div class="metric-sub">99.8% успешных</div>
</div>
<div class="metric warning">
<div class="metric-label">В РАБОТЕ</div>
<div class="metric-value">3</div>
<div class="metric-sub">актуальные</div>
</div>
<div class="metric">
<div class="metric-label">SLA: РЕАКЦИЯ</div>
<div class="metric-value">63.4</div>
<div class="metric-sub">мин среднее</div>
</div>
<div class="metric danger">
<div class="metric-label">SLA: ЗАКРЫТИЕ</div>
<div class="metric-value">251.3</div>
<div class="metric-sub">мин среднее</div>
</div>
</div>
<div class="漏斗">
<div class="funnel-step">
<div class="funnel-num">4334</div>
<div class="funnel-label">Новый</div>
</div>
<div class="funnel-step">
<div class="funnel-num">4373</div>
<div class="funnel-label">Назначена</div>
</div>
<div class="funnel-step">
<div class="funnel-num">481</div>
<div class="funnel-label">В ожидании</div>
</div>
<div class="funnel-step">
<div class="funnel-num">59</div>
<div class="funnel-label">Запланирована</div>
</div>
<div class="funnel-step">
<div class="funnel-num" style="color:var(--success)">4416</div>
<div class="funnel-label">Закрыто</div>
</div>
</div>
<div class="charts">
<div class="chart-card">
<h3>📈 Динамика заявок по месяцам</h3>
<div class="chart-container">
<canvas id="trendChart"></canvas>
</div>
</div>
<div class="chart-card">
<h3>🎯 Распределение по статусам</h3>
<div class="chart-container">
<canvas id="statusChart"></canvas>
</div>
</div>
<div class="chart-card">
<h3>👥 ТОП-10 исполнителей</h3>
<div class="chart-container">
<canvas id="executorsChart"></canvas>
</div>
</div>
<div class="chart-card">
<h3>🏢 Заявки по департаментам</h3>
<div class="chart-container">
<canvas id="deptChart"></canvas>
</div>
</div>
</div>
<div class="top-lists">
<div class="top-card">
<h3>🏆 ТОП исполнителей</h3>
<div id="executorsList"></div>
</div>
<div class="top-card">
<h3>📢 ТОП заявителей</h3>
<div id="requestersList"></div>
</div>
</div>
<div class="table-card">
<h3>📋 Последние заявки</h3>
<div class="table-wrap">
<table id="ticketsTable">
<thead>
<tr>
<th>ID</th>
<th>Заголовок</th>
<th>Организация</th>
<th>Дата</th>
<th>Статус</th>
<th>Инициатор</th>
<th>Исполнитель</th>
<th>Канал</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<script>
const DATA = {
tickets: [
{id:68217,title:"Заявки на доработку",org:"SK SAP",date:"20.05.2026",status:"Назначена",initiator:"Арын Ерсаин",executor:"Салимгожинов Тамерлан",channel:"-"},
{id:68203,title:"Запрос на доступ",org:"Service Desk",date:"19.05.2026",status:"Закрыто",initiator:"Абраев Жумабек",executor:"Карипбаев Бауыржан",channel:"E-Mail"},
{id:68202,title:"Запрос на доступ",org:"Service Desk",date:"19.05.2026",status:"Закрыто",initiator:"Абраев Жумабек",executor:"Карипбаев Бауыржан",channel:"E-Mail"},
{id:68201,title:"Запрос на доступ",org:"Service Desk",date:"19.05.2026",status:"Закрыто",initiator:"Абраев Жумабек",executor:"Карипбаев Бауыржан",channel:"E-Mail"},
{id:68190,title:"Заявки на обслуживание",org:"SK SAP",date:"19.05.2026",status:"Закрыто",initiator:"Арын Ерсаин",executor:"Берденова Асыл",channel:"-"},
{id:68188,title:"Заявки на обслуживание",org:"SK SAP",date:"19.05.2026",status:"Закрыто",initiator:"Арын Ерсаин",executor:"Берденова Асыл",channel:"-"},
{id:68186,title:"Заявки на обслуживание",org:"SK SAP",date:"19.05.2026",status:"Закрыто",initiator:"Арын Ерсаин",executor:"Берденова Асыл",channel:"-"},
{id:68185,title:"Заявки на обслуживание",org:"SK SAP",date:"19.05.2026",status:"Закрыто",initiator:"Арын Ерсаин",executor:"Берденова Асыл",channel:"-"},
{id:68184,title:"Заявки на обслуживание",org:"SK SAP",date:"19.05.2026",status:"Закрыто",initiator:"Арын Ерсаин",executor:"Берденова Асыл",channel:"-"},
{id:68183,title:"Заявки на обслуживание",org:"SK SAP",date:"19.05.2026",status:"Закрыто",initiator:"Арын Ерсаин",executor:"Берденова Асыл",channel:"-"},
{id:68179,title:"Запрос на обслуживание",org:"Service Desk",date:"19.05.2026",status:"Закрыто",initiator:"Арын Ерсаин",executor:"Амангалиев Азамат",channel:"Мессенджеры"},
{id:68172,title:"Запрос на доступ",org:"SK SIP телефония",date:"19.05.2026",status:"Закрыто",initiator:"Арын Ерсаин",executor:"Жолдасбек Ердос",channel:"-"},
{id:68171,title:"Запрос на доступ",org:"Service Desk",date:"19.05.2026",status:"Закрыто",initiator:"Мукашев Данияр",executor:"Сулейменова Дина",channel:"E-Mail"},
{id:68170,title:"Запрос на доступ",org:"Service Desk",date:"19.05.2026",status:"Закрыто",initiator:"Прназаров Алибек",executor:"Сулейменова Дина",channel:"E-Mail"},
{id:68169,title:"Запрос на доступ",org:"Service Desk",date:"19.05.2026",status:"Закрыто",initiator:"Прназаров Алибек",executor:"Сулейменова Дина",channel:"E-Mail"},
{id:68165,title:"Заявки на доступ",org:"Service Desk",date:"19.05.2026",status:"Закрыто",initiator:"Арын Ерсаин",executor:"Жусупов Галым",channel:"-"}
],
executors: [
{name:"Жусупов Галым",count:150},
{name:"Арын Ерсаин",count:2891},
{name:"Магперов Куаныш",count:1881},
{name:"Абраев Жумабек",count:1000},
{name:"Адилхан Максат",count:580},
{name:"Мукашев Данияр",count:470},
{name:"Неталина Асем",count:350},
{name:"Бузаубаев Парасат",count:140},
{name:"Рахимжанова Меруерт",count:140},
{name:"Мухтар Дидар",count:120}
],
requesters: [
{name:"Арын Ерсаин",count:2571},
{name:"Магперов Куаныш",count:1521},
{name:"Абраев Жумабек",count:1060},
{name:"Адилхан Максат",count:730},
{name:"Мукашев Данияр",count:420},
{name:"Неталина Асем",count:290},
{name:"Бузаубаев Парасат",count:110},
{name:"Мухтар Дидар",count:90},
{name:"Табултаев Адиль",count:80},
{name:"Ибраев Максат",count:60}
],
months: [
{m:"Янв",v:183},{m:"Фев",v:390},{m:"Мар",v:445},
{m:"Апр",v:510},{m:"Май",v:342},{m:"Июн",v:399},
{m:"Июл",v:474},{m:"Авг",v:440},{m:"Сен",v:427},
{m:"Окт",v:438},{m:"Ноя",v:419},{m:"Дек",v:444}
],
depts: [
{name:"ДИТ",v:149},{name:"ДУИС",v:12},{name:"Документооборот",v:7},
{name:"Управление рисками",v:2},{name:"Управление персоналом",v:1}
]
};
const statusDist = {closed:990,assigned:2,planned:1,waiting:0};
function getStatusClass(s){
if(s==="Закрыто") return "status-closed";
if(s==="Назначена") return "status-assigned";
if(s==="Запланирована") return "status-planned";
return "status-waiting";
}
function renderTable(data){
const tbody=document.querySelector("#ticketsTable tbody");
tbody.innerHTML=data.map(t=>
`<tr>
<td><span class="badge">#${t.id}</span></td>
<td>${t.title}</td>
<td>${t.org}</td>
<td>${t.date}</td>
<td><span class="status ${getStatusClass(t.status)}">${t.status}</span></td>
<td>${t.initiator}</td>
<td>${t.executor}</td>
<td>${t.channel}</td>
</tr>`
).join("");
}
function renderTopList(data,containerId){
document.getElementById(containerId).innerHTML=data.map(i=>
`<div class="top-item">
<div class="top-name">${i.name}</div>
<div class="top-val">${i.count}</div>
</div>`
).join("");
}
new Chart(document.getElementById("trendChart"),{
type:"line",
data:{
labels:DATA.months.map(m=>m.m),
datasets:[{
label:"Заявки",
data:DATA.months.map(m=>m.v),
borderColor:"#00E5FF",
backgroundColor:"rgba(0,229,255,0.1)",
fill:true,
tension:0.4
}]
},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false}},scales:{y:{beginAtZero:false}}}
});
new Chart(document.getElementById("statusChart"),{
type:"doughnut",
data:{
labels:["Закрыто","Назначена","Запланирована","В ожидании"],
datasets:[{
data:[statusDist.closed,statusDist.assigned,59,statusDist.waiting],
backgroundColor:["#10B981","#F59E0B","#3B82F6","#6B7280"]
}]
},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:"right"}}}
});
new Chart(document.getElementById("executorsChart"),{
type:"bar",
data:{
labels:DATA.executors.slice(0,10).map(e=>e.name.split(" ")[0]+" "+e.name.split(" ")[1].charAt(0)+"."),
datasets:[{
label:"Заявок",
data:DATA.executors.slice(0,10).map(e=>e.count),
backgroundColor:"#00E5FF"
}]
},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false}},scales:{y:{beginAtZero:true},x:{ticks:{maxRotation:45,minRotation:45}}}
});
new Chart(document.getElementById("deptChart"),{
type:"bar",
data:{
labels:DATA.depts.map(d=>d.name),
datasets:[{
label:"Заявок",
data:DATA.depts.map(d=>d.v),
backgroundColor:"#3B82F6"
}]
},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false}},indexAxis:"y"}
});
renderTable(DATA.tickets);
renderTopList(DATA.executors,"executorsList");
renderTopList(DATA.requesters,"requestersList");
document.getElementById("searchInput").addEventListener("input",function(e){
const q=e.target.value.toLowerCase();
const filtered=DATA.tickets.filter(t=>
t.title.toLowerCase().includes(q)||t.initiator.toLowerCase().includes(q)||t.executor.toLowerCase().includes(q)
);
renderTable(filtered);
});
document.getElementById("statusFilter").addEventListener("change",function(e){
const s=e.target.value;
let filtered=DATA.tickets;
if(s!=="all"){
if(s==="closed") filtered=DATA.tickets.filter(t=>t.status==="Закрыто");
if(s==="assigned") filtered=DATA.tickets.filter(t=>t.status==="Назначена");
if(s==="planned") filtered=DATA.tickets.filter(t=>t.status==="Запланирована");
if(s==="waiting") filtered=DATA.tickets.filter(t=>t.status==="В ожидании");
}
renderTable(filtered);
});
</script>
</body>
</html>