Опубликовано через кнопку

This commit is contained in:
tore 2026-09-12 14:59:32 +00:00
parent 48602415c6
commit c1de8e30bd

View File

@ -12,6 +12,7 @@
.kt-ai-app { background: var(--kt-ai-bg); color: var(--kt-ai-fg); font-family: var(--kt-ai-font-sans); min-height: 100vh; }
.metrics-header { display: flex; align-items: center; gap: var(--kt-ai-space-4); padding: var(--kt-ai-space-4) 0; border-bottom: 1px solid var(--kt-ai-border); background: var(--kt-ai-bg); position: sticky; top: 0; z-index: 10; }
.metrics-title { font-size: var(--kt-ai-text-xl); font-weight: var(--kt-ai-weight-semibold); }
.demo-badge { background: var(--kt-ai-chart-orange); color: var(--kt-ai-fg); font-size: var(--kt-ai-text-xs); font-weight: var(--kt-ai-weight-bold); padding: 4px 10px; border-radius: var(--kt-ai-radius-full); text-transform: uppercase; letter-spacing: 0.5px; }
.filters-row { display: flex; gap: var(--kt-ai-space-3); align-items: center; flex-wrap: wrap; margin-bottom: var(--kt-ai-space-6); }
.kpi-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--kt-ai-space-4); margin-bottom: var(--kt-ai-space-6); }
.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); }
@ -52,6 +53,7 @@
<header class="metrics-header">
<img src="https://code.vibe42.kz/tore/aitu/kt-logo.svg" alt="Казахтелеком" height="28">
<span class="metrics-title">Business Metrics</span>
<span class="demo-badge">Демонстрационные данные</span>
<span class="kt-ai-spacer"></span>
<button class="kt-ai-btn" data-variant="ghost" id="resetBtn">Сбросить фильтры</button>
</header>
@ -82,7 +84,13 @@
<option value="7d">7 дней</option>
<option value="30d">30 дней</option>
<option value="90d">90 дней</option>
<option value="year">Год</option>
</select>
</div>
<div class="kt-ai-field">
<label style="font-size: var(--kt-ai-text-xs); color: var(--kt-ai-fg-muted); display: block; margin-bottom: var(--kt-ai-space-1);">Режим</label>
<select class="kt-ai-select" id="dataModeFilter" style="min-width: 160px;">
<option value="data">С данными</option>
<option value="nodata">Нет данных</option>
</select>
</div>
</div>
@ -141,17 +149,13 @@
<div class="insights-title">Анализ отклонений</div>
<div id="insightsContent">
<div class="insight-card">
<div class="insight-label">Объяснение отклонений</div>
<div class="insight-label">Гипотеза причин отклонений</div>
<div class="insight-text" id="explanationText">Загрузка...</div>
</div>
<div class="insight-card">
<div class="insight-label">Рекомендации</div>
<ul class="recommendation-list" id="recommendationsList"></ul>
</div>
<div class="insight-card">
<div class="insight-label">Исходные показатели</div>
<div class="insight-text" id="sourceLink">Загрузка...</div>
</div>
</div>
</div>
@ -164,53 +168,129 @@
<script src="./design-system/kt-ai-chart-tip.js"></script>
<script>
const DEMO_DATA = {
metrics: [
{ channel: 'call-center', segment: 'B2B', nps: 38, csat: 84, fcr: 72, aht: 5.2, requests: 1240 },
{ channel: 'call-center', segment: 'B2C', nps: 45, csat: 89, fcr: 78, aht: 3.8, requests: 3420 },
{ channel: 'online', segment: 'B2B', nps: 52, csat: 92, fcr: 85, aht: 2.1, requests: 890 },
{ channel: 'online', segment: 'B2C', nps: 48, csat: 88, fcr: 80, aht: 2.5, requests: 2150 },
{ channel: 'mobile-app', segment: 'B2B', nps: 44, csat: 86, fcr: 75, aht: 3.2, requests: 560 },
{ channel: 'mobile-app', segment: 'B2C', nps: 50, csat: 91, fcr: 82, aht: 2.8, requests: 1890 },
{ channel: 'office', segment: 'B2B', nps: 35, csat: 80, fcr: 68, aht: 6.5, requests: 320 },
{ channel: 'office', segment: 'B2C', nps: 40, csat: 85, fcr: 74, aht: 4.5, requests: 980 }
],
history: [
{ period: 'Янв', nps: 39 }, { period: 'Фев', nps: 41 }, { period: 'Мар', nps: 38 },
{ period: 'Апр', nps: 43 }, { period: 'Май', nps: 45 }, { period: 'Июн', nps: 42 },
{ period: 'Июл', nps: 44 }, { period: 'Авг', nps: 46 }, { period: 'Сен', nps: 42 },
{ period: 'Окт', nps: 40 }, { period: 'Ноя', nps: 43 }, { period: 'Дек', nps: 42 }
],
insights: {
B2B: {
explanation: 'Снижение FCR в B2B-сегменте связано с увеличением сложных технических запросов. AHT выше среднего из-за длительной обработки заявок в офисах.',
recommendations: [
'Усилить техническую подготовку операторов B2B-направления',
'Внедрить шаблоны ответов для типовых сложных запросов',
'Сократить время обработки в офисах через цифровизацию процессов'
],
source: 'Показатели взяты из системы мониторинга качества за 4 квартал 2025'
},
B2C: {
explanation: 'B2C-сегмент показывает стабильные результаты. Небольшое снижение NPS в сентябре связано с сезонным ростом нагрузки.',
recommendations: [
'Поддерживать текущий уровень сервиса в онлайн-каналах',
'Провести работу над ошибками в call-центре для роста FCR'
],
source: 'Показатели взяты из системы мониторинга качества за 4 квартал 2025'
},
all: {
explanation: 'Общие показатели в пределах нормы. Наблюдается положительная динамика NPS за последние 3 месяца.',
recommendations: [
'Продолжить развитие онлайн-каналов обслуживания',
'Провести обучение операторов call-центра',
'Внедрить единую базу знаний для всех каналов'
],
source: 'Показатели взяты из системы мониторинга качества за 4 квартал 2025'
'7d': {
metrics: [
{ channel: 'call-center', segment: 'B2B', nps: 38, csat: 84, fcr: 72, aht: 5.2, requests: 320 },
{ channel: 'call-center', segment: 'B2C', nps: 45, csat: 89, fcr: 78, aht: 3.8, requests: 890 },
{ channel: 'online', segment: 'B2B', nps: 52, csat: 92, fcr: 85, aht: 2.1, requests: 240 },
{ channel: 'online', segment: 'B2C', nps: 48, csat: 88, fcr: 80, aht: 2.5, requests: 560 },
{ channel: 'mobile-app', segment: 'B2B', nps: 44, csat: 86, fcr: 75, aht: 3.2, requests: 150 },
{ channel: 'mobile-app', segment: 'B2C', nps: 50, csat: 91, fcr: 82, aht: 2.8, requests: 480 },
{ channel: 'office', segment: 'B2B', nps: 35, csat: 80, fcr: 68, aht: 6.5, requests: 85 },
{ channel: 'office', segment: 'B2C', nps: 40, csat: 85, fcr: 74, aht: 4.5, requests: 260 }
],
history: [
{ period: 'Пн', nps: 40 }, { period: 'Вт', nps: 42 }, { period: 'Ср', nps: 39 },
{ period: 'Чт', nps: 44 }, { period: 'Пт', nps: 43 }, { period: 'Сб', nps: 41 }, { period: 'Вс', nps: 42 }
],
insights: {
B2B: {
explanation: 'Возможно, снижение FCR в B2B связано с увеличением сложных технических запросов на этой неделе. AHT выше среднего из-за длительной обработки в офисах.',
recommendations: [
'Усилить техническую подготовку операторов B2B-направления',
'Внедрить шаблоны ответов для типовых сложных запросов'
]
},
B2C: {
explanation: 'Возможно, небольшое снижение NPS в выходные связано с ростом нагрузки в выходные дни.',
recommendations: [
'Поддерживать текущий уровень сервиса в онлайн-каналах',
'Провести работу над ошибками в call-центре для роста FCR'
]
},
all: {
explanation: 'Общие показатели в пределах нормы. Наблюдается стабильная динамика NPS в течение недели.',
recommendations: [
'Продолжить развитие онлайн-каналов обслуживания',
'Провести обучение операторов call-центра'
]
}
}
},
'30d': {
metrics: [
{ channel: 'call-center', segment: 'B2B', nps: 40, csat: 85, fcr: 74, aht: 5.0, requests: 1240 },
{ channel: 'call-center', segment: 'B2C', nps: 47, csat: 90, fcr: 79, aht: 3.6, requests: 3420 },
{ channel: 'online', segment: 'B2B', nps: 54, csat: 93, fcr: 86, aht: 2.0, requests: 890 },
{ channel: 'online', segment: 'B2C', nps: 50, csat: 89, fcr: 81, aht: 2.4, requests: 2150 },
{ channel: 'mobile-app', segment: 'B2B', nps: 46, csat: 87, fcr: 76, aht: 3.0, requests: 560 },
{ channel: 'mobile-app', segment: 'B2C', nps: 52, csat: 92, fcr: 83, aht: 2.6, requests: 1890 },
{ channel: 'office', segment: 'B2B', nps: 37, csat: 81, fcr: 70, aht: 6.2, requests: 320 },
{ channel: 'office', segment: 'B2C', nps: 42, csat: 86, fcr: 75, aht: 4.3, requests: 980 }
],
history: [
{ period: 'Нед 1', nps: 39 }, { period: 'Нед 2', nps: 42 }, { period: 'Нед 3', nps: 44 },
{ period: 'Нед 4', nps: 43 }
],
insights: {
B2B: {
explanation: 'Возможно, снижение FCR в B2B-сегменте связано с увеличением сложных технических запросов в этом месяце.',
recommendations: [
'Усилить техническую подготовку операторов B2B-направления',
'Внедрить шаблоны ответов для типовых сложных запросов',
'Сократить время обработки в офисах через цифровизацию'
]
},
B2C: {
explanation: 'B2C-сегмент показывает стабильные результаты. Возможно, небольшое снижение NPS связано с сезонным ростом нагрузки.',
recommendations: [
'Поддерживать текущий уровень сервиса в онлайн-каналах',
'Провести работу над ошибками в call-центре'
]
},
all: {
explanation: 'Общие показатели в пределах нормы. Наблюдается положительная динамика NPS за месяц.',
recommendations: [
'Продолжить развитие онлайн-каналов обслуживания',
'Провести обучение операторов call-центра',
'Внедрить единую базу знаний для всех каналов'
]
}
}
},
'90d': {
metrics: [
{ channel: 'call-center', segment: 'B2B', nps: 41, csat: 86, fcr: 75, aht: 4.9, requests: 3800 },
{ channel: 'call-center', segment: 'B2C', nps: 48, csat: 91, fcr: 80, aht: 3.5, requests: 10200 },
{ channel: 'online', segment: 'B2B', nps: 55, csat: 94, fcr: 87, aht: 1.9, requests: 2700 },
{ channel: 'online', segment: 'B2C', nps: 51, csat: 90, fcr: 82, aht: 2.3, requests: 6400 },
{ channel: 'mobile-app', segment: 'B2B', nps: 47, csat: 88, fcr: 77, aht: 2.9, requests: 1700 },
{ channel: 'mobile-app', segment: 'B2C', nps: 53, csat: 93, fcr: 84, aht: 2.5, requests: 5600 },
{ channel: 'office', segment: 'B2B', nps: 38, csat: 82, fcr: 71, aht: 6.0, requests: 950 },
{ channel: 'office', segment: 'B2C', nps: 43, csat: 87, fcr: 76, aht: 4.2, requests: 2900 }
],
history: [
{ period: 'Июл', nps: 44 }, { period: 'Авг', nps: 46 }, { period: 'Сен', nps: 42 }
],
insights: {
B2B: {
explanation: 'Возможно, снижение FCR в B2B-сегменте за квартал связано с увеличением объёма сложных технических запросов.',
recommendations: [
'Усилить техническую подготовку операторов B2B-направления',
'Внедрить шаблоны ответов для типовых сложных запросов',
'Сократить время обработки в офисах через цифровизацию процессов'
]
},
B2C: {
explanation: 'B2C-сегмент показывает стабильные результаты за квартал. Возможно, снижение NPS в сентябре связано с сезонным фактором.',
recommendations: [
'Поддерживать текущий уровень сервиса в онлайн-каналах',
'Провести работу над ошибками в call-центре для роста FCR'
]
},
all: {
explanation: 'Общие показатели в пределах нормы. Наблюдается положительная динамика NPS за последние 3 месяца.',
recommendations: [
'Продолжить развитие онлайн-каналов обслуживания',
'Провести обучение операторов call-центра',
'Внедрить единую базу знаний для всех каналов'
]
}
}
}
};
let currentFilters = { segment: 'all', channel: 'all', period: '7d' };
let currentFilters = { segment: 'all', channel: 'all', period: '7d', dataMode: 'data' };
function loadFilters() {
const saved = localStorage.getItem('bm_filters');
@ -219,6 +299,7 @@ function loadFilters() {
document.getElementById('segmentFilter').value = currentFilters.segment;
document.getElementById('channelFilter').value = currentFilters.channel;
document.getElementById('periodFilter').value = currentFilters.period;
document.getElementById('dataModeFilter').value = currentFilters.dataMode;
}
}
@ -227,7 +308,8 @@ function saveFilters() {
}
function getFilteredData() {
let data = DEMO_DATA.metrics;
const periodData = DEMO_DATA[currentFilters.period] || DEMO_DATA['7d'];
let data = periodData.metrics;
if (currentFilters.segment !== 'all') {
data = data.filter(m => m.segment === currentFilters.segment);
}
@ -237,6 +319,16 @@ function getFilteredData() {
return data;
}
function getHistory() {
const periodData = DEMO_DATA[currentFilters.period] || DEMO_DATA['7d'];
return periodData.history;
}
function getInsights(segment) {
const periodData = DEMO_DATA[currentFilters.period] || DEMO_DATA['7d'];
return periodData.insights[segment] || periodData.insights.all;
}
function calculateAverages(data) {
if (data.length === 0) return null;
const sum = data.reduce((acc, m) => ({
@ -270,12 +362,16 @@ function renderKPIs(avgs) {
function renderChart() {
const container = document.getElementById('chartContainer');
const maxNPS = Math.max(...DEMO_DATA.history.map(h => h.nps));
container.innerHTML = DEMO_DATA.history.map((h, i) => {
const history = getHistory();
if (!history || history.length === 0) {
container.innerHTML = '<div class="no-data" style="padding: var(--kt-ai-space-10);">Недостаточно данных</div>';
return;
}
const maxNPS = Math.max(...history.map(h => h.nps));
const colors = ['var(--kt-ai-chart-blue)', 'var(--kt-ai-chart-green)', 'var(--kt-ai-chart-orange)', 'var(--kt-ai-chart-pink)', 'var(--kt-ai-chart-red)', 'var(--kt-ai-chart-salmon)'];
container.innerHTML = history.map((h, i) => {
const height = (h.nps / maxNPS) * 160;
const colorIndex = i % 6;
const colors = ['var(--kt-ai-chart-blue)', 'var(--kt-ai-chart-green)', 'var(--kt-ai-chart-orange)', 'var(--kt-ai-chart-pink)', 'var(--kt-ai-chart-red)', 'var(--kt-ai-chart-salmon)'];
return `<div class="chart-bar" style="height: ${height}px; background: ${colors[colorIndex]}" data-kt-tip="${h.period}: ${h.nps}"></div>`;
return `<div class="chart-bar" style="height: ${height}px; background: ${colors[i % colors.length]}" data-kt-tip="${h.period}: ${h.nps}"></div>`;
}).join('');
}
@ -299,27 +395,40 @@ function renderTable(data) {
}
function renderInsights(segment) {
const insight = DEMO_DATA.insights[segment] || DEMO_DATA.insights.all;
if (currentFilters.dataMode === 'nodata') {
document.getElementById('insightsContent').style.display = 'none';
return;
}
document.getElementById('insightsContent').style.display = 'block';
const insight = getInsights(segment);
document.getElementById('explanationText').textContent = insight.explanation;
document.getElementById('recommendationsList').innerHTML = insight.recommendations.map(r => `<li>${r}</li>`).join('');
document.getElementById('sourceLink').textContent = insight.source;
}
function render() {
const noData = currentFilters.dataMode === 'nodata';
document.getElementById('kpiStrip').style.display = noData ? 'none' : 'grid';
document.getElementById('noDataBlock').style.display = noData ? 'block' : 'none';
document.getElementById('metricsTable').closest('.table-wrap').style.display = noData ? 'none' : 'block';
if (noData) {
renderChart();
renderInsights(null);
return;
}
const data = getFilteredData();
const avgs = calculateAverages(data);
renderKPIs(avgs);
renderChart();
renderTable(data);
renderInsights(currentFilters.segment);
document.getElementById('noDataBlock').style.display = data.length === 0 ? 'block' : 'none';
}
function onFilterChange() {
currentFilters = {
segment: document.getElementById('segmentFilter').value,
channel: document.getElementById('channelFilter').value,
period: document.getElementById('periodFilter').value
period: document.getElementById('periodFilter').value,
dataMode: document.getElementById('dataModeFilter').value
};
saveFilters();
render();
@ -329,10 +438,11 @@ document.getElementById('segmentFilter').addEventListener('change', onFilterChan
document.getElementById('channelFilter').addEventListener('change', onFilterChange);
document.getElementById('periodFilter').addEventListener('change', onFilterChange);
document.getElementById('resetBtn').addEventListener('click', () => {
currentFilters = { segment: 'all', channel: 'all', period: '7d' };
currentFilters = { segment: 'all', channel: 'all', period: '7d', dataMode: 'data' };
document.getElementById('segmentFilter').value = 'all';
document.getElementById('channelFilter').value = 'all';
document.getElementById('periodFilter').value = '7d';
document.getElementById('dataModeFilter').value = 'data';
saveFilters();
render();
});