dashbord-roznichnyh-prodazh-5/script.js

462 lines
22 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Дашборд розничных продаж 2026: данные, графики, таблицы, фильтры.
// ---------- Данные ----------
var MONTHS = ["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь"];
var MONTHS_SHORT = ["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен"];
var GROUP_DEFS = [
{ id:"connect", name:"Подключение", base:"var(--kt-ai-chart-blue)" },
{ id:"mobile", name:"Мобильная связь", base:"var(--kt-ai-chart-green)" },
{ id:"devices", name:"Устройства", base:"var(--kt-ai-chart-orange)" },
{ id:"content", name:"Тарифы и контент", base:"var(--kt-ai-chart-pink)" },
{ id:"extras", name:"Доп. услуги", base:"var(--kt-ai-chart-salmon)" }
];
// Объём продаж (млн тг) по месяцам за 2025 (сравнение) и 2026 (факт до сентября).
var GROUP_DATA = {
connect: { m2025: [52, 55, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76],
m2026: [79, 83, 87, 92, 97, 101, 106, 110, 114] },
mobile: { m2025: [45, 46, 48, 49, 50, 52, 53, 55, 56, 58, 60, 61],
m2026: [64, 66, 69, 73, 77, 80, 84, 87, 89] },
devices: { m2025: [38, 40, 43, 44, 42, 46, 48, 49, 51, 53, 52, 50],
m2026: [63, 66, 71, 68, 74, 78, 82, 79, 85] },
content: { m2025: [31, 32, 34, 35, 36, 38, 39, 40, 41, 43, 44, 45],
m2026: [51, 54, 57, 61, 65, 68, 73, 76, 80] },
extras: { m2025: [27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38],
m2026: [44, 47, 50, 53, 57, 60, 64, 67, 71] }
};
var PERIODS = [
{ id: "9m", label: "Янв–Сен 2026", months: 9, yoy: true },
{ id: "q1", label: "I квартал", months: 3, yoy: true },
{ id: "q2", label: "II квартал", months: 6, yoy: true },
{ id: "m", label: "По месяцам", months: 9, yoy: false }
];
var STATE_KEY = "dash-retail-v1";
// ---------- Утилиты ----------
function fmtInt(n) {
var s = String(Math.round(n));
var out = "";
while (s.length > 3) { out = "." + s.slice(-3) + out; s = s.slice(0, -3); }
return s.length ? s + out : "0" + out;
}
function fmtMoney(mn) { return fmtInt(mn) + " млн тг"; }
function fmtPct(p) {
var sign = p > 0 ? "+" : "";
var num = (Math.abs(p)).toFixed(1).replace(".", ",");
return sign + num + "%";
}
function sum(arr) {
var t = 0;
for (var i = 0; i < arr.length; i++) t += arr[i];
return t;
}
function sumRange(arr, from, len) {
var t = 0;
for (var i = from; i < from + len && i < arr.length; i++) t += arr[i];
return t;
}
function stateFromJson(s) {
var st = {};
for (var k in s) st[k] = s[k];
return st;
}
function loadState() {
var def = { screen: "overview", period: "9m", group: "all" };
try {
var raw = localStorage.getItem(STATE_KEY);
if (!raw) return def;
var parsed = JSON.parse(raw);
return stateFromJson(parsed) == null ? def : Object.assign(def, parsed);
} catch (e) { return def; }
}
function saveState() {
try { localStorage.setItem(STATE_KEY, JSON.stringify(state)); } catch (e) {}
}
var state = loadState();
function periodDef(id) {
for (var i = 0; i < PERIODS.length; i++) if (PERIODS[i].id === id) return PERIODS[i];
return PERIODS[0];
}
function filteredGroups() {
var all = GROUP_DEFS;
if (state.group !== "all") {
all = [];
for (var i = 0; i < GROUP_DEFS.length; i++) {
if (GROUP_DEFS[i].id === state.group) all.push(GROUP_DEFS[i]);
}
}
return all;
}
// ---------- Рендер фильтров ----------
function filterBarHtml() {
var perSel = "";
for (var i = 0; i < PERIODS.length; i++) {
var sel = state.period === PERIODS[i].id ? " selected" : "";
perSel += "<option value=\"" + PERIODS[i].id + "\"" + sel + ">" + PERIODS[i].label + "</option>";
}
var gSel = "<option value=\"all\"" + (state.group === "all" ? " selected" : "") + ">Все группы</option>";
for (var j = 0; j < GROUP_DEFS.length; j++) {
var g = GROUP_DEFS[j];
gSel += "<option value=\"" + g.id + "\"" + (state.group === g.id ? " selected" : "") + ">" + g.name + "</option>";
}
return '<div class="tbl-toolbar">' +
'<label class="kt-ai-label" for="f-period">Период</label>' +
'<span class="kt-ai-select-wrap" style="min-width:150px"><select id="f-period" class="kt-ai-select" onchange="setPeriod(this.value)">' + perSel + '</select></span>' +
'<label class="kt-ai-label" for="f-group">Группа</label>' +
'<span class="kt-ai-select-wrap" style="min-width:170px"><select id="f-group" class="kt-ai-select" onchange="setGroup(this.value)">' + gSel + '</select></span>' +
'<span class="spacer"></span>' +
'<span class="kt-ai-chip">2026</span>' +
'</div>';
}
function setPeriod(id) {
state.period = id; saveState(); renderAll();
}
function setGroup(id) {
state.group = id; saveState(); renderAll();
}
// ---------- Графики ----------
function chartCard(title, meta, body, note) {
var head = '<div class="cc-head"><div><div class="cc-title">' + title + "</div>" +
(meta ? '<div class="kt-ai-meta" style="margin-top:2px">' + meta + "</div>" : "") + "</div></div>";
var foot = note ? '<div class="kt-ai-meta" style="margin-top:var(--kt-ai-space-4)">' + note + "</div>" : "";
return '<section class="kt-ai-chart-card">' + head + body + foot + "</section>";
}
function groupedBarHtml(pairs, tipRows) {
var max = 0;
for (var i = 0; i < pairs.length; i++) {
for (var j = 1; j < pairs[i].vals.length; j++) {
if (pairs[i].vals[j] > max) max = pairs[i].vals[j];
}
}
if (max < 1) max = 1;
var bars = "";
for (var k = 0; k < pairs.length; k++) {
var p = pairs[k];
var inner = "";
for (var m = 0; m < p.vals.length; m++) {
var h = Math.max(2, Math.round(p.vals[m] / max * 190));
var tip = tipRows[p.tipKey || "" + k];
inner += '<span class="grp-bar" style="height:' + h + 'px;background:' + p.colors[m] + '"' +
(tip ? ' data-kt-tip=\'' + tip + '\'' : "") + "></span>";
}
bars += '<div class="grp-col" data-kt-tip=\'' + (tipRows[p.tipKey || "" + k] || "") + '\'><span class="grp-bars">' + inner + "</span>" +
'<span class="grp-label">' + p.label + "</span></div>";
}
return '<div class="grp-wrap">' + bars + "</div>" +
'<div class="grp-legend"><span><i style="background:var(--kt-ai-chart-bar-fill)"></i>2025 (сравнение)</span>' +
'<span><i style="background:var(--kt-ai-chart-blue)"></i>2026 (факт)</span></div>';
}
function lineChartHtml(points) {
var W = 520, H = 200, padX = 10, padT = 14, padB = 26, innerH = H - padT - padB;
var max = 0, min = Infinity;
for (var i = 0; i < points.length; i++) {
if (points[i].v > max) max = points[i].v;
if (points[i].v < min) min = points[i].v;
}
if (min <= 0) min = 0;
var top = max * 1.06, bot = Math.max(0, min - (max - min) * 0.35);
var n = points.length;
function X(i) { return n === 1 ? W / 2 : padX + i * (W - 2 * padX) / (n - 1); }
function Y(v) { return padT + (1 - (v - bot) / (top - bot || 1)) * innerH; }
var path = "", dots = "", labels = "";
for (var k = 0; k < n; k++) {
path += (k ? "L" : "M") + X(k).toFixed(1) + " " + Y(points[k].v).toFixed(1);
dots += '<circle cx="' + X(k).toFixed(1) + '" cy="' + Y(points[k].v).toFixed(1) + '" r="3.2" fill="var(--kt-ai-chart-blue)"' +
' data-kt-tip=\'' + points[k].tip + "'></circle>";
labels += '<text class="lc-lbl" x="' + X(k).toFixed(1) + '" y="' + (H - 8) + '">' + points[k].label + "</text>";
}
var grid = "";
for (var g = 0; g < 4; g++) {
var gy = padT + g * innerH / 3;
var gv = top - g * (top - bot) / 3;
grid += '<line x1="' + padX + '" y1="' + gy.toFixed(1) + '" x2="' + (W - padX) + '" y2="' + gy.toFixed(1) + '" stroke="var(--kt-ai-divider)"/>' +
'<text class="lc-axis" x="0" y="' + (gy - 3).toFixed(1) + '">' + fmtInt(gv) + "</text>";
}
return '<div class="lc-wrap"><svg class="lc-svg" viewBox="0 0 ' + W + " " + H + '" preserveAspectRatio="xMidYMid meet">' +
grid + '<path class="lc-line" d="' + path + '"/>' + dots + labels + "</svg></div>";
}
function barRowHtml(label, value, maxV, color, tip) {
var w = Math.max(2, Math.round(value / maxV * 100));
return '<div class="rowbar" data-kt-tip=\'' + tip + '\'><span class="rowbar-label">' + label + "</span>" +
'<span class="rowbar-track"><span class="rowbar-fill" style="width:' + w + "%;background:" + color + '"></span></span>' +
'<span class="rowbar-num kt-ai-num">' + fmtInt(value) + "</span></div>";
}
function shareOf(id, months2026) {
var num = 0, den = 0;
for (var m = 0; m < months2026; m++) { num += GROUP_DATA[id].m2026[m]; }
for (var k = 0; k < GROUP_DEFS.length; k++) { den += sumRange(GROUP_DATA[GROUP_DEFS[k].id].m2026, 0, months2026); }
return den ? num / den * 100 : 0;
}
function topMonthOf(id, from, len) {
var best = 0, idx = from;
for (var m = from; m < from + len; m++) {
if (GROUP_DATA[id].m2026[m] > best) { best = GROUP_DATA[id].m2026[m]; idx = m; }
}
return { month: idx, value: best };
}
function groupTotal(id) {
return sum(GROUP_DATA[id].m2026) + sum(GROUP_DATA[id].m2025);
}
function yoyFor(id, from, len) {
var cur = sumRange(GROUP_DATA[id].m2026, from, len);
var prev = sumRange(GROUP_DATA[id].m2025, from, len);
return prev ? (cur - prev) / prev * 100 : 0;
}
function overviewKpis() {
var p = periodDef(state.period);
var cur = 0, prev = 0;
var groups = filteredGroups();
for (var i = 0; i < groups.length; i++) {
cur += sumRange(GROUP_DATA[groups[i].id].m2026, 0, p.months);
prev += sumRange(GROUP_DATA[groups[i].id].m2025, 0, p.months);
}
var yoy = prev ? (cur - prev) / prev * 100 : 0;
var topId = "all", topV = -1;
for (var j = 0; j < GROUP_DEFS.length; j++) {
var v = sumRange(GROUP_DATA[GROUP_DEFS[j].id].m2026, 0, p.months);
if (v > topV) { topV = v; topId = GROUP_DEFS[j].id; }
}
var topName = "";
for (var k = 0; k < GROUP_DEFS.length; k++) if (GROUP_DEFS[k].id === topId) topName = GROUP_DEFS[k].name;
var avg = cur / p.months;
return [
{ label: "Объём продаж", value: fmtInt(cur), unit: " млн тг", hint: p.label },
{ label: "К уровню 2025", value: fmtPct(yoy), unit: "", hint: "за период" },
{ label: "Средний месяц", value: fmtInt(avg), unit: " млн тг", hint: "за " + p.months + " мес." },
{ label: "Лучшая группа", value: topName, unit: "", hint: fmtInt(topV) + " млн тг" }
];
}
function renderOverview() {
var p = periodDef(state.period);
var groups = filteredGroups();
var groupsLabel = state.group === "all" ? "все группы" : groups[0].name.toLowerCase();
var h = "";
h += '<div class="screen-head"><h1>Обзор</h1>' +
'<span class="screen-sub">Розничные продажи 2026 · ' + p.label + " · " + groupsLabel + "</span></div>";
h += filterBarHtml();
var kpis = overviewKpis();
h += '<div class="kt-ai-kpi-strip" data-cards="true" id="kpis">';
for (var i = 0; i < kpis.length; i++) {
var k = kpis[i];
var long = String(k.value).length > 9 ? " data-long=\"true\"" : "";
h += '<div class="kt-ai-kpi"><span class="label">' + k.label + '</span>' +
'<span class="value"' + long + ">" + k.value + (k.unit ? '<span class="kt-ai-meta" style="font-weight:400">' + k.unit + "</span>" : "") + "</span>" +
'<span class="hint">' + k.hint + "</span></div>";
}
h += "</div>";
h += '<div class="chart-row">';
if (p.yoy) {
var pairs = [];
var tips = {};
for (var m = 0; m < p.months; m++) {
var curV = 0, prevV = 0;
for (var gg = 0; gg < groups.length; gg++) {
curV += GROUP_DATA[groups[gg].id].m2026[m];
prevV += GROUP_DATA[groups[gg].id].m2025[m];
}
pairs.push({ label: MONTHS_SHORT[m], vals: [prevV, curV], colors: ["var(--kt-ai-chart-bar-fill)", "var(--kt-ai-chart-blue)"], tipKey: "" + m });
tips["" + m] = MONTHS[m] + ": 2026 — " + fmtInt(curV) + " млн тг; 2025 — " + fmtInt(prevV) + " млн тг" + (prevV ? " (" + fmtPct((curV - prevV) / prevV * 100) + ")" : "");
}
h += chartCard("Динамика за месяц", "2026 против 2025, млн тг", groupedBarHtml(pairs, tips));
} else {
var pts = [];
for (var mm = 0; mm < p.months; mm++) {
var mv = 0;
for (var gg = 0; gg < groups.length; gg++) mv += GROUP_DATA[groups[gg].id].m2026[mm];
var yoyv = 0;
for (var gg2 = 0; gg2 < groups.length; gg2++) yoyv += GROUP_DATA[groups[gg2].id].m2025[mm];
var yy = yoyv ? (mv - yoyv) / yoyv * 100 : 0;
pts.push({ label: MONTHS_SHORT[mm], v: mv, tip: MONTHS[mm] + ": " + fmtInt(mv) + " млн тг, к 2025 " + (yoyv ? fmtPct(yy) : "") });
}
h += chartCard("Продажи по месяцам", "млн тг, 2026", lineChartHtml(pts));
}
if (state.group === "all") {
var donPts = [];
for (var d = 0; d < GROUP_DEFS.length; d++) {
var gd = GROUP_DEFS[d];
donPts.push({ label: gd.name, value: sumRange(GROUP_DATA[gd.id].m2026, 0, p.months), color: gd.base });
}
var donTotal = 0;
for (var d2 = 0; d2 < donPts.length; d2++) donTotal += donPts[d2].value;
h += chartCard("Структура продаж", p.label + ", млн тг", donutHtml(donTotal, donPts));
} else {
var rows = [];
for (var r = 0; r < GROUP_DEFS.length; r++) {
var gr = GROUP_DEFS[r];
rows.push({ id: gr.id, label: gr.name, value: sumRange(GROUP_DATA[gr.id].m2026, 0, p.months), color: gr.base });
}
var rmax = 0;
for (var r2 = 0; r2 < rows.length; r2++) if (rows[r2].value > rmax) rmax = rows[r2].value;
var body = "";
for (var r3 = 0; r3 < rows.length; r3++) {
var rr = rows[r3];
var selMark = rr.id === state.group ? " · выбрано" : "";
body += barRowHtml(rr.label + selMark, rr.value, rmax, rr.id === state.group ? rr.color : "var(--kt-ai-chart-bar-fill)", rr.label + ": " + fmtInt(rr.value) + " млн тг");
}
h += chartCard("Сравнение групп", p.label + ", млн тг", body);
}
h += "</div>";
var t = Math.round(Date.now() / 60000) * 60000;
h += '<section class="kt-ai-conclusion" style="margin-top:var(--kt-ai-stack-block);padding:var(--kt-ai-space-5) var(--kt-ai-space-6)">' +
'<span class="title">Итог за период</span>' +
'<span class="text">За ' + p.label.toLowerCase() + " продано на " + fmtInt(kpis[0].value.replace(/[^\d.]/g, "")) + " млн тг (" + groupsLabel + "). Лучшие результаты — " + kpis[3].value.toLowerCase() + "; к уровню 2025 года показатель " + (String(kpis[1].value).indexOf("+") === 0 ? "выше" : "ниже") + " на " + pctNum(kpis[1].value).toFixed(1).replace(".", ",") + " п.д.</span>" +
'<span class="kt-ai-meta" style="margin-left:auto;white-space:nowrap">Обновлено сегодня в ' + new Date(t).getHours() + ":" + String(new Date(t).getMinutes()).padStart(2, "0") + "</span>" +
"</section>";
return h;
}
function pctNum(v) {
return Math.abs(parseFloat(String(v).replace(",", ".")));
}
function groupName(id) {
for (var i = 0; i < GROUP_DEFS.length; i++) if (GROUP_DEFS[i].id === id) return GROUP_DEFS[i].name;
return id;
}
function renderDetails() {
var p = periodDef(state.period);
var h = "";
h += '<div class="screen-head"><h1>Детали</h1>' +
'<span class="screen-sub">Разбивка 2026 по периодам и товарным группам</span></div>';
h += filterBarHtml();
// Таблица по месяцам
h += '<section class="detail-section"><h2 class="kt-ai-h3 block-title">Продажи по месяцам</h2>';
var gIds = [];
for (var i = 0; i < GROUP_DEFS.length; i++) gIds.push(GROUP_DEFS[i].id);
gIds.sort(function (a, b) { return groupTotal(b) - groupTotal(a); });
var headCols = "<th>Месяц</th>";
for (var c = 0; c < gIds.length; c++) {
headCols += "<th>" + groupName(gIds[c]) + "</th>";
}
headCols += "<th>Итого</th><th>К 2025</th>";
var body = "";
var colTot = {};
for (var c3 = 0; c3 < gIds.length; c3++) colTot[gIds[c3]] = 0;
var grand = 0, grandPrev = 0;
for (var m = 0; m < p.months; m++) {
body += "<tr><td><strong>" + MONTHS[m] + "</strong></td>";
var rowTot = 0, rowPrev = 0;
for (var c4 = 0; c4 < gIds.length; c4++) {
var v = GROUP_DATA[gIds[c4]].m2026[m];
var pv = GROUP_DATA[gIds[c4]].m2025[m];
colTot[gIds[c4]] += v; rowTot += v; rowPrev += pv;
body += '<td class="num" style="padding-right:18px">' + fmtInt(v) + "</td>";
}
grand += rowTot; grandPrev += rowPrev;
body += '<td class="num"><strong>' + fmtInt(rowTot) + "</strong></td>" +
'<td class="num">' + (rowPrev ? fmtPct((rowTot - rowPrev) / rowPrev * 100) : "—") + "</td></tr>";
}
body += '<tr><td><strong>Итого</strong></td>';
for (var c5 = 0; c5 < gIds.length; c5++) body += '<td class="num" style="padding-right:18px"><strong>' + fmtInt(colTot[gIds[c5]]) + "</strong></td>";
body += '<td class="num"><strong>' + fmtInt(grand) + "</strong></td>" +
'<td class="num"><strong>' + (grandPrev ? fmtPct((grand - grandPrev) / grandPrev * 100) : "—") + "</strong></td></tr>";
var foot = "Значения в млн тг. «К 2025» — прирост того же месяца к прошлому году.";
h += '<div class="kt-ai-table-wrap"><table class="kt-ai-table"><thead><tr>' + headCols + "</tr></thead><tbody>" + body + "</tbody></table></div>" +
'<p class="kt-ai-meta" style="margin:var(--kt-ai-space-3) 0 0">' + foot + "</p></section>";
// Разбивка по группам
h += '<section class="detail-section"><h2 class="kt-ai-h3 block-title">Разбивка по товарным группам</h2>';
if (state.group === "all") {
h += '<div class="kt-ai-table-wrap"><table class="kt-ai-table"><thead><tr>' +
"<th>Товарная группа</th><th>Объём</th><th>Место</th><th>Доля</th><th>Лучший месяц</th><th>К 2025</th>" +
"</tr></thead><tbody>";
for (var g = 0; g < gIds.length; g++) {
var gd = null;
for (var g2 = 0; g2 < GROUP_DEFS.length; g2++) if (GROUP_DEFS[g2].id === gIds[g]) gd = GROUP_DEFS[g2];
var tot = sumRange(GROUP_DATA[gd.id].m2026, 0, p.months);
var share = shareOf(gd.id, p.months);
var tm = topMonthOf(gd.id, 0, p.months);
var yoy = yoyFor(gd.id, 0, p.months);
h += '<tr><td><strong>' + gd.name + '</strong></td><td class="num" data-kt-tip="' + gd.name + ": " + fmtInt(tot) + " млн тг за " + p.label.toLowerCase() + '">' + fmtInt(tot) + " <span class=\"kt-ai-meta\">млн тг</span></td>" +
'<td class="num">' + (g + 1) + "</td><td class=\"num\">" + Math.round(share) + "%</td>" +
"<td>" + MONTHS[tm.month] + " · " + fmtInt(tm.value) + '</td><td class="num">' + fmtPct(yoy) + "</td></tr>";
}
h += "</tbody></table></div>";
} else {
h += '<div class="kt-ai-table-wrap"><table class="kt-ai-table"><thead><tr>' +
"<th>Месяц</th><th>Объём</th><th>Доля в группе</th><th>К 2025</th>" +
"</tr></thead><tbody>";
var gTot = 0;
for (var m2 = 0; m2 < p.months; m2++) gTot += GROUP_DATA[state.group].m2026[m2];
for (var m3 = 0; m3 < p.months; m3++) {
var mvv = GROUP_DATA[state.group].m2026[m3];
var pvv = GROUP_DATA[state.group].m2025[m3];
var sh = gTot ? mvv / gTot * 100 : 0;
h += '<tr><td>' + MONTHS[m3] + '</td><td class="num" data-kt-tip="' + MONTHS[m3] + ": " + fmtInt(mvv) + " млн тг\">" + fmtInt(mvv) + ' <span class="kt-ai-meta">млн тг</span></td>' +
'<td class="num">' + sh.toFixed(1).replace(".", ",") + "%</td>" +
'<td class="num">' + (pvv ? fmtPct((mvv - pvv) / pvv * 100) : "—") + "</td></tr>";
}
h += '<tr><td><strong>Итого</strong></td><td class="num"><strong>' + fmtInt(gTot) + ' <span class="kt-ai-meta">млн тг</span></strong></td><td class="num">100%</td><td class="num"><strong>' + fmtPct(yoyFor(state.group, 0, p.months)) + "</strong></td></tr></tbody></table></div>";
}
h += "</section>";
return h;
}
function donutHtml(total, pts) {
var acc = 0;
var segs = "";
for (var i = 0; i < pts.length; i++) {
var p = pts[i];
if (p.value <= 0) continue;
var share = p.value / total * 100;
segs += '<circle cx="21" cy="21" r="15.9155" fill="none" stroke="' + p.color + '" stroke-width="5.5" pathLength="100" ' +
'stroke-dasharray="' + share.toFixed(2) + " " + (100 - share).toFixed(2) + '" stroke-dashoffset="' + (25 - acc).toFixed(2) + '"' +
' data-kt-tip=\'' + p.label + ': ' + fmtInt(p.value) + ' млн тг, ' + Math.round(share) + "%" + "'></circle>";
acc += share;
}
var legend = "";
for (var j = 0; j < pts.length; j++) {
var q = pts[j];
if (q.value <= 0) continue;
var pc = Math.round(q.value / total * 100);
legend += '<div class="dl-row"><span class="dl-dot" style="background:' + q.color + '"></span>' +
'<span class="dl-label">' + q.label + '</span><span class="dl-num kt-ai-num">' + pc + "%</span></div>";
}
var svg = '<svg class="dl-svg" viewBox="0 0 42 42" role="img"><circle cx="21" cy="21" r="15.9155" fill="none" stroke="var(--kt-ai-bg-soft)" stroke-width="5.5"/>' +
segs + '<text class="dl-center" x="21" y="20.5">' + fmtInt(total) + '</text><text class="dl-unit" x="21" y="26.5">млн тг</text></svg>';
return '<div class="dl-wrap">' + svg + '<div class="dl-legend">' + legend + "</div></div>";
}
function renderAll() {
applyScreen();
}
function applyScreen() {
var ov = document.getElementById("screen-overview");
var dt = document.getElementById("screen-details");
if (state.screen === "details") {
if (ov) ov.hidden = true;
if (dt) { dt.hidden = false; dt.innerHTML = renderDetails(); }
} else {
if (ov) { ov.hidden = false; ov.innerHTML = renderOverview(); }
if (dt) dt.hidden = true;
}
var items = document.querySelectorAll('.kt-ai-nav-item[data-screen]');
for (var i = 0; i < items.length; i++) {
items[i].setAttribute("data-active", items[i].getAttribute("data-screen") === state.screen ? "true" : "false");
}
}
function switchScreen(id) {
state.screen = id; saveState(); renderAll();
}
renderAll();