dashbord-top-uslug-b2c-po-re-2/index.html

282 lines
21 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" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Топ услуг B2C по регионам</title>
<link rel="stylesheet" href="./design-system/kt-ai-fonts.css">
<link rel="stylesheet" href="./design-system/kt-ai-tokens.css">
<link rel="stylesheet" href="./design-system/kt-ai-components.css">
<link rel="stylesheet" href="./design-system/kt-ai-page.css">
<style>
.summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--kt-ai-space-4); margin-bottom: var(--kt-ai-stack-block); }
.summary-tile { background: var(--kt-ai-card-bg); border: 1px solid var(--kt-ai-card-border); border-radius: var(--kt-ai-radius-2xl); padding: var(--kt-ai-space-5); }
.summary-tile .label { font-size: var(--kt-ai-text-xs); color: var(--kt-ai-fg-muted); margin-bottom: var(--kt-ai-space-2); }
.summary-tile .value { font-size: var(--kt-ai-text-2xl); font-weight: var(--kt-ai-weight-semibold); color: var(--kt-ai-fg); }
.summary-tile .sub { font-size: var(--kt-ai-text-xs); color: var(--kt-ai-fg-muted); margin-top: var(--kt-ai-space-1); }
.chart-wrap { background: var(--kt-ai-bg-soft); border-radius: var(--kt-ai-radius-2xl); padding: var(--kt-ai-space-6); margin-bottom: var(--kt-ai-stack-block); }
.chart-title { font-size: var(--kt-ai-text-lg); font-weight: var(--kt-ai-weight-semibold); margin-bottom: var(--kt-ai-space-4); }
.chart-bars { display: flex; align-items: flex-end; gap: var(--kt-ai-space-2); height: 200px; padding: var(--kt-ai-space-4) 0; }
.chart-bar { flex: 1; background: var(--kt-ai-primary); border-radius: var(--kt-ai-radius-md) var(--kt-ai-radius-md) 0 0; min-width: 24px; transition: opacity 0.2s; cursor: pointer; }
.chart-bar:hover { opacity: 0.8; }
.chart-labels { display: flex; gap: var(--kt-ai-space-2); margin-top: var(--kt-ai-space-3); font-size: var(--kt-ai-text-xs); color: var(--kt-ai-fg-muted); }
.chart-labels span { flex: 1; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip-row { display: flex; gap: var(--kt-ai-space-2); flex-wrap: wrap; margin-bottom: var(--kt-ai-stack-block); }
.chip { padding: var(--kt-ai-space-2) var(--kt-ai-space-4); border-radius: var(--kt-ai-radius-full); background: var(--kt-ai-bg-soft); color: var(--kt-ai-fg); font-size: var(--kt-ai-text-sm); cursor: pointer; border: 1px solid transparent; transition: all 0.2s; }
.chip:hover { background: var(--kt-ai-bg-hover); }
.chip.active { background: var(--kt-ai-primary); color: white; }
.toolbar-row { display: flex; align-items: center; gap: var(--kt-ai-space-4); flex-wrap: wrap; padding: 0 0 var(--kt-ai-stack-block); }
.toolbar-row .spacer { flex: 1; }
.page-hero { display: flex; align-items: flex-start; gap: var(--kt-ai-space-6); margin-bottom: var(--kt-ai-stack-group); }
.page-hero .ph-text { flex: 1; min-width: 0; }
.page-hero h1 { margin: 0; font-size: var(--kt-ai-text-2xl); font-weight: var(--kt-ai-weight-semibold); letter-spacing: -.01em; line-height: 1.15; }
.page-hero .sub { margin-top: var(--kt-ai-space-3); font-size: var(--kt-ai-text-md); color: var(--kt-ai-fg-muted); }
.cell-title { font-weight: var(--kt-ai-weight-medium); }
.cell-sub { font-size: var(--kt-ai-text-2xs); color: var(--kt-ai-fg-muted); margin-top: 1px; }
.kt-ai-sidebar { width: 240px; background: var(--kt-ai-bg-soft); border-right: 1px solid var(--kt-ai-border); padding: var(--kt-ai-space-4); position: fixed; height: 100vh; overflow-y: auto; }
.kt-ai-main { margin-left: 240px; min-height: 100vh; background: var(--kt-ai-bg); }
.kt-ai-topbar { position: sticky; top: 0; z-index: 100; background: var(--kt-ai-bg-elevated); border-bottom: 1px solid var(--kt-ai-border); padding: var(--kt-ai-space-3) var(--kt-ai-space-5); display: flex; align-items: center; gap: var(--kt-ai-space-4); }
.kt-ai-nav-item { display: flex; align-items: center; gap: var(--kt-ai-space-3); padding: var(--kt-ai-space-3) var(--kt-ai-space-3); border-radius: var(--kt-ai-radius-lg); color: var(--kt-ai-fg); text-decoration: none; margin-bottom: 2px; }
.kt-ai-nav-item:hover { background: var(--kt-ai-bg-hover); }
.kt-ai-nav-item--active { background: var(--kt-ai-primary-subtle); color: var(--kt-ai-primary); }
.nav-icon { width: 20px; height: 20px; }
.nav-label { font-size: var(--kt-ai-text-sm); font-weight: var(--kt-ai-weight-medium); }
.sb-top { display: flex; align-items: center; gap: var(--kt-ai-space-3); padding: var(--kt-ai-space-3); margin-bottom: var(--kt-ai-space-4); }
.sb-top img { width: 28px; height: 28px; border-radius: var(--kt-ai-radius-xl); }
.sb-top .nm { font-weight: var(--kt-ai-weight-semibold); font-size: var(--kt-ai-text-sm); }
.sb-bottom { margin-top: auto; padding-top: var(--kt-ai-space-4); border-top: 1px solid var(--kt-ai-divider); }
.kt-icon { width: 18px; height: 18px; }
.shell-topbar-burger { display: none; background: none; border: none; cursor: pointer; padding: var(--kt-ai-space-2); }
@media (max-width: 831.98px) {
.shell-topbar-burger { display: block; }
.kt-ai-sidebar { transform: translateX(-100%); transition: transform 0.3s; z-index: 200; }
.kt-ai-sidebar.open { transform: translateX(0); }
.kt-ai-main { margin-left: 0; }
}
</style>
</head>
<body>
<aside class="kt-ai-sidebar" id="sidebar">
<div class="sb-top">
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='.9em' font-size='90'%3E📊%3C/text%3E%3C/svg%3E" alt="Logo">
<span class="nm">B2C Дашборд</span>
</div>
<nav>
<a class="kt-ai-nav-item kt-ai-nav-item--active"><svg class="nav-icon"><use href="#dashboard"/></svg><span class="nav-label">Дашборд</span></a>
<a class="kt-ai-nav-item"><svg class="nav-icon"><use href="#map"/></svg><span class="nav-label">Регионы</span></a>
<a class="kt-ai-nav-item"><svg class="nav-icon"><use href="#catalog"/></svg><span class="nav-label">Услуги</span></a>
<a class="kt-ai-nav-item"><svg class="nav-icon"><use href="#spreadsheet"/></svg><span class="nav-label">Отчёты</span></a>
</nav>
<div class="sb-bottom">
<a class="kt-ai-nav-item"><svg class="nav-icon"><use href="#help"/></svg><span class="nav-label">Помощь</span></a>
</div>
</aside>
<main class="kt-ai-main">
<header class="kt-ai-topbar">
<button class="shell-topbar-burger" onclick="toggleSidebar()"><svg class="kt-icon"><use href="#menu"/></svg></button>
<div class="topbar-title">
<span class="t" style="font-size:var(--kt-ai-text-lg);font-weight:var(--kt-ai-weight-semibold)">Топ услуг B2C по регионам</span>
<span class="sub" style="font-size:var(--kt-ai-text-xs);color:var(--kt-ai-fg-muted)">Июль 2026</span>
</div>
<div style="margin-left:auto;display:flex;gap:var(--kt-ai-space-2)">
<button class="kt-ai-btn" data-variant="ghost" data-size="sm" onclick="toggleTheme()"><svg class="kt-icon"><use href="#sun"/></svg></button>
<button class="kt-ai-btn" data-variant="ghost" data-size="sm" onclick="exportCsv()"><svg class="kt-icon"><use href="#download"/></svg></button>
</div>
</header>
<div id="view" style="padding:var(--kt-ai-stack-region)">
<div class="page-hero">
<div class="ph-text">
<h1>Топ услуг B2C по регионам</h1>
<p class="sub">Рейтинг услуг по количеству покупок и сумме в разбивке по регионам</p>
</div>
</div>
<div class="summary-grid" id="kpi-strip"></div>
<div class="chart-wrap">
<div class="chart-title">По регионам (сумма)</div>
<div class="chart-bars" id="region-chart"></div>
<div class="chart-labels" id="region-labels"></div>
</div>
<div class="toolbar-row">
<div class="chip-row" id="region-filters"></div>
</div>
<div class="kt-ai-card">
<table class="kt-ai-table">
<thead>
<tr>
<th>Регион</th>
<th>Услуга</th>
<th style="text-align:right">Кол-во</th>
<th style="text-align:right">Сумма (₸)</th>
<th>Статус</th>
</tr>
</thead>
<tbody id="table-body"></tbody>
</table>
</div>
</div>
</main>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" style="position:absolute;width:0;height:0;overflow:hidden">
<symbol id="dashboard" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></symbol>
<symbol id="map" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M3 7V21l9-4 9 4V3l-9 4z"/><path d="M12 3v18"/><path d="M21 7l-9-4-9 4"/></symbol>
<symbol id="catalog" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></symbol>
<symbol id="spreadsheet" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M8 13h2"/><path d="M14 13h2"/><path d="M8 17h2"/><path d="M14 17h2"/></symbol>
<symbol id="download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></symbol>
<symbol id="sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></symbol>
<symbol id="menu" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="20" y2="18"/></symbol>
<symbol id="help" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></symbol>
</svg>
<script>
const RAW_DATA = [
{"region":"Астана","service":"B2C Абонплата ШПД по FTTx ГТС (New)","qty":37372,"amount":238463786.09},
{"region":"г.Алматы","service":"B2C Абонплата ШПД по FTTx ГТС (New)","qty":39671,"amount":237005698.16},
{"region":"г.Алматы","service":"Абонплата ШПД по FTTx для B2C","qty":35588,"amount":152893561.28},
{"region":"Астана","service":"2025 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":35637,"amount":136187938.99},
{"region":"Астана","service":"B2C TV Plus Абонплата Full ГТС","qty":47661,"amount":132060372.93},
{"region":"Астана","service":"2021 B2C Абонплата за мобильный номер Kcell GSM закр.","qty":61700,"amount":123403164.43},
{"region":"г.Алматы","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":57378,"amount":110497209.55},
{"region":"г.Алматы","service":"2025 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":29997,"amount":106749675.59},
{"region":"Астана","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":52504,"amount":99235780.41},
{"region":"Астана","service":"2022 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":36946,"amount":96430127.27},
{"region":"Астана","service":"Абонплата ШПД по FTTx для B2C","qty":19565,"amount":88834659.73},
{"region":"Астана","service":"2024 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":22167,"amount":83549325.34},
{"region":"г.Алматы","service":"B2C TV Plus Абонплата Full ГТС","qty":31221,"amount":83511013.05},
{"region":"г.Алматы","service":"2021 B2C Абонплата за мобильный номер Kcell GSM закр.","qty":40838,"amount":82748215.46},
{"region":"г.Семей","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":41486,"amount":76400272.02},
{"region":"Астана","service":"TV Plus Абонплата за Премиальный пакет ГТС","qty":29210,"amount":71278165.09},
{"region":"г.Алматы","service":"2022 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":24001,"amount":62521274.30},
{"region":"Астана","service":"2021 B2C Абонплата за мобильный номер Kcell GSM откр.","qty":27864,"amount":59635624.39},
{"region":"г.Алматы","service":"2024 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":15678,"amount":58970815.48},
{"region":"г.Актобе","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":30641,"amount":55819936.98},
{"region":"г.Алматы","service":"B2C Абонплата за основной телефон ГТС","qty":52732,"amount":49636818.50},
{"region":"г.Актобе","service":"2021 B2C Абонплата за мобильный номер Kcell GSM закр.","qty":25178,"amount":48000483.43},
{"region":"г.Алматы","service":"TV Plus Абонплата за Премиальный пакет ГТС","qty":19055,"amount":45298192.89},
{"region":"г.Павлодар","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":23334,"amount":44857518.84},
{"region":"г.Алматы","service":"B2C TV Plus Аренда STB оператора новое","qty":96183,"amount":43252519.71},
{"region":"г.Караганда","service":"Абонплата ШПД по FTTx для B2C","qty":8916,"amount":41624727.66},
{"region":"Астана","service":"2026 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":13685,"amount":41618770.34},
{"region":"г.Алматы","service":"2018 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":11568,"amount":40199964.35},
{"region":"г.Тараз","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":21880,"amount":39564813.34},
{"region":"г.Алматы","service":"Абонплата ШПД по ADSL для B2C","qty":10710,"amount":36238454.72},
{"region":"г.Алматы","service":"2020 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":12148,"amount":36010481.68},
{"region":"г.Усть-Каменогорск","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":18362,"amount":35883527.39},
{"region":"г. Шымкент","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":19180,"amount":35338634.51},
{"region":"г. Шымкент","service":"2021 B2C Абонплата за мобильный номер Kcell GSM закр.","qty":18945,"amount":35138664.91},
{"region":"Астана","service":"B2C TV Plus Аренда STB оператора новое","qty":76655,"amount":34746783.92},
{"region":"Астана","service":"2018 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":8705,"amount":32905725.03},
{"region":"г.Актобе","service":"B2C Абонплата ШПД по FTTx ГТС (New)","qty":6742,"amount":32313021.91},
{"region":"г.Усть-Каменогорск","service":"Абонплата ШПД по FTTx для B2C","qty":7269,"amount":31834355.15},
{"region":"г.Атырау","service":"B2C Абонплата ШПД по FTTx ГТС (New)","qty":5495,"amount":31688118.61},
{"region":"г.Усть-Каменогорск","service":"B2C Абонплата ШПД по FTTx ГТС (New)","qty":5600,"amount":31520413.53},
{"region":"Астана","service":"B2C Аренда оборудования ONT оператора - новое","qty":87071,"amount":31220264.09},
{"region":"г.Павлодар","service":"B2C Абонплата ШПД по FTTx ГТС (New)","qty":6104,"amount":30831455.08},
{"region":"г.Кокшетау","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":16818,"amount":30654924.73},
{"region":"г.Актобе","service":"2025 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":8613,"amount":30562705.78},
{"region":"г.Костанай","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":16473,"amount":30539180.35},
{"region":"Енбекшиказахский район","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":17347,"amount":30429552.38},
{"region":"г.Актобе","service":"2022 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":11709,"amount":30303557.00},
{"region":"г.Кызылорда","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":16539,"amount":29772241.67},
{"region":"г.Алматы","service":"2026 B2C Абонплата за Домашний Интернет FTTx ГТС","qty":10206,"amount":29200316.21},
{"region":"г.Караганда","service":"2022 B2C Абонплата за мобильный номер MT-C GSM закр.","qty":15714,"amount":28837871.98}
];
function fmtNum(n) { return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " "); }
function fmtMoney(n) { return fmtNum(n.toFixed(2).replace(".", ",")); }
function getStatus(i) {
if (i < 10) return { label: "Топ-10", tone: "ok" };
if (i < 20) return { label: "Высокий спрос", tone: "info" };
if (i < 35) return { label: "Средний", tone: "info" };
return { label: "Растущий", tone: "warn" };
}
const rows = RAW_DATA.map((r, i) => ({
region: r.region,
service: r.service,
qty: fmtNum(r.qty),
amount: fmtMoney(r.amount),
status: getStatus(i),
_raw: r
}));
localStorage.setItem("b2c_dashboard_data", JSON.stringify(rows));
// KPIs
const totalQty = RAW_DATA.reduce((s, r) => s + r.qty, 0);
const regions = new Set(RAW_DATA.map(r => r.region));
const regionData = {};
RAW_DATA.forEach(r => { regionData[r.region] = (regionData[r.region] || 0) + r.amount; });
const topRegion = Object.entries(regionData).sort((a, b) => b[1] - a[1])[0];
document.getElementById("kpi-strip").innerHTML = [
{ label: "Всего покупок", value: fmtNum(totalQty), sub: "Июль 2026" },
{ label: "Всего регионов", value: regions.size, sub: "в топ-50" },
{ label: "Услуга-лидер", value: "B2C Абонплата ШПД", sub: "37 372 покупок" },
{ label: "Регион-лидер", value: topRegion[0], sub: fmtMoney(topRegion[1] / 1e6) + " млн ₸" }
].map(k => `<div class="summary-tile"><div class="label">${k.label}</div><div class="value">${k.value}</div><div class="sub">${k.sub}</div></div>`).join("");
// Chart
const sortedRegions = Object.entries(regionData).sort((a, b) => b[1] - a[1]).slice(0, 8);
const maxVal = Math.max(...sortedRegions.map(([,v]) => v));
document.getElementById("region-chart").innerHTML = sortedRegions.map(([r, v]) => {
const h = (v / maxVal) * 160;
return `<div class="chart-bar" style="height:${h}px" title="${r}: ${fmtMoney(v)}"></div>`;
}).join("");
document.getElementById("region-labels").innerHTML = sortedRegions.map(([r]) => `<span>${r.replace("г.","").replace(" ","")}</span>`).join("");
// Filters
const allRegions = ["Все", ...regions];
document.getElementById("region-filters").innerHTML = allRegions.map(r => `<button class="chip${r==="Все"?" active":""}" data-region="${r}">${r}</button>`).join("");
document.querySelectorAll(".chip").forEach(chip => {
chip.addEventListener("click", () => {
document.querySelectorAll(".chip").forEach(c => c.classList.remove("active"));
chip.classList.add("active");
renderTable(chip.dataset.region);
});
});
function renderTable(filter) {
const filtered = filter === "Все" ? rows : rows.filter(r => r.region === filter);
document.getElementById("table-body").innerHTML = filtered.map((r, i) => `
<tr>
<td>${r.region}</td>
<td><div class="cell-title">${r.service}</div></td>
<td style="text-align:right">${r.qty}</td>
<td style="text-align:right">${r.amount}</td>
<td><span class="kt-ai-chip" data-tone="${r.status.tone}">${r.status.label}</span></td>
</tr>
`).join("");
}
renderTable("Все");
window.exportCsv = function() {
const csv = "\uFEFF" + ["Регион", "Услуга", "Кол-во", "Сумма"].join(";") + "\n" +
rows.map(r => [r.region, r.service, r._raw.qty, r._raw.amount].join(";")).join("\n");
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "top_uslug_b2c.csv";
a.click();
};
window.toggleTheme = function() {
const html = document.documentElement;
html.setAttribute("data-theme", html.getAttribute("data-theme") === "light" ? "dark" : "light");
};
window.toggleSidebar = function() {
document.getElementById("sidebar").classList.toggle("open");
};
</script>
</body>
</html>