413 lines
19 KiB
JavaScript
413 lines
19 KiB
JavaScript
// Дашборд розничных продаж 2026: данные, экраны, фильтры.
|
||
var MONTHS = ["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь"];
|
||
var MONTHS_SHORT = ["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен"];
|
||
var GROUPS = ["Связь","Интернет","ТВ","Устройства","Услуги"];
|
||
var GROUP_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-salmon)"];
|
||
// данные[группа][месяц] = { revenue (млн ₸), orders (шт) }
|
||
var DATA = {
|
||
"Связь": [ {revenue:312.4,orders:14820},{revenue:305.1,orders:14410},{revenue:328.9,orders:15260},{revenue:341.2,orders:15780},{revenue:356.8,orders:16340},{revenue:362.4,orders:16520},{revenue:359.0,orders:16390},{revenue:371.5,orders:16980},{revenue:384.7,orders:17540} ],
|
||
"Интернет": [ {revenue:201.3,orders:8340},{revenue:198.7,orders:8210},{revenue:210.6,orders:8720},{revenue:224.8,orders:9230},{revenue:237.1,orders:9780},{revenue:245.9,orders:10150},{revenue:243.2,orders:10020},{revenue:254.6,orders:10480},{revenue:268.3,orders:11020} ],
|
||
"ТВ": [ {revenue:118.6,orders:5120},{revenue:115.2,orders:4980},{revenue:121.9,orders:5260},{revenue:128.4,orders:5510},{revenue:133.7,orders:5730},{revenue:139.2,orders:5940},{revenue:137.5,orders:5860},{revenue:142.8,orders:6070},{revenue:149.6,orders:6340} ],
|
||
"Устройства": [ {revenue:87.2,orders:2740},{revenue:84.9,orders:2660},{revenue:91.3,orders:2870},{revenue:96.7,orders:3050},{revenue:102.4,orders:3240},{revenue:108.1,orders:3420},{revenue:106.8,orders:3370},{revenue:112.5,orders:3550},{revenue:118.9,orders:3760} ],
|
||
"Услуги": [ {revenue:56.4,orders:4310},{revenue:54.8,orders:4210},{revenue:58.2,orders:4440},{revenue:61.9,orders:4680},{revenue:65.3,orders:4890},{revenue:67.8,orders:5060},{revenue:66.9,orders:5000},{revenue:70.2,orders:5210},{revenue:74.5,orders:5480} ]
|
||
};
|
||
|
||
var state = { screen: "overview", period: "year", metric: "revenue", mixMonth: "year", search: "", sort: { key: "label", dir: 1 }, detailPeriod: "year", detailGroup: "all", mixFilter: "all" };
|
||
var ui = { sortDetail: { key: "revenue", dir: -1 } };
|
||
|
||
var $ = function (sel, ctx) { return (ctx || document).querySelector(sel); };
|
||
var $$ = function (sel, ctx) { return Array.prototype.slice.call((ctx || document).querySelectorAll(sel)); };
|
||
function byId(id) { return document.getElementById(id); }
|
||
function el(name, attrs, children) {
|
||
var node = document.createElement(name);
|
||
if (attrs) for (var k in attrs) if (node.setAttribute) { if (k === "text") node.textContent = attrs[k]; else node.setAttribute(k, attrs[k]); }
|
||
if (children) children.forEach(function (c) { if (c) node.appendChild(typeof c === "string" ? document.createTextNode(c) : c); });
|
||
return node;
|
||
}
|
||
function fmt(n, dec) {
|
||
var s = Number(n).toLocaleString("ru-RU", { minimumFractionDigits: dec == null ? 1 : dec, maximumFractionDigits: dec == null ? 1 : dec });
|
||
return s;
|
||
}
|
||
function fmtInt(n) { return Number(n).toLocaleString("ru-RU"); }
|
||
function pct(n) { return ((Number(n) * 100)).toFixed(1).replace(".", ",") + " %"; }
|
||
|
||
// ---------- сборка месяцев ----------
|
||
var cacheMonths = null;
|
||
function monthRows() {
|
||
if (cacheMonths) return cacheMonths;
|
||
cacheMonths = [];
|
||
for (var i = 0; i < MONTHS.length; i++) {
|
||
var rev = 0, ord = 0;
|
||
GROUPS.forEach(function (g) { rev += DATA[g][i].revenue; ord += DATA[g][i].orders; });
|
||
cacheMonths.push({ idx: i, label: MONTHS[i], rev: rev, ord: ord, avg: rev * 1000000 / ord });
|
||
}
|
||
return cacheMonths;
|
||
}
|
||
function sumRange(idx) {
|
||
var rows = monthRows().slice(idx[0], idx[1] + 1);
|
||
var rev = 0, ord = 0;
|
||
rows.forEach(function (r) { rev += r.rev; ord += r.ord; });
|
||
return { rev: rev, ord: ord };
|
||
}
|
||
function quarterIndex(q) { var s = (q - 1) * 3; return [s, s + 2]; }
|
||
function periodRange(period) {
|
||
if (period === "year") return [0, 8];
|
||
if (period === "q1") return quarterIndex(1);
|
||
if (period === "q2") return quarterIndex(2);
|
||
if (period === "q3") return quarterIndex(3);
|
||
return [8, 8];
|
||
}
|
||
function periodLabel(period) {
|
||
if (period === "year") return "Янв — Сен 2026";
|
||
if (period === "q1") return "Январь — Март 2026";
|
||
if (period === "q2") return "Апрель — Июнь 2026";
|
||
if (period === "q3") return "Июль — Сентябрь 2026";
|
||
if (period === "sep") return "Сентябрь 2026";
|
||
return "Сентябрь 2026";
|
||
}
|
||
|
||
// ---------- KPI ----------
|
||
function renderKpis(id) {
|
||
var range = periodRange(state.period);
|
||
var cur = sumRange(range);
|
||
var growth = prevMonthGrowth();
|
||
byId(id).innerHTML = "";
|
||
byId(id).appendChild(kpi(fmt(cur.rev), "Выручка, млн ₸", periodLabel(state.period)));
|
||
byId(id).appendChild(kpi(fmtInt(cur.ord), "Заказы", periodLabel(state.period)));
|
||
byId(id).appendChild(kpi(fmt(cur.rev * 1000000 / cur.ord / 1000, 1) + " т. ₸", "Средний чек", "за период"));
|
||
byId(id).appendChild(kpi((growth >= 0 ? "+" : "") + fmt(growth / 100, 1) + " %", "К пред. месяцу", "Сентябрь к Августу"));
|
||
}
|
||
function prevMonthGrowth() {
|
||
var rows = monthRows();
|
||
var a = rows[rows.length - 1], b = rows[rows.length - 2];
|
||
return (a.rev - b.rev) / b.rev * 100;
|
||
}
|
||
function kpi(value, label, hint) {
|
||
return el("div", { "class": "kt-ai-kpi" }, [
|
||
el("div", { "class": "value", text: value }),
|
||
el("div", { "class": "label", text: label }),
|
||
el("div", { "class": "hint", text: hint })
|
||
]);
|
||
}
|
||
|
||
// ---------- график: динамика ----------
|
||
function renderChartOverview() {
|
||
var chart = byId("chart-overview");
|
||
chart.innerHTML = "";
|
||
var range = periodRange(state.period);
|
||
var idxs = [];
|
||
for (var i = range[0]; i <= range[1]; i++) idxs.push(i);
|
||
|
||
var values = idxs.map(function (i) {
|
||
if (state.metric === "revenue") { var s = 0; GROUPS.forEach(function (g) { s += DATA[g][i].revenue; }); return s; }
|
||
if (state.metric === "orders") { var s2 = 0; GROUPS.forEach(function (g) { s2 += DATA[g][i].orders; }); return s2; }
|
||
return 0;
|
||
});
|
||
var max = Math.max.apply(null, values);
|
||
values.forEach(function (v, j) {
|
||
var i = idxs[j];
|
||
var h = Math.max(4, Math.round(v / max * 100));
|
||
var bar = el("div", { "class": "cc-bar", style: "height:" + h + "%" });
|
||
if (state.metric === "orders") bar.setAttribute("data-series", "green");
|
||
bar.setAttribute("data-kt-tip", JSON.stringify({ title: MONTHS[i] + " 2026", rows: [[ "Выручка", fmt(monthRows()[i].rev) + " млн ₸" ], [ "Заказы", fmtInt(monthRows()[i].ord) ]] }));
|
||
var col = el("div", { "class": "cc-col" }, [bar, el("div", { "class": "cc-label", text: MONTHS_SHORT[i] })]);
|
||
chart.appendChild(col);
|
||
});
|
||
|
||
var legend = byId("legend-overview");
|
||
legend.innerHTML = "";
|
||
legend.appendChild(legendItem(state.metric === "revenue" ? "Выручка" : "Заказы", state.metric === "revenue" ? "Var(--kt-ai-chart-blue)" : "var(--kt-ai-chart-green)"));
|
||
}
|
||
function legendItem(name, color) {
|
||
var item = el("span", { "class": "kt-ai-legend-item" }, [
|
||
el("span", { "class": "kt-ai-legend-swatch", style: "background:" + color }),
|
||
document.createTextNode(name)
|
||
]);
|
||
return item;
|
||
}
|
||
|
||
// ---------- структура: mix ----------
|
||
function renderMix() {
|
||
var range = periodRange(state.period);
|
||
var allTotals = GROUPS.map(function (g) { var s = 0; for (var i = range[0]; i <= range[1]; i++) s += DATA[g][i].revenue; return s; });
|
||
var allSum = allTotals.reduce(function (a, b) { return a + b; }, 0);
|
||
|
||
var chips = byId("mix-chips");
|
||
chips.innerHTML = "";
|
||
var chipAll = el("button", { "class": "kt-ai-filter-chip", type: "button", text: "Все" });
|
||
if (state.mixFilter === "all") chipAll.setAttribute("data-active", "true");
|
||
chipAll.addEventListener("click", function () { state.mixFilter = "all"; renderMix(); });
|
||
chips.appendChild(chipAll);
|
||
GROUPS.forEach(function (g, k) {
|
||
var b = el("button", { "class": "kt-ai-filter-chip", type: "button", text: g });
|
||
if (state.mixFilter === g) b.setAttribute("data-active", "true");
|
||
b.addEventListener("click", function () { state.mixFilter = g; renderMix(); });
|
||
chips.appendChild(b);
|
||
});
|
||
|
||
var visible = state.mixFilter === "all" ? GROUPS : [state.mixFilter];
|
||
var totals = {};
|
||
visible.forEach(function (g) {
|
||
var k = GROUPS.indexOf(g);
|
||
totals[g] = { v: allTotals[k], c: GROUP_COLORS[k] };
|
||
});
|
||
|
||
var bars = byId("mix-bars");
|
||
bars.innerHTML = "";
|
||
var sorted = visible.map(function (g) { return { g: g, v: totals[g].v, c: totals[g].c }; })
|
||
.sort(function (a, b) { return b.v - a.v; });
|
||
var vSum = sorted.reduce(function (a, r) { return a + r.v; }, 0);
|
||
sorted.forEach(function (row) {
|
||
var share = vSum ? row.v / vSum * 100 : 0;
|
||
bars.appendChild(el("div", { "class": "kt-ai-mix-row" }, [
|
||
el("div", { "class": "kt-ai-mix-name", text: row.g }),
|
||
el("div", { "class": "kt-ai-mix-track" }, [el("div", { "class": "kt-ai-mix-fill", style: "width:" + share + "%;background:" + row.c })]),
|
||
el("div", { "class": "kt-ai-mix-pct", text: share.toFixed(1).replace(".", ",") + " %" })
|
||
]));
|
||
});
|
||
|
||
var tbody = $("#table-mix tbody");
|
||
tbody.innerHTML = "";
|
||
sorted.forEach(function (row) {
|
||
var share = vSum ? row.v / vSum * 100 : 0;
|
||
tbody.appendChild(el("tr", null, [
|
||
el("td", null, [el("span", { "class": "kt-ai-legend-swatch", style: "display:inline-block;width:8px;height:8px;margin-right:6px;background:" + row.c + ";border-radius:2px;vertical-align:middle" }), document.createTextNode(row.g)]),
|
||
el("td", { "class": "rt", text: fmt(row.v) }),
|
||
el("td", { "class": "rt", text: share.toFixed(1).replace(".", ",") + " %" })
|
||
]));
|
||
});
|
||
}
|
||
|
||
// ---------- детали: KPI ----------
|
||
function renderKpisDetail() {
|
||
var range = periodRange(state.detailPeriod);
|
||
var cur = sumRange(range);
|
||
var groupsData = GROUPS.map(function (g) {
|
||
var rev = 0, ord = 0;
|
||
for (var i = range[0]; i <= range[1]; i++) { rev += DATA[g][i].revenue; ord += DATA[g][i].orders; }
|
||
return { g: g, rev: rev, ord: ord };
|
||
});
|
||
var top = groupsData.slice().sort(function (a, b) { return b.rev - a.rev; })[0];
|
||
byId("kpi-detail").innerHTML = "";
|
||
byId("kpi-detail").appendChild(kpi(fmt(cur.rev), "Выручка, млн ₸", periodLabel(state.detailPeriod)));
|
||
byId("kpi-detail").appendChild(kpi(fmtInt(cur.ord), "Заказы", periodLabel(state.detailPeriod)));
|
||
byId("kpi-detail").appendChild(kpi(top.g, "Лидер по выручке", fmt(top.rev) + " млн ₸ · " + (top.rev / cur.rev * 100).toFixed(1).replace(".", ",") + " %"));
|
||
byId("kpi-detail").appendChild(kpi(fmt(cur.rev * 1000000 / cur.ord / 1000, 1) + " т. ₸", "Средний чек", "за период"));
|
||
}
|
||
|
||
// ---------- детали: таблица месяцев ----------
|
||
function renderMonths() {
|
||
var rows = monthRows();
|
||
var q = state.search.trim().toLowerCase();
|
||
var shown = rows.filter(function (r) { return !q || r.label.toLowerCase().indexOf(q) >= 0; });
|
||
|
||
var key = state.sort.key, dir = state.sort.dir;
|
||
shown.sort(function (a, b) {
|
||
var va = a[key], vb = b[key];
|
||
if (typeof va === "string") return va.localeCompare(vb, "ru") * dir;
|
||
return (va - vb) * dir;
|
||
});
|
||
|
||
var tbody = $("#table-months tbody");
|
||
tbody.innerHTML = "";
|
||
shown.forEach(function (r, i) {
|
||
var prev = i > 0 ? shown[i - 1] : null;
|
||
var mom = prev ? (r.rev - prev.rev) / prev.rev * 100 : null;
|
||
var momCell;
|
||
if (mom == null) momCell = el("span", { "class": "kt-ai-meta", text: "—" });
|
||
else momCell = el("span", { "class": "kt-ai-status-pill", "data-status": mom >= 0 ? "ok" : "risk", text: (mom >= 0 ? "+" : "") + mom.toFixed(1).replace(".", ",") + "%" });
|
||
tbody.appendChild(el("tr", { "class": "kt-ai-row-hover" }, [
|
||
el("td", { text: r.label }),
|
||
el("td", { "class": "rt", text: fmt(r.rev) }),
|
||
el("td", { "class": "rt", text: fmtInt(r.ord) }),
|
||
el("td", { "class": "rt", text: fmtInt(Math.round(r.avg)) }),
|
||
el("td", { "class": "rt" }, [momCell])
|
||
]));
|
||
});
|
||
|
||
byId("empty-months").hidden = shown.length !== 0;
|
||
byId("table-months").style.display = shown.length ? "" : "none";
|
||
|
||
// aria-sort
|
||
$$("#table-months th.sortable").forEach(function (th) {
|
||
if (th.getAttribute("data-sort") === key) th.setAttribute("aria-sort", dir > 0 ? "ascending" : "descending");
|
||
else th.removeAttribute("aria-sort");
|
||
});
|
||
}
|
||
|
||
// ---------- детали: разбивка ----------
|
||
function renderDetail() {
|
||
var range = periodRange(state.detailPeriod);
|
||
var idxs = [];
|
||
for (var i = range[0]; i <= range[1]; i++) idxs.push(i);
|
||
var groups = state.detailGroup === "all" ? GROUPS : [state.detailGroup];
|
||
var rows = [];
|
||
var total = 0;
|
||
idxs.forEach(function (i) {
|
||
var rev = 0, ord = 0;
|
||
groups.forEach(function (g) { rev += DATA[g][i].revenue; ord += DATA[g][i].orders; });
|
||
rows.push({ label: MONTHS[i], rev: rev, ord: ord, avg: rev * 1000000 / ord });
|
||
total += rev;
|
||
});
|
||
rows.forEach(function (r) { r.share = total ? r.rev / total : 0; });
|
||
|
||
var key = ui.sortDetail.key, dir = ui.sortDetail.dir;
|
||
rows.sort(function (a, b) {
|
||
var va = a[key], vb = b[key];
|
||
if (typeof va === "string") return va.localeCompare(vb, "ru") * dir;
|
||
return (va - vb) * dir;
|
||
});
|
||
|
||
// график: столбцы по периодам
|
||
var chart = byId("chart-detail");
|
||
chart.innerHTML = "";
|
||
var max = Math.max.apply(null, idxs.map(function (i) {
|
||
var s = 0; groups.forEach(function (g) { s += DATA[g][i].revenue; }); return s;
|
||
}));
|
||
idxs.forEach(function (i) {
|
||
var s = 0; groups.forEach(function (g) { s += DATA[g][i].revenue; });
|
||
var h = Math.max(4, Math.round(s / max * 100));
|
||
var bar = el("div", { "class": "cc-bar", style: "height:" + h + "%" });
|
||
bar.setAttribute("data-kt-tip", JSON.stringify({ title: MONTHS[i] + " 2026", rows: [[ "Выручка", fmt(s) + " млн ₸" ]] }));
|
||
var col = el("div", { "class": "cc-col" }, [bar, el("div", { "class": "cc-label", text: MONTHS_SHORT[i] })]);
|
||
chart.appendChild(col);
|
||
});
|
||
|
||
var tbody = $("#table-detail tbody");
|
||
tbody.innerHTML = "";
|
||
rows.forEach(function (r) {
|
||
tbody.appendChild(el("tr", null, [
|
||
el("td", { text: r.label }),
|
||
el("td", { "class": "rt", text: fmt(r.rev) }),
|
||
el("td", { "class": "rt", text: fmtInt(r.ord) }),
|
||
el("td", { "class": "rt", text: fmtInt(Math.round(r.avg)) }),
|
||
el("td", { "class": "rt", text: pct(r.share) })
|
||
]));
|
||
});
|
||
byId("empty-detail").hidden = rows.length !== 0;
|
||
|
||
$$("#table-detail th.sortable").forEach(function (th) {
|
||
if (th.getAttribute("data-sort") === key) th.setAttribute("aria-sort", dir > 0 ? "ascending" : "descending");
|
||
else th.removeAttribute("aria-sort");
|
||
});
|
||
}
|
||
|
||
// ---------- тулбар ----------
|
||
function renderPeriodTabs() {
|
||
var tabs = [
|
||
{ id: "year", label: "Год" },
|
||
{ id: "q1", label: "Q1" },
|
||
{ id: "q2", label: "Q2" },
|
||
{ id: "q3", label: "Q3" }
|
||
];
|
||
var box = byId("period-tabs");
|
||
box.classList.add("kt-ai-segmented");
|
||
box.innerHTML = "";
|
||
tabs.forEach(function (t) {
|
||
var b = el("button", { "class": "seg", type: "button", text: t.label });
|
||
if (state.period === t.id) b.setAttribute("data-active", "true");
|
||
b.addEventListener("click", function () {
|
||
state.period = t.id;
|
||
byId("period-chip").textContent = periodLabel(state.period);
|
||
renderAll();
|
||
});
|
||
box.appendChild(b);
|
||
});
|
||
|
||
var mt = byId("metric-tabs");
|
||
mt.innerHTML = "";
|
||
[["revenue", "Выручка"], ["orders", "Заказы"]].forEach(function (m) {
|
||
var b = el("button", { "class": "seg", type: "button", text: m[1] });
|
||
if (state.metric === m[0]) b.setAttribute("data-active", "true");
|
||
b.addEventListener("click", function () { state.metric = m[0]; renderAll(); });
|
||
mt.appendChild(b);
|
||
});
|
||
|
||
var ms = byId("month-select");
|
||
ms.innerHTML = "";
|
||
[["year", "Янв — Сен"], ["q1", "Q1"], ["q2", "Q2"], ["q3", "Q3"], ["sep", "Сен"]].forEach(function (m) {
|
||
var b = el("button", { "class": "seg", type: "button", text: m[1] });
|
||
if (state.period === m[0]) b.setAttribute("data-active", "true");
|
||
b.addEventListener("click", function () { state.period = m[0]; byId("period-chip").textContent = periodLabel(state.period); renderAll(); });
|
||
ms.appendChild(b);
|
||
});
|
||
|
||
var selP = byId("sel-period");
|
||
selP.innerHTML = "";
|
||
[["year", "Год (Янв — Сен)"], ["q1", "Q1 (Янв — Мар)"], ["q2", "Q2 (Апр — Июн)"], ["q3", "Q3 (Июл — Сен)"]].forEach(function (o) {
|
||
var opt = el("option", { value: o[0], text: o[1] });
|
||
if (state.detailPeriod === o[0]) opt.setAttribute("selected", "");
|
||
selP.appendChild(opt);
|
||
});
|
||
selP.onchange = function () { state.detailPeriod = selP.value; renderKpisDetail(); renderDetail(); };
|
||
|
||
var selG = byId("sel-group");
|
||
selG.innerHTML = "";
|
||
var optAll = el("option", { value: "all", text: "Все группы" });
|
||
if (state.detailGroup === "all") optAll.setAttribute("selected", "");
|
||
selG.appendChild(optAll);
|
||
GROUPS.forEach(function (g) {
|
||
var opt = el("option", { value: g, text: g });
|
||
if (state.detailGroup === g) opt.setAttribute("selected", "");
|
||
selG.appendChild(opt);
|
||
});
|
||
selG.onchange = function () { state.detailGroup = selG.value; renderDetail(); };
|
||
|
||
byId("period-chip").textContent = periodLabel(state.period);
|
||
}
|
||
|
||
// ---------- переключение экранов ----------
|
||
function showScreen(name) {
|
||
state.screen = name;
|
||
byId("screen-overview").hidden = name !== "overview";
|
||
byId("screen-detail").hidden = name !== "detail";
|
||
byId("screen-title").textContent = name === "overview" ? "Обзор" : "Детали";
|
||
$$(".kt-ai-nav-item").forEach(function (a) {
|
||
if (a.getAttribute("data-screen") === name) a.classList.add("is-active");
|
||
else a.classList.remove("is-active");
|
||
});
|
||
}
|
||
function renderAll() {
|
||
renderKpis("kpi-overview");
|
||
renderChartOverview();
|
||
renderMix();
|
||
renderKpisDetail();
|
||
renderMonths();
|
||
renderDetail();
|
||
renderPeriodTabs();
|
||
}
|
||
|
||
// ---------- события ----------
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
// навигация
|
||
$$(".kt-ai-nav-item").forEach(function (a) {
|
||
a.addEventListener("click", function (e) { e.preventDefault(); showScreen(a.getAttribute("data-screen")); });
|
||
});
|
||
// сортировка table-months
|
||
$$("#table-months th.sortable").forEach(function (th) {
|
||
th.addEventListener("click", function () {
|
||
var k = th.getAttribute("data-sort");
|
||
if (state.sort.key === k) state.sort.dir = -state.sort.dir;
|
||
else { state.sort.key = k; state.sort.dir = k === "label" ? 1 : -1; }
|
||
renderMonths();
|
||
});
|
||
});
|
||
// сортировка table-detail
|
||
$$("#table-detail th.sortable").forEach(function (th) {
|
||
th.addEventListener("click", function () {
|
||
var k = th.getAttribute("data-sort");
|
||
if (ui.sortDetail.key === k) ui.sortDetail.dir = -ui.sortDetail.dir;
|
||
else { ui.sortDetail.key = k; ui.sortDetail.dir = 1; }
|
||
renderDetail();
|
||
});
|
||
});
|
||
// поиск
|
||
var search = byId("search-months");
|
||
if (search) search.addEventListener("input", function () { state.search = search.value; renderMonths(); });
|
||
var clear = byId("clear-search");
|
||
if (clear) clear.addEventListener("click", function () { state.search = ""; if (search) search.value = ""; renderMonths(); });
|
||
|
||
showScreen("overview");
|
||
renderAll();
|
||
});
|