fix: подключил KT AI DS правильными путями
This commit is contained in:
parent
556c257c44
commit
afb6bd88ea
228
index.html
228
index.html
@ -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();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user