282 lines
21 KiB
HTML
282 lines
21 KiB
HTML
<!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>
|