281 lines
19 KiB
HTML
281 lines
19 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru" data-theme="auto">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<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>
|
||
.dashboard-wrap { padding: var(--kt-ai-space-6) 0; }
|
||
.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--kt-ai-space-4); margin-bottom: var(--kt-ai-space-8); }
|
||
.kpi-card { background: var(--kt-ai-card-bg); border: 1px solid var(--kt-ai-card-border); border-radius: var(--kt-ai-radius-xl); padding: var(--kt-ai-space-5); }
|
||
.kpi-label { font-size: var(--kt-ai-text-sm); color: var(--kt-ai-fg-muted); margin-bottom: var(--kt-ai-space-2); }
|
||
.kpi-value { font-size: var(--kt-ai-text-2xl); font-weight: var(--kt-ai-weight-bold); color: var(--kt-ai-fg); }
|
||
.kpi-sub { font-size: var(--kt-ai-text-xs); color: var(--kt-ai-fg-faint); margin-top: var(--kt-ai-space-1); }
|
||
.charts-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--kt-ai-space-6); margin-bottom: var(--kt-ai-space-8); }
|
||
@media (max-width: 900px) { .charts-grid { grid-template-columns: 1fr; } }
|
||
.chart-card { background: var(--kt-ai-card-bg); border: 1px solid var(--kt-ai-card-border); border-radius: var(--kt-ai-radius-xl); padding: var(--kt-ai-space-5); }
|
||
.chart-title { font-size: var(--kt-ai-text-lg); font-weight: var(--kt-ai-weight-semibold); margin-bottom: var(--kt-ai-space-4); color: var(--kt-ai-fg); }
|
||
.bar-chart { display: flex; flex-direction: column; gap: var(--kt-ai-space-2); }
|
||
.bar-row { display: flex; align-items: center; gap: var(--kt-ai-space-3); }
|
||
.bar-label { width: 140px; font-size: var(--kt-ai-text-xs); color: var(--kt-ai-fg-muted); text-align: right; flex-shrink: 0; }
|
||
.bar-track { flex: 1; height: 24px; background: var(--kt-ai-bg-soft); border-radius: var(--kt-ai-radius-full); overflow: hidden; }
|
||
.bar-fill { height: 100%; background: var(--kt-ai-primary); border-radius: var(--kt-ai-radius-full); transition: width 0.5s ease; }
|
||
.bar-value { width: 80px; font-size: var(--kt-ai-text-xs); font-weight: var(--kt-ai-weight-semibold); color: var(--kt-ai-fg); text-align: right; flex-shrink: 0; }
|
||
.table-container { overflow-x: auto; border: 1px solid var(--kt-ai-card-border); border-radius: var(--kt-ai-radius-xl); margin-bottom: var(--kt-ai-space-6); }
|
||
table { width: 100%; border-collapse: collapse; background: var(--kt-ai-card-bg); }
|
||
th { text-align: left; padding: var(--kt-ai-space-3) var(--kt-ai-space-4); font-size: var(--kt-ai-text-sm); color: var(--kt-ai-fg-muted); border-bottom: 1px solid var(--kt-ai-divider); white-space: nowrap; }
|
||
td { padding: var(--kt-ai-space-4); border-bottom: 1px solid var(--kt-ai-divider); font-size: var(--kt-ai-text-sm); }
|
||
tr:last-child td { border-bottom: none; }
|
||
tr:hover td { background: var(--kt-ai-bg-soft); }
|
||
.rank { width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--kt-ai-radius-full); font-weight: var(--kt-ai-weight-bold); font-size: var(--kt-ai-text-xs); }
|
||
.rank-1 { background: #FFD700; color: #000; }
|
||
.rank-2 { background: #C0C0C0; color: #000; }
|
||
.rank-3 { background: #CD7F32; color: #fff; }
|
||
.rank-other { background: var(--kt-ai-bg-soft); color: var(--kt-ai-fg-muted); }
|
||
.amount { font-weight: var(--kt-ai-weight-semibold); color: var(--kt-ai-primary); }
|
||
.last-update { font-size: var(--kt-ai-text-sm); color: var(--kt-ai-fg-muted); text-align: center; padding: var(--kt-ai-space-4); }
|
||
.region-tag { display: inline-block; padding: 2px 10px; border-radius: var(--kt-ai-radius-full); font-size: var(--kt-ai-text-xs); background: var(--kt-ai-bg-soft); color: var(--kt-ai-fg); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="kt-ai-pagehead">
|
||
<div class="kt-ai-wrap kt-ai-pagehead-in">
|
||
<strong>Дашборд B2C</strong>
|
||
<span class="kt-ai-spacer"></span>
|
||
<span class="region-tag">Июль 2026</span>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="kt-ai-wrap dashboard-wrap">
|
||
<section>
|
||
<h1 style="margin-bottom: var(--kt-ai-space-2)">Топ услуг B2C по регионам</h1>
|
||
<p class="kt-ai-hero-sub" style="margin-bottom: var(--kt-ai-space-6)">Сводная статистика покупок услуг по всем регионам Казахстана</p>
|
||
|
||
<div class="kpi-grid" id="kpiGrid">
|
||
<div class="kpi-card">
|
||
<div class="kpi-label">Всего покупок</div>
|
||
<div class="kpi-value" id="totalPurchases">—</div>
|
||
<div class="kpi-sub" id="totalPurchasesSub">в топ-50</div>
|
||
</div>
|
||
<div class="kpi-card">
|
||
<div class="kpi-label">Всего регионов</div>
|
||
<div class="kpi-value" id="totalRegions">—</div>
|
||
<div class="kpi-sub">уникальных</div>
|
||
</div>
|
||
<div class="kpi-card">
|
||
<div class="kpi-label">Услуга-лидер</div>
|
||
<div class="kpi-value" style="font-size: var(--kt-ai-text-lg)" id="topService">—</div>
|
||
<div class="kpi-sub" id="topServicePurchases">—</div>
|
||
</div>
|
||
<div class="kpi-card">
|
||
<div class="kpi-label">Регион-лидер</div>
|
||
<div class="kpi-value" id="topRegion">—</div>
|
||
<div class="kpi-sub" id="topRegionPurchases">—</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="charts-grid">
|
||
<div class="chart-card">
|
||
<div class="chart-title">Топ-10 регионов по покупкам</div>
|
||
<div class="bar-chart" id="regionChart"></div>
|
||
</div>
|
||
<div class="chart-card">
|
||
<div class="chart-title">Топ-10 услуг по покупкам</div>
|
||
<div class="bar-chart" id="serviceChart"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<h2 style="font-size: var(--kt-ai-text-xl); margin-bottom: var(--kt-ai-space-4)">Полная таблица топ-50</h2>
|
||
<div class="table-container">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th style="width: 60px">#</th>
|
||
<th>Регион</th>
|
||
<th>Услуга</th>
|
||
<th style="text-align: right">Покупок</th>
|
||
<th style="text-align: right">Сумма (₸)</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="dataTable"></tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="last-update">
|
||
Данные за июль 2026 года | Источник: ClickHouse Хранилище АО «Казахтелеком»
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<footer class="kt-ai-wrap kt-ai-pagefoot">
|
||
<span>© 2026 Казахтелеком CRM</span>
|
||
<span class="kt-ai-spacer"></span>
|
||
<span>Дашборд B2C</span>
|
||
</footer>
|
||
|
||
<script>
|
||
// Данные из b2c_top_services_july_2026.md
|
||
const rawData = [
|
||
{ rank: 1, region: "Астана", service: "B2C Абонплата ШПД по FTTx ГТС (New)", purchases: 37372, amount: 238463786.09 },
|
||
{ rank: 2, region: "г.Алматы", service: "B2C Абонплата ШПД по FTTx ГТС (New)", purchases: 39671, amount: 237005698.16 },
|
||
{ rank: 3, region: "г.Алматы", service: "Абонплата ШПД по FTTx для B2C", purchases: 35588, amount: 152893561.28 },
|
||
{ rank: 4, region: "Астана", service: "2025 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 35637, amount: 136187938.99 },
|
||
{ rank: 5, region: "Астана", service: "B2C TV Plus Абонплата Full ГТС", purchases: 47661, amount: 132060372.93 },
|
||
{ rank: 6, region: "Астана", service: "2021 B2C Абонплата за мобильный номер Kcell GSM закр.", purchases: 61700, amount: 123403164.43 },
|
||
{ rank: 7, region: "г.Алматы", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 57378, amount: 110497209.55 },
|
||
{ rank: 8, region: "г.Алматы", service: "2025 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 29997, amount: 106749675.59 },
|
||
{ rank: 9, region: "Астана", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 52504, amount: 99235780.41 },
|
||
{ rank: 10, region: "Астана", service: "2022 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 36946, amount: 96430127.27 },
|
||
{ rank: 11, region: "Астана", service: "Абонплата ШПД по FTTx для B2C", purchases: 19565, amount: 88834659.73 },
|
||
{ rank: 12, region: "Астана", service: "2024 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 22167, amount: 83549325.34 },
|
||
{ rank: 13, region: "г.Алматы", service: "B2C TV Plus Абонплата Full ГТС", purchases: 31221, amount: 83511013.05 },
|
||
{ rank: 14, region: "г.Алматы", service: "2021 B2C Абонплата за мобильный номер Kcell GSM закр.", purchases: 40838, amount: 82748215.46 },
|
||
{ rank: 15, region: "г.Семей", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 41486, amount: 76400272.02 },
|
||
{ rank: 16, region: "Астана", service: "TV Plus Абонплата за Премиальный пакет ГТС", purchases: 29210, amount: 71278165.09 },
|
||
{ rank: 17, region: "г.Алматы", service: "2022 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 24001, amount: 62521274.30 },
|
||
{ rank: 18, region: "Астана", service: "2021 B2C Абонплата за мобильный номер Kcell GSM откр.", purchases: 27864, amount: 59635624.39 },
|
||
{ rank: 19, region: "г.Алматы", service: "2024 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 15678, amount: 58970815.48 },
|
||
{ rank: 20, region: "г.Актобе", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 30641, amount: 55819936.98 },
|
||
{ rank: 21, region: "г.Алматы", service: "B2C Абонплата за основной телефон ГТС", purchases: 52732, amount: 49636818.50 },
|
||
{ rank: 22, region: "г.Актобе", service: "2021 B2C Абонплата за мобильный номер Kcell GSM закр.", purchases: 25178, amount: 48000483.43 },
|
||
{ rank: 23, region: "г.Алматы", service: "TV Plus Абонплата за Премиальный пакет ГТС", purchases: 19055, amount: 45298192.89 },
|
||
{ rank: 24, region: "г.Павлодар", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 23334, amount: 44857518.84 },
|
||
{ rank: 25, region: "г.Алматы", service: "B2C TV Plus Аренда STB оператора новое", purchases: 96183, amount: 43252519.71 },
|
||
{ rank: 26, region: "г.Караганда", service: "Абонплата ШПД по FTTx для B2C", purchases: 8916, amount: 41624727.66 },
|
||
{ rank: 27, region: "Астана", service: "2026 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 13685, amount: 41618770.34 },
|
||
{ rank: 28, region: "г.Алматы", service: "2018 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 11568, amount: 40199964.35 },
|
||
{ rank: 29, region: "г.Тараз", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 21880, amount: 39564813.34 },
|
||
{ rank: 30, region: "г.Алматы", service: "Абонплата ШПД по ADSL для B2C", purchases: 10710, amount: 36238454.72 },
|
||
{ rank: 31, region: "г.Алматы", service: "2020 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 12148, amount: 36010481.68 },
|
||
{ rank: 32, region: "г.Усть-Каменогорск", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 18362, amount: 35883527.39 },
|
||
{ rank: 33, region: "г. Шымкент", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 19180, amount: 35338634.51 },
|
||
{ rank: 34, region: "г. Шымкент", service: "2021 B2C Абонплата за мобильный номер Kcell GSM закр.", purchases: 18945, amount: 35138664.91 },
|
||
{ rank: 35, region: "Астана", service: "B2C TV Plus Аренда STB оператора новое", purchases: 76655, amount: 34746783.92 },
|
||
{ rank: 36, region: "Астана", service: "2018 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 8705, amount: 32905725.03 },
|
||
{ rank: 37, region: "г.Актобе", service: "B2C Абонплата ШПД по FTTx ГТС (New)", purchases: 6742, amount: 32313021.91 },
|
||
{ rank: 38, region: "г.Усть-Каменогорск", service: "Абонплата ШПД по FTTx для B2C", purchases: 7269, amount: 31834355.15 },
|
||
{ rank: 39, region: "г.Атырау", service: "B2C Абонплата ШПД по FTTx ГТС (New)", purchases: 5495, amount: 31688118.61 },
|
||
{ rank: 40, region: "г.Усть-Каменогорск", service: "B2C Абонплата ШПД по FTTx ГТС (New)", purchases: 5600, amount: 31520413.53 },
|
||
{ rank: 41, region: "Астана", service: "B2C Аренда оборудования ONT оператора - новое", purchases: 87071, amount: 31220264.09 },
|
||
{ rank: 42, region: "г.Павлодар", service: "B2C Абонплата ШПД по FTTx ГТС (New)", purchases: 6104, amount: 30831455.08 },
|
||
{ rank: 43, region: "г.Кокшетау", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 16818, amount: 30654924.73 },
|
||
{ rank: 44, region: "г.Актобе", service: "2025 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 8613, amount: 30562705.78 },
|
||
{ rank: 45, region: "г.Костанай", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 16473, amount: 30539180.35 },
|
||
{ rank: 46, region: "Енбекшиказахский район", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 17347, amount: 30429552.38 },
|
||
{ rank: 47, region: "г.Актобе", service: "2022 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 11709, amount: 30303557.00 },
|
||
{ rank: 48, region: "г.Кызылорда", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 16539, amount: 29772241.67 },
|
||
{ rank: 49, region: "г.Алматы", service: "2026 B2C Абонплата за Домашний интернет FTTx ГТС", purchases: 10206, amount: 29200316.21 },
|
||
{ rank: 50, region: "г.Караганда", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", purchases: 15714, amount: 28837871.98 }
|
||
];
|
||
|
||
function formatNumber(n) {
|
||
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
||
}
|
||
|
||
function formatAmount(a) {
|
||
return formatNumber(Math.round(a).toString()) + " ₸";
|
||
}
|
||
|
||
function normalizeRegion(r) {
|
||
return r.replace(/^г\./, '').replace(/^\s+/, '').trim();
|
||
}
|
||
|
||
function calculateKPI(data) {
|
||
const totalPurchases = data.reduce((sum, d) => sum + d.purchases, 0);
|
||
|
||
const regionMap = {};
|
||
data.forEach(d => {
|
||
const reg = normalizeRegion(d.region);
|
||
regionMap[reg] = (regionMap[reg] || 0) + d.purchases;
|
||
});
|
||
const totalRegions = Object.keys(regionMap).length;
|
||
|
||
const topService = data.reduce((max, d) => d.purchases > max.purchases ? d : max, data[0]);
|
||
|
||
const regionTotals = {};
|
||
data.forEach(d => {
|
||
const reg = normalizeRegion(d.region);
|
||
regionTotals[reg] = (regionTotals[reg] || 0) + d.purchases;
|
||
});
|
||
const topRegion = Object.entries(regionTotals).sort((a, b) => b[1] - a[1])[0];
|
||
|
||
return { totalPurchases, totalRegions, topService, topRegion: topRegion[0], topRegionPurchases: topRegion[1] };
|
||
}
|
||
|
||
function renderKPI(kpi) {
|
||
document.getElementById('totalPurchases').textContent = formatNumber(kpi.totalPurchases);
|
||
document.getElementById('totalRegions').textContent = kpi.totalRegions;
|
||
document.getElementById('topService').textContent = kpi.topService.service.length > 40 ? kpi.topService.service.slice(0, 37) + '...' : kpi.topService.service;
|
||
document.getElementById('topServicePurchases').textContent = formatNumber(kpi.topService.purchases) + ' покупок';
|
||
document.getElementById('topRegion').textContent = kpi.topRegion;
|
||
document.getElementById('topRegionPurchases').textContent = formatNumber(kpi.topRegionPurchases) + ' покупок';
|
||
}
|
||
|
||
function renderRegionChart(data) {
|
||
const regionMap = {};
|
||
data.forEach(d => {
|
||
const reg = normalizeRegion(d.region);
|
||
regionMap[reg] = (regionMap[reg] || 0) + d.purchases;
|
||
});
|
||
const sorted = Object.entries(regionMap).sort((a, b) => b[1] - a[1]).slice(0, 10);
|
||
const maxVal = sorted[0][1];
|
||
|
||
const container = document.getElementById('regionChart');
|
||
container.innerHTML = sorted.map(function(item) {
|
||
const pct = (item[1] / maxVal) * 100;
|
||
return '<div class="bar-row">' +
|
||
'<div class="bar-label">' + item[0] + '</div>' +
|
||
'<div class="bar-track"><div class="bar-fill" style="width:' + pct + '%"></div></div>' +
|
||
'<div class="bar-value">' + formatNumber(item[1]) + '</div>' +
|
||
'</div>';
|
||
}).join('');
|
||
}
|
||
|
||
function renderServiceChart(data) {
|
||
const sorted = data.slice(0, 10);
|
||
const maxVal = sorted[0].purchases;
|
||
|
||
const container = document.getElementById('serviceChart');
|
||
container.innerHTML = sorted.map(function(item, i) {
|
||
const pct = (item.purchases / maxVal) * 100;
|
||
const svcName = item.service.length > 35 ? item.service.slice(0, 32) + '...' : item.service;
|
||
return '<div class="bar-row">' +
|
||
'<div class="bar-label" style="width:160px">' + svcName + '</div>' +
|
||
'<div class="bar-track"><div class="bar-fill" style="width:' + pct + '%"></div></div>' +
|
||
'<div class="bar-value">' + formatNumber(item.purchases) + '</div>' +
|
||
'</div>';
|
||
}).join('');
|
||
}
|
||
|
||
function renderTable(data) {
|
||
const tbody = document.getElementById('dataTable');
|
||
tbody.innerHTML = data.map(function(d, i) {
|
||
const rankClass = d.rank <= 3 ? 'rank-' + d.rank : 'rank-other';
|
||
return '<tr>' +
|
||
'<td><span class="rank ' + rankClass + '">' + d.rank + '</span></td>' +
|
||
'<td>' + d.region + '</td>' +
|
||
'<td>' + d.service + '</td>' +
|
||
'<td style="text-align:right">' + formatNumber(d.purchases) + '</td>' +
|
||
'<td style="text-align:right" class="amount">' + formatAmount(d.amount) + '</td>' +
|
||
'</tr>';
|
||
}).join('');
|
||
}
|
||
|
||
// Инициализация
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const kpi = calculateKPI(rawData);
|
||
renderKPI(kpi);
|
||
renderRegionChart(rawData);
|
||
renderServiceChart(rawData);
|
||
renderTable(rawData);
|
||
});
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|