fix: подключил KT AI DS правильными путями

This commit is contained in:
tore 2026-09-04 07:15:24 +00:00
parent 556c257c44
commit afb6bd88ea

View File

@ -1,104 +1,95 @@
<!doctype html>
<html lang="ru">
<html lang="ru" data-theme="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Топ услуг B2C по регионам</title>
<link rel="stylesheet" href="./design-system/fonts.css">
<link rel="stylesheet" href="./design-system/variables.css">
<link rel="stylesheet" href="./design-system/base.css">
<link rel="stylesheet" href="./design-system/components.css">
<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">
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<style>
* { margin: 0; box-sizing: border-box; }
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--kt-bg-secondary, #f5f5f7);
color: var(--kt-text-primary, #1a1a1a);
padding: 24px;
}
.container { max-width: 1400px; margin: 0 auto; }
.header {
.dashboard-header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 32px;
padding: 20px 24px;
background: var(--kt-bg-primary, #ffffff);
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
background: var(--kt-ai-card-bg);
border-radius: var(--kt-ai-radius-xl);
border: 1px solid var(--kt-ai-border);
}
.header img { height: 48px; }
.header h1 {
.dashboard-header img { height: 48px; }
.dashboard-header h1 {
font-size: 24px;
font-weight: 600;
color: var(--kt-text-primary, #1a1a1a);
color: var(--kt-ai-fg);
}
.header-meta {
margin-left: auto;
font-size: 14px;
color: var(--kt-text-secondary, #6b7280);
color: var(--kt-ai-fg-muted);
text-align: right;
}
.summary {
.summary-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
margin-bottom: 32px;
}
.card {
background: var(--kt-bg-primary, #ffffff);
border-radius: 12px;
.kpi-card {
background: var(--kt-ai-card-bg);
border-radius: var(--kt-ai-radius-xl);
padding: 24px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
border: 1px solid var(--kt-ai-border);
}
.card-label {
.kpi-label {
font-size: 14px;
color: var(--kt-text-secondary, #6b7280);
color: var(--kt-ai-fg-muted);
margin-bottom: 8px;
}
.card-value {
.kpi-value {
font-size: 32px;
font-weight: 700;
color: var(--kt-primary, #0096D7);
color: var(--kt-ai-primary);
}
.card-sub {
.kpi-sub {
font-size: 13px;
color: var(--kt-text-secondary, #6b7280);
color: var(--kt-ai-fg-faint);
margin-top: 8px;
}
.charts {
.charts-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
margin-bottom: 32px;
}
@media (max-width: 1024px) {
.charts { grid-template-columns: 1fr; }
.charts-grid { grid-template-columns: 1fr; }
}
.chart-container {
background: var(--kt-bg-primary, #ffffff);
border-radius: 12px;
.chart-card {
background: var(--kt-ai-card-bg);
border-radius: var(--kt-ai-radius-xl);
padding: 24px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
border: 1px solid var(--kt-ai-border);
}
.chart-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 16px;
color: var(--kt-text-primary, #1a1a1a);
color: var(--kt-ai-fg);
}
.table-section {
background: var(--kt-bg-primary, #ffffff);
border-radius: 12px;
.table-card {
background: var(--kt-ai-card-bg);
border-radius: var(--kt-ai-radius-xl);
padding: 24px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
border: 1px solid var(--kt-ai-border);
}
.table-header {
.table-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
@ -109,7 +100,7 @@
.table-title {
font-size: 18px;
font-weight: 600;
color: var(--kt-text-primary, #1a1a1a);
color: var(--kt-ai-fg);
}
.filter-group {
display: flex;
@ -118,81 +109,81 @@
}
.filter-label {
font-size: 14px;
color: var(--kt-text-secondary, #6b7280);
color: var(--kt-ai-fg-muted);
}
.filter-select {
padding: 8px 12px;
border: 1px solid var(--kt-border, #e5e7eb);
border-radius: 8px;
border: 1px solid var(--kt-ai-border);
border-radius: var(--kt-ai-radius-md);
font-size: 14px;
background: var(--kt-bg-primary, #ffffff);
color: var(--kt-text-primary, #1a1a1a);
background: var(--kt-ai-card-bg);
color: var(--kt-ai-fg);
cursor: pointer;
}
.filter-select:focus {
outline: 2px solid var(--kt-primary, #0096D7);
outline: 2px solid var(--kt-ai-primary);
border-color: transparent;
}
.table-wrapper {
overflow-x: auto;
}
table {
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
}
th {
.data-table th {
text-align: left;
padding: 12px 16px;
background: var(--kt-bg-secondary, #f5f5f7);
color: var(--kt-text-secondary, #6b7280);
background: var(--kt-ai-bg-soft);
color: var(--kt-ai-fg-muted);
font-weight: 600;
border-bottom: 2px solid var(--kt-border, #e5e7eb);
border-bottom: 2px solid var(--kt-ai-border);
}
td {
.data-table td {
padding: 14px 16px;
border-bottom: 1px solid var(--kt-border, #e5e7eb);
color: var(--kt-text-primary, #1a1a1a);
border-bottom: 1px solid var(--kt-ai-border);
color: var(--kt-ai-fg);
}
tr:hover {
background: var(--kt-bg-secondary, #f5f5f7);
.data-table tr:hover {
background: var(--kt-ai-bg-soft);
}
.rank {
.rank-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--kt-primary, #0096D7);
background: var(--kt-ai-primary);
color: white;
font-weight: 600;
font-size: 13px;
}
.amount {
.amount-cell {
font-weight: 600;
color: var(--kt-text-primary, #1a1a1a);
color: var(--kt-ai-fg);
}
.region-tag {
.region-chip {
display: inline-block;
padding: 4px 10px;
background: var(--kt-bg-secondary, #f5f5f7);
border-radius: 6px;
background: var(--kt-ai-bg-soft);
border-radius: var(--kt-ai-radius-md);
font-size: 13px;
color: var(--kt-text-primary, #1a1a1a);
color: var(--kt-ai-fg);
}
.empty-state {
text-align: center;
padding: 48px 24px;
color: var(--kt-text-secondary, #6b7280);
color: var(--kt-ai-fg-muted);
}
</style>
</head>
<body>
<div class="container">
<header class="header">
<div class="kt-ai-wrap">
<header class="dashboard-header">
<img src="./kt-logo.svg" alt="Казахтелеком" height="48">
<h1>Топ услуг B2C по регионам</h1>
<div class="header-meta">
@ -201,21 +192,21 @@
</div>
</header>
<section class="summary" id="summary"></section>
<section class="summary-grid" id="summary"></section>
<section class="charts">
<div class="chart-container">
<section class="charts-grid">
<div class="chart-card">
<h2 class="chart-title">Топ-10 регионов по количеству покупок</h2>
<canvas id="regionChart"></canvas>
</div>
<div class="chart-container">
<div class="chart-card">
<h2 class="chart-title">Топ-10 услуг по количеству покупок</h2>
<canvas id="serviceChart"></canvas>
</div>
</section>
<section class="table-section">
<div class="table-header">
<section class="table-card">
<div class="table-toolbar">
<h2 class="table-title">Детализация по услугам</h2>
<div class="filter-group">
<label class="filter-label" for="regionFilter">Регион:</label>
@ -225,7 +216,7 @@
</div>
</div>
<div class="table-wrapper">
<table>
<table class="data-table">
<thead>
<tr>
<th>#</th>
@ -242,7 +233,6 @@
</div>
<script>
// Исходные данные из брифа
const RAW_DATA = [
{ region: "Астана", service: "B2C Абонплата ШПД по FTTx ГТС (New)", qty: 37372, amount: 238463786.09 },
{ region: "г.Алматы", service: "B2C Абонплата ШПД по FTTx ГТС (New)", qty: 39671, amount: 237005698.16 },
@ -296,7 +286,6 @@
{ region: "г.Караганда", service: "2022 B2C Абонплата за мобильный номер MT-C GSM закр.", qty: 15714, amount: 28837871.98 }
];
// Форматирование чисел
function fmtNum(n) {
return n.toString().replace(/\D/g, '').replace(/\B(?=(\d{3})+(?!\d))/g, " ");
}
@ -304,7 +293,6 @@
return n.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$& ') + ' ₸';
}
// Агрегация данных
function aggregate() {
const byRegion = {};
const byService = {};
@ -329,35 +317,33 @@
};
}
// Рендер сводных плиток
function renderSummary() {
const agg = aggregate();
const container = document.getElementById('summary');
container.innerHTML = `
<div class="card">
<div class="card-label">Всего покупок</div>
<div class="card-value">${fmtNum(agg.totalQty)}</div>
<div class="card-sub">за июль 2026</div>
<div class="kpi-card">
<div class="kpi-label">Всего покупок</div>
<div class="kpi-value">${fmtNum(agg.totalQty)}</div>
<div class="kpi-sub">за июль 2026</div>
</div>
<div class="card">
<div class="card-label">Всего регионов</div>
<div class="card-value">${agg.totalRegions}</div>
<div class="card-sub">в топ-50</div>
<div class="kpi-card">
<div class="kpi-label">Всего регионов</div>
<div class="kpi-value">${agg.totalRegions}</div>
<div class="kpi-sub">в топ-50</div>
</div>
<div class="card">
<div class="card-label">Услуга-лидер</div>
<div class="card-value" style="font-size:18px;line-height:1.4">${agg.topService}</div>
<div class="card-sub">по количеству покупок</div>
<div class="kpi-card">
<div class="kpi-label">Услуга-лидер</div>
<div class="kpi-value" style="font-size:18px;line-height:1.4">${agg.topService}</div>
<div class="kpi-sub">по количеству покупок</div>
</div>
<div class="card">
<div class="card-label">Регион-лидер</div>
<div class="card-value">${agg.topRegion}</div>
<div class="card-sub">больше всего покупок</div>
<div class="kpi-card">
<div class="kpi-label">Регион-лидер</div>
<div class="kpi-value">${agg.topRegion}</div>
<div class="kpi-sub">больше всего покупок</div>
</div>
`;
}
// Графики
let regionChartInstance = null;
let serviceChartInstance = null;
@ -367,11 +353,10 @@
const top10Services = agg.services.slice(0, 10);
const colors = [
'#0096D7', '#0077A8', '#005F87', '#004A6B', '#003852',
'var(--kt-ai-primary)', '#0077A8', '#005F87', '#004A6B', '#003852',
'#00C7E3', '#00A3C7', '#0087B0', '#006B94', '#005278'
];
// Регионы
const ctx1 = document.getElementById('regionChart').getContext('2d');
if (regionChartInstance) regionChartInstance.destroy();
regionChartInstance = new Chart(ctx1, {
@ -387,22 +372,14 @@
},
options: {
responsive: true,
plugins: {
legend: { display: false }
},
plugins: { legend: { display: false } },
scales: {
y: {
beginAtZero: true,
grid: { color: '#f0f0f0' }
},
x: {
grid: { display: false }
}
y: { beginAtZero: true, grid: { color: 'rgba(0,0,0,0.05)' } },
x: { grid: { display: false } }
}
}
});
// Услуги
const ctx2 = document.getElementById('serviceChart').getContext('2d');
if (serviceChartInstance) serviceChartInstance.destroy();
serviceChartInstance = new Chart(ctx2, {
@ -419,23 +396,15 @@
options: {
indexAxis: 'y',
responsive: true,
plugins: {
legend: { display: false }
},
plugins: { legend: { display: false } },
scales: {
x: {
beginAtZero: true,
grid: { color: '#f0f0f0' }
},
y: {
grid: { display: false }
}
x: { beginAtZero: true, grid: { color: 'rgba(0,0,0,0.05)' } },
y: { grid: { display: false } }
}
}
});
}
// Таблица
function renderTable(filterRegion = '') {
const tbody = document.getElementById('tableBody');
let filtered = RAW_DATA;
@ -451,16 +420,15 @@
tbody.innerHTML = filtered.map((r, i) => `
<tr>
<td><span class="rank">${i + 1}</span></td>
<td><span class="region-tag">${r.region}</span></td>
<td><span class="rank-badge">${i + 1}</span></td>
<td><span class="region-chip">${r.region}</span></td>
<td>${r.service}</td>
<td>${fmtNum(r.qty)}</td>
<td class="amount">${fmtAmount(r.amount)}</td>
<td class="amount-cell">${fmtAmount(r.amount)}</td>
</tr>
`).join('');
}
// Заполнение фильтра регионов
function initFilter() {
const select = document.getElementById('regionFilter');
const regions = [...new Set(RAW_DATA.map(r => r.region))].sort();
@ -477,13 +445,11 @@
localStorage.setItem('b2c_region_filter', e.target.value);
});
// Восстановление из localStorage
const saved = localStorage.getItem('b2c_region_filter') || '';
select.value = saved;
renderTable(saved);
}
// Инициализация
document.addEventListener('DOMContentLoaded', () => {
renderSummary();
renderCharts();