nado-proanalizirovat-dannye/index.html

201 lines
11 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: 6vh 8vw; flex-direction: column; justify-content: flex-start; }
.slide.on { display: flex; }
.slide h1 { font-size: clamp(28px, 5vw, 52px); margin-bottom: 20px; color: #00b8cc; }
.slide h2 { font-size: clamp(22px, 4vw, 38px); margin-bottom: 16px; color: #00b8cc; }
.slide li { font-size: clamp(16px, 2.4vw, 24px); line-height: 1.5; margin-bottom: 8px; list-style: none; padding-left: 24px; position: relative; }
.slide li::before { content: ""; position: absolute; left: 0; top: .55em; width: 10px; height: 10px; 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(240px, 1fr)); gap: 16px; margin-top: 20px; }
.metric-card { background: #1a1d24; border-radius: 10px; padding: 20px; border: 1px solid #2a2f3a; }
.metric-card h3 { font-size: 14px; color: #8a8a92; margin-bottom: 6px; }
.metric-card .value { font-size: clamp(24px, 3.5vw, 36px); font-weight: bold; color: #00b8cc; }
.metric-card .sub { font-size: 13px; color: #6a6a72; margin-top: 4px; }
.chart-placeholder { background: #1a1d24; border-radius: 12px; padding: 30px; text-align: center; margin: 16px 0; border: 1px solid #2a2f3a; }
.bar-row { display: flex; align-items: center; margin: 10px 0; }
.bar-label { width: 180px; font-size: 12px; color: #a1a1aa; }
.bar-container { flex: 1; background: #2a2f3a; border-radius: 4px; height: 22px; overflow: hidden; }
.bar { height: 100%; background: linear-gradient(90deg, #00b8cc, #0ea5e9); border-radius: 4px; transition: width 0.3s; }
.bar-value { margin-left: 10px; font-size: 13px; color: #00b8cc; min-width: 70px; text-align: right; font-weight: 600; }
.file-input { margin: 16px 0; }
.file-input input { display: none; }
.file-input label { display: inline-block; padding: 8px 16px; background: #00b8cc; color: #0f1218; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 14px; }
.file-input label:hover { background: #00d4ec; }
.rdt-section { margin: 20px 0; }
.rdt-title { font-size: 18px; color: #00b8cc; margin: 16px 0 10px 0; font-weight: 600; }
.tariff-row { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #2a2f3a; font-size: 13px; }
.tariff-row:last-child { border-bottom: none; }
.tariff-name { color: #a1a1aa; }
.tariff-val { color: #00b8cc; font-weight: 600; }
.summary-box { background: #1a1d24; border: 1px solid #00b8cc; border-radius: 10px; padding: 20px; margin: 16px 0; }
.summary-box h3 { color: #00b8cc; margin-bottom: 12px; font-size: 18px; }
@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 RDT_DATA = [
{ name: "Восточная РДТ", paynet: 0, bastau8: 8, bastau50: 61, bastauCamCall: 2, bastauCam100: 5, horeca: 1 },
{ name: "Западная РДТ", paynet: 1, bastau8: 7, bastau50: 121, bastauCamCall: 6, bastauCam100: 60, horeca: 6 },
{ name: "РДТ Алматытелеком", paynet: 51, bastau8: 10, bastau50: 58, bastauCamCall: 10, bastauCam100: 42, horeca: 10 },
{ name: "Северная РДТ", paynet: 0, bastau8: 15, bastau50: 38, bastauCamCall: 1, bastauCam100: 10, horeca: 0 },
{ name: "Центральная РДТ", paynet: 4, bastau8: 8, bastau50: 204, bastauCamCall: 9, bastauCam100: 132, horeca: 0 },
{ name: "Южная РДТ", paynet: 15, bastau8: 42, bastau50: 51, bastauCamCall: 4, bastauCam100: 21, horeca: 2 }
];
var TOTALS = {
paynet: 71,
bastau5k: 623,
bastau7_5k: 43,
bastau10k: 270,
horeca: 19
};
var SLIDES = [];
function buildSlides() {
var totalMsb = TOTALS.paynet + TOTALS.bastau5k + TOTALS.bastau7_5k + TOTALS.bastau10k + TOTALS.horeca;
SLIDES = [
{
title: "1. ОСНОВНЫЕ ФОКУСЫ ДЛЯ АГРЕССИВНОГО ЗАХВАТА РЫНКА МСБ",
custom: function() {
var maxVal = Math.max(TOTALS.paynet, TOTALS.bastau5k, TOTALS.bastau7_5k, TOTALS.bastau10k, TOTALS.horeca);
var tariffs = [
{ name: "BASTAU 8/50 (5 000 ₸)", val: TOTALS.bastau5k, color: "#0ea5e9" },
{ name: "BASTAU CAM 100 (10 000 ₸)", val: TOTALS.bastau10k, color: "#f59e0b" },
{ name: "Paynet 5 (1 500 ₸)", val: TOTALS.paynet, color: "#00b8cc" },
{ name: "BASTAU CAM/CALL 50 (7 500 ₸)", val: TOTALS.bastau7_5k, color: "#10b981" },
{ name: "HoReCa KT (15 000 ₸)", val: TOTALS.horeca, color: "#ef4444" }
];
var bars = tariffs.map(function(t) {
var pct = maxVal > 0 ? (t.val / maxVal * 100) : 0;
return '<div class="bar-row">' +
'<div class="bar-label">' + t.name + '</div>' +
'<div class="bar-container"><div class="bar" style="width:' + pct + '%; background:' + t.color + '"></div></div>' +
'<div class="bar-value">' + t.val + ' точек</div></div>';
}).join('');
return '<div class="summary-box"><h3>📊 Итого по МСБ тарифам: ' + totalMsb.toLocaleString('ru-RU') + ' точек</h3>' + bars + '</div>' +
'<div class="metric-grid">' +
'<div class="metric-card"><h3>Paynet 5</h3><div class="value">' + TOTALS.paynet + '</div><div class="sub">1 500 ₸</div></div>' +
'<div class="metric-card"><h3>BASTAU 8/50</h3><div class="value">' + TOTALS.bastau5k + '</div><div class="sub">5 000 ₸ (ADSL + ОПТИКА)</div></div>' +
'<div class="metric-card"><h3>BASTAU CAM/CALL 50</h3><div class="value">' + TOTALS.bastau7_5k + '</div><div class="sub">7 500 ₸ (Cam + Call)</div></div>' +
'<div class="metric-card"><h3>BASTAU CAM 100</h3><div class="value">' + TOTALS.bastau10k + '</div><div class="sub">10 000 ₸</div></div>' +
'<div class="metric-card"><h3>HoReCa KT</h3><div class="value">' + TOTALS.horeca + '</div><div class="sub">15 000 ₸</div></div>' +
'</div>';
}
},
{
title: "2. Тарифы МСБ в разрезе РДТ (Август 2026)",
custom: function() {
var tariffs = [
{ name: "Paynet 5 (1 500 ₸)", data: RDT_DATA.map(function(r) { return {rdt: r.name, val: r.paynet}; }), total: TOTALS.paynet, color: "#00b8cc" },
{ name: "BASTAU 8/50 (5 000 ₸)", data: RDT_DATA.map(function(r) { return {rdt: r.name, val: r.bastau8 + r.bastau50}; }), total: TOTALS.bastau5k, color: "#0ea5e9" },
{ name: "BASTAU CAM/CALL 50 (7 500 ₸)", data: RDT_DATA.map(function(r) { return {rdt: r.name, val: r.bastauCamCall}; }), total: TOTALS.bastau7_5k, color: "#10b981" },
{ name: "BASTAU CAM 100 (10 000 ₸)", data: RDT_DATA.map(function(r) { return {rdt: r.name, val: r.bastauCam100}; }), total: TOTALS.bastau10k, color: "#f59e0b" },
{ name: "HoReCa KT (15 000 ₸)", data: RDT_DATA.map(function(r) { return {rdt: r.name, val: r.horeca}; }), total: TOTALS.horeca, color: "#ef4444" }
];
return tariffs.map(function(t) {
var maxVal = Math.max.apply(null, t.data.map(function(d) { return d.val; })) || 1;
var sortedData = t.data.sort(function(a,b) { return b.val - a.val; });
var bars = sortedData.map(function(d) {
var pct = maxVal > 0 ? (d.val / maxVal * 100) : 0;
return '<div class="bar-row">' +
'<div class="bar-label">' + d.rdt + '</div>' +
'<div class="bar-container"><div class="bar" style="width:' + pct + '%; background:' + t.color + '"></div></div>' +
'<div class="bar-value">' + d.val + '</div></div>';
}).join('');
return '<div class="rdt-section">' +
'<div class="rdt-title" style="color:' + t.color + '">' + t.name + ' — ' + t.total + ' точек всего</div>' +
bars +
'</div>';
}).join('');
}
}
];
}
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" : "") + '">' +
'<h1>' + s.title + '</h1>' + 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" };
sl.addText(s.title, { x: 0.5, y: 0.4, w: 12.3, h: 1, fontSize: 28, bold: true, color: "00B8CC" });
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: 18, color: "E4E4E7" } };
});
sl.addText(bulletPoints, { x: 0.7, y: 1.6, w: 12, h: 5, lineSpacingMultiple: 1.4 });
}
});
p.writeFile({ fileName: "MSB_dashboard_" + new Date().toISOString().slice(0,10) + ".pptx" });
}
function handleFiles(files) {
alert('Файлов выбрано: ' + files.length + '\\n\\nДанные встроены в код.\\nДля обновления — отправьте новые Excel-файлы, я обновлю дашборд.');
}
buildSlides();
render();
</script>
</body>
</html>