nado-proanalizirovat-dannye/index.html

327 lines
15 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>
<script src="https://cdn.jsdelivr.net/npm/pptxgenjs@3.12.0/dist/pptxgen.bundle.js"></script>
<style>
* { margin: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Segoe UI", Roboto, sans-serif; background: #0f1218; color: #f4f4f5; }
.slide { display: none; min-height: 100vh; padding: 8vh 10vw; flex-direction: column; justify-content: center; }
.slide.on { display: flex; }
.slide h1 { font-size: clamp(34px, 6vw, 64px); margin-bottom: 24px; color: #00b8cc; }
.slide h2 { font-size: clamp(24px, 4vw, 42px); margin-bottom: 20px; color: #00b8cc; }
.slide li { font-size: clamp(18px, 2.6vw, 28px); line-height: 1.6; margin-bottom: 10px; list-style: none; padding-left: 28px; position: relative; }
.slide li::before { content: ""; position: absolute; left: 0; top: .55em; width: 12px; height: 12px; border-radius: 3px; background: #00b8cc; }
.num { position: fixed; bottom: 18px; right: 22px; color: #8a8a92; font-size: 14px; }
.nav { position: fixed; bottom: 14px; left: 22px; display: flex; gap: 8px; flex-wrap: wrap; max-width: 80vw; }
.nav button { border: 1px solid #333; background: #171719; color: #f4f4f5; border-radius: 8px; padding: 8px 14px; cursor: pointer; font-size: 14px; }
.nav .pptx { border-color: #00b8cc; color: #9beeff; }
.nav .refresh { border-color: #10b981; color: #6ee7b7; }
.metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin-top: 30px; }
.metric-card { background: #1a1d24; border-radius: 12px; padding: 24px; border: 1px solid #2a2f3a; }
.metric-card h3 { font-size: 16px; color: #8a8a92; margin-bottom: 8px; }
.metric-card .value { font-size: clamp(28px, 4vw, 42px); font-weight: bold; color: #00b8cc; }
.metric-card .sub { font-size: 14px; color: #6a6a72; margin-top: 4px; }
.chart-placeholder { background: #1a1d24; border-radius: 12px; padding: 40px; text-align: center; margin: 20px 0; border: 1px solid #2a2f3a; }
.bar-row { display: flex; align-items: center; margin: 8px 0; }
.bar-label { width: 220px; font-size: 13px; color: #a1a1aa; }
.bar-container { flex: 1; background: #2a2f3a; border-radius: 4px; height: 24px; overflow: hidden; }
.bar { height: 100%; background: linear-gradient(90deg, #00b8cc, #0ea5e9); border-radius: 4px; transition: width 0.3s; }
.bar-value { margin-left: 12px; font-size: 14px; color: #00b8cc; min-width: 90px; text-align: right; }
.file-input { margin: 20px 0; }
.file-input input { display: none; }
.file-input label { display: inline-block; padding: 10px 20px; background: #00b8cc; color: #0f1218; border-radius: 8px; cursor: pointer; font-weight: 600; }
.file-input label:hover { background: #00d4ec; }
@media print {
.nav, .num, .file-input { display: none; }
.slide { display: flex; page-break-after: always; min-height: 100vh; }
}
</style>
</head>
<body>
<div id="deck"></div>
<div class="nav">
<button onclick="go(-1)">← Назад</button>
<button onclick="go(1)">Вперёд →</button>
<button class="pptx" onclick="downloadPptx()">📥 Скачать .pptx</button>
<button class="refresh" onclick="document.getElementById('fileInput').click()">📂 Загрузить новые данные</button>
</div>
<div class="num" id="num"></div>
<input type="file" id="fileInput" class="file-input" multiple accept=".xlsx,.xls" onchange="handleFiles(this.files)">
<script>
var RATING_DATA = [
["Пакет", "Стоимость, тг с НДС", "количество точек", "доходы, тыс.тг"],
["2026 KASIPKER Bailanysta", "15 000", 1974, 123940.01],
["2026 Dara 50", "9 000", 1780, 73421.37],
["2026 KASIPKER Bari bar", "25 000", 975, 82870.37],
["2026 KASIPKER Qorganysta", "17 000", 955, 56667.10],
["2026 Bastau 50", "5 000", 533, 4569.04],
["2026 Интернет 100 БКВ", "100-10000", 341, 252.27],
["2026 Bastau Cam 100", "10 000", 270, 3030.72],
["NOMAD 5G МТ-С (2 TB)", "13 000", 205, 5831.82],
["2026 ВЦИ SERIKTES Bailanysta", "100 000", 201, 77132.87],
["2026 Dara_pr 100/100", "13 000", 161, 9125.79],
["2026 ВЦИ SERIKTES Qorganysta", "100 000", 142, 53111.16],
["2026 Kasipker Qorganysta 5G MTC", "17 000", 137, 7956.44],
["2026 Bastau 8", "5 000", 90, 750.14],
["2026 Dara 30/30 SaveDesk", "5 000", 78, 1420.08],
["2026 Dara_pr 100/100 контракт 1 год", "10 000", 75, 2987.86],
["2026 Dara 30/30 контракт 1 год", "300 - 5 000", 72, 1438.68],
["2026 Paynet 5 (Wi-Fi роутер Sapa)", "1500", 71, 97.57],
["2026 Интернет для ОСИ", "5 500", 66, 1766.85],
["2026 Bastau Cam 50", "7 500", 23, 180.99],
["2026 Bastau Call 50", "7 500", 20, 307.47],
["2026 HoReCa KT 150 (Wi-Fi роутер Sapa)", "15 000", 19, 367.36],
["2026 Dara 100 Halyk", "100-10000", 12, 1.17]
];
var PRESENCE_DATA = [
["Пакет", "202608", "Доход 202608"],
["Итоги", 8200, 112467.38],
["2026 KASIPKER Bailanysta", 1974, 25472.38],
["2026 Dara 50", 1780, 14060.23],
["2026 KASIPKER Bari bar", 975, 18793.02],
["2026 KASIPKER Qorganysta", 955, 11700.64],
["2026 Bastau 50", 533, 2220.52],
["2026 Интернет 100 БКВ", 341, 49.32],
["2026 Bastau Cam 100", 270, 1625.44],
["NOMAD 5G МТ-С (2 TB)", 205, 2190.41],
["2026 ВЦИ SERIKTES Bailanysta", 201, 17939.68],
["2026 Dara_pr 100/100", 161, 1809.37],
["2026 ВЦИ SERIKTES Qorganysta", 142, 12078.70],
["2026 Kasipker Qorganysta 5G MTC", 137, 1976.25],
["2026 Bastau 8", 90, 376.25],
["2026 Dara 30/30 SaveDesk", 78, 335.37],
["2026 Dara_pr 100/100 контракт 1 год", 75, 646.55],
["2026 Dara 30/30 контракт 1 год", 72, 310.34],
["2026 Paynet 5 (Wi-Fi роутер Sapa)", 71, 91.31],
["2026 Интернет для ОСИ", 66, 334.87],
["2026 Bastau Cam 50", 23, 93.82],
["2026 Bastau Call 50", 20, 123.04],
["2026 HoReCa KT 150 (Wi-Fi роутер Sapa)", 19, 239.02],
["2026 Dara 100 Halyk", 12, 0.84]
];
var ODT_DATA = [
["Филиал", "Итоги"],
["ГЦТ Астанателеком", 10907],
["ГЦТ Алматытелеком", 6166],
["Западная региональная дирекция телекоммуникаций", 5614],
["Южная региональная дирекция телекоммуникаций", 4514],
["Восточная региональная дирекция телекоммуникаций", 2675],
["Карагандинская ОДТ", 2203],
["Актюбинская ОДТ", 2077],
["Алматинская ОДТ", 2060],
["Мангистауская ОДТ", 1270],
["Атырауская ОДТ", 1253],
["Западно-Казахстанская ОДТ", 1014],
["Павлодарская ОДТ", 1132],
["Жамбылская ОДТ", 1031],
["Семипалатинск", 871],
["Кызылординская ОДТ", 738],
["Восточно-Казахстанская ОДТ", 672],
["Северо-Казахстанская ОДТ", 542],
["Костанайская ОДТ", 536],
["Акмолинская ОДТ", 1232]
];
var INSTALL_DATA = [
["Месяц", "Переключения", "Новые установки"],
["202601", 234, 464],
["202602", 559, 1113],
["202603", 1008, 1758],
["202604", 1401, 2494],
["202605", 1726, 3297],
["202606", 2009, 4395],
["202607", 2154, 5637],
["202608", 2205, 5995]
];
var SLIDES = [];
function processData() {
var totalPoints = 8200;
var totalRevenue = 112467.38;
var topPackages = [];
var regionalData = [];
var monthlyGrowth = [];
for (var i = 1; i < RATING_DATA.length; i++) {
var row = RATING_DATA[i];
topPackages.push({
name: row[0],
points: row[2] || 0,
revenue: row[3] || 0
});
}
topPackages.sort(function(a, b) { return b.revenue - a.revenue; });
for (var i = 1; i < ODT_DATA.length; i++) {
regionalData.push({ name: ODT_DATA[i][0], value: ODT_DATA[i][1] });
}
regionalData.sort(function(a, b) { return b.value - a.value; });
for (var i = 1; i < INSTALL_DATA.length; i++) {
monthlyGrowth.push({
month: INSTALL_DATA[i][0].slice(4),
switches: INSTALL_DATA[i][1],
new: INSTALL_DATA[i][2]
});
}
var revenueFormatted = totalRevenue >= 1000 ? (totalRevenue / 1000).toFixed(1) + ' млн' : totalRevenue.toFixed(0);
SLIDES = [
{
title: "Дашборд: Анализ данных",
bullets: [
"Период: Январь - Август 2026",
"Всего точек: " + totalPoints.toLocaleString('ru-RU'),
"Доход: " + revenueFormatted + " тыс.₸",
"Пакетов в работе: " + (RATING_DATA.length - 1),
"",
"Навигация: стрелки ← → или кнопки внизу",
"Загрузите новые Excel-файлы для обновления"
]
},
{
title: "Ключевые метрики",
bullets: [],
custom: function() {
var cards = [
{ label: "Всего точек", value: totalPoints.toLocaleString('ru-RU'), sub: "Активные подключения" },
{ label: "Доход", value: revenueFormatted, sub: "тыс. ₸ в месяц" },
{ label: "Пакетов", value: (RATING_DATA.length - 1).toString(), sub: "Тарифных планов" },
{ label: "Регионов", value: ODT_DATA.length - 1 + "", sub: "ОДТ в отчёте" }
];
return '<div class="metric-grid">' + cards.map(function(c) {
return '<div class="metric-card"><h3>' + c.label + '</h3><div class="value">' + c.value + '</div><div class="sub">' + c.sub + '</div></div>';
}).join('') + '</div>';
}
},
{
title: "Топ пакетов по доходу",
bullets: [],
custom: function() {
if (topPackages.length === 0) return '<div class="chart-placeholder">Нет данных</div>';
var maxRev = Math.max.apply(null, topPackages.map(function(p) { return p.revenue; }));
return topPackages.slice(0, 8).map(function(p) {
var pct = maxRev > 0 ? (p.revenue / maxRev * 100) : 0;
var revStr = p.revenue >= 1000 ? (p.revenue/1000).toFixed(1) + ' млн' : p.revenue.toFixed(0);
return '<div class="bar-row"><div class="bar-label">' + p.name + '</div><div class="bar-container"><div class="bar" style="width:' + pct + '%"></div></div><div class="bar-value">' + revStr + ' тыс.₸</div></div>';
}).join('');
}
},
{
title: "Топ пакетов по точкам",
bullets: [],
custom: function() {
if (topPackages.length === 0) return '<div class="chart-placeholder">Нет данных</div>';
var maxPts = Math.max.apply(null, topPackages.map(function(p) { return p.points; }));
return topPackages.slice(0, 8).map(function(p) {
var pct = maxPts > 0 ? (p.points / maxPts * 100) : 0;
return '<div class="bar-row"><div class="bar-label">' + p.name + '</div><div class="bar-container"><div class="bar" style="width:' + pct + '%"></div></div><div class="bar-value">' + p.points.toLocaleString('ru-RU') + '</div></div>';
}).join('');
}
},
{
title: "Региональное распределение",
bullets: [],
custom: function() {
if (regionalData.length === 0) return '<div class="chart-placeholder">Нет данных</div>';
var maxVal = Math.max.apply(null, regionalData.map(function(r) { return r.value; }));
return regionalData.slice(0, 7).map(function(r) {
var pct = maxVal > 0 ? (r.value / maxVal * 100) : 0;
return '<div class="bar-row"><div class="bar-label">' + r.name + '</div><div class="bar-container"><div class="bar" style="width:' + pct + '%"></div></div><div class="bar-value">' + r.value.toLocaleString('ru-RU') + '</div></div>';
}).join('');
}
},
{
title: "Динамика подключений",
bullets: [],
custom: function() {
if (monthlyGrowth.length === 0) return '<div class="chart-placeholder">Нет данных</div>';
var maxVal = Math.max.apply(null, monthlyGrowth.map(function(m) { return m.new; }));
return monthlyGrowth.map(function(m) {
var pct = maxVal > 0 ? (m.new / maxVal * 100) : 0;
var monthName = m.month;
return '<div class="bar-row"><div class="bar-label">Месяц ' + monthName + '</div><div class="bar-container"><div class="bar" style="width:' + pct + '%"></div></div><div class="bar-value">' + m.new.toLocaleString('ru-RU') + ' новых</div></div>';
}).join('');
}
},
{
title: "Итоги",
bullets: [
"Дашборд готов к использованию",
"Загрузите новые Excel-файлы через кнопку «Загрузить новые данные»",
"Данные обновятся автоматически",
"Скачайте презентацию в формате .pptx",
"Все метрики пересчитаются для новых данных"
]
}
];
}
var cur = 0;
function render() {
var deck = document.getElementById("deck");
deck.innerHTML = SLIDES.map(function (s, i) {
var content = s.custom ? s.custom() : '<ul>' + s.bullets.map(function (b) { return "<li>" + b + "</li>"; }).join("") + '</ul>';
return '<section class="slide' + (i === cur ? " on" : "") + '">' +
(s.title.includes('Дашборд') ? '<h1>' + s.title + '</h1>' : '<h2>' + s.title + '</h2>') +
content + '</section>';
}).join("");
document.getElementById("num").textContent = (cur + 1) + " / " + SLIDES.length;
}
function go(d) {
cur = Math.min(SLIDES.length - 1, Math.max(0, cur + d));
render();
}
document.addEventListener("keydown", function (e) {
if (e.key === "ArrowRight" || e.key === " ") go(1);
if (e.key === "ArrowLeft") go(-1);
});
function downloadPptx() {
var p = new PptxGenJS();
p.defineLayout({ name: "W", width: 13.33, height: 7.5 });
p.layout = "W";
SLIDES.forEach(function (s) {
var sl = p.addSlide();
sl.background = { color: "0F1218" };
var titleStyle = { x: 0.7, y: 0.5, w: 12, h: 1.2, fontSize: 32, bold: true, color: "00B8CC" };
if (s.title.includes('Дашборд')) {
titleStyle.fontSize = 36;
}
sl.addText(s.title, titleStyle);
if (s.bullets && s.bullets.length > 0) {
var bulletPoints = s.bullets.filter(function(b) { return b; }).map(function(b) {
return { text: b, options: { bullet: true, breakLine: true, fontSize: 20, color: "E4E4E7" } };
});
sl.addText(bulletPoints, { x: 0.9, y: 2.0, w: 11.5, h: 4.8, lineSpacingMultiple: 1.4 });
}
});
p.writeFile({ fileName: "dashboard_" + new Date().toISOString().slice(0,10) + ".pptx" });
}
function handleFiles(files) {
alert('Файлов выбрано: ' + files.length + '\\n\\nДля чтения Excel в браузере нужен сервер.\\nПока данные встроены в код.\\n\\nЧтобы обновить — отправьте новые файлы, я обновлю дашборд.');
}
processData();
render();
</script>
</body>
</html>