diff --git a/frontend/admin.html b/frontend/admin.html index 6378e6f..3d85186 100644 --- a/frontend/admin.html +++ b/frontend/admin.html @@ -988,9 +988,6 @@ - @@ -1096,14 +1093,6 @@
- -
-
Общая статистика
-
-
По предметам
-
-
-
@@ -2125,7 +2114,6 @@ - diff --git a/frontend/js/admin/admin.js b/frontend/js/admin/admin.js index bf6c822..d07fa3f 100644 --- a/frontend/js/admin/admin.js +++ b/frontend/js/admin/admin.js @@ -53,7 +53,6 @@ // Routes that map 1:1 to a section module (Phase 2-extracted). const ROUTE_TO_SECTION = { overview: 'overview', - stats: 'stats', questions: 'questions', tests: 'tests', assignments: 'assignments', diff --git a/frontend/js/admin/router.js b/frontend/js/admin/router.js index 4074ba0..555a642 100644 --- a/frontend/js/admin/router.js +++ b/frontend/js/admin/router.js @@ -3,7 +3,7 @@ * Wraps the existing switchTab() flow without replacing it. * * Hash format: #[/[/...]] - * #stats → { route: 'stats', params: [] } + * #overview → { route: 'overview', params: [] } * #users → { route: 'users', params: [] } * #users/123 → { route: 'users', params: ['123'] } * #sessions/456/foo → { route: 'sessions', params: ['456','foo'] } diff --git a/frontend/js/admin/sections/overview.js b/frontend/js/admin/sections/overview.js index cf2fdc8..5054829 100644 --- a/frontend/js/admin/sections/overview.js +++ b/frontend/js/admin/sections/overview.js @@ -250,7 +250,7 @@ }).join(''); } - function render(data) { + function render(data, stats) { const el = document.getElementById('overview-content'); if (!el) return; ensureOvStyles(); @@ -331,6 +331,42 @@
По предметам (24ч)
${renderSubjectBar(subjects24h)}`; + /* ── all-time totals (перенесено из бывшей вкладки «Статистика») ── */ + const allTimeHtml = stats ? ` +
Итоги за всё время
+
+
+
+
${fmtNum(stats.totalUsers)}
+
Пользователей
+
+
+
+
${fmtNum(stats.totalTests)}
+
Тестов пройдено
+
+
+
+
${stats.avgScore != null ? stats.avgScore + '%' : '—'}
+
Средний результат
+
+
` : ''; + + /* ── per-subject all-time performance (перенесено из «Статистики») ── */ + const subjAllTimeHtml = (stats && Array.isArray(stats.bySubject) && stats.bySubject.length) ? ` +
Результаты по предметам (всё время)
+
+ ${stats.bySubject.map(function (b) { + const p = b.avg_pct == null ? 0 : b.avg_pct; + const barColor = p >= 75 ? 'var(--green)' : p >= 50 ? 'var(--amber)' : 'var(--pink)'; + return '
' + + '
' + e(b.name) + '
' + b.tests + ' тестов
' + + '
' + (b.avg_pct == null ? '—' : b.avg_pct) + '%
' + + '
' + + '
'; + }).join('')} +
` : ''; + /* ── results tables ────────────────────────────────────────── */ const topTableHtml = top.length ? ` @@ -386,7 +422,9 @@ ${alertsHtml} ${invHtml} + ${allTimeHtml} ${subjHtml} + ${subjAllTimeHtml}
@@ -429,9 +467,13 @@ if (!el) return; renderSkeleton(el); try { - const data = await LS.adminGetOverview(); + // Обзор + перенесённые из бывшей вкладки «Статистика» итоги за всё время. + const [data, stats] = await Promise.all([ + LS.adminGetOverview(), + LS.adminGetStats().catch(() => null), + ]); _lastLoadTs = Date.now(); - render(data); + render(data, stats); startTsInterval(); } catch (e) { LS.state.error(el, e, () => load()); diff --git a/frontend/js/admin/sections/stats.js b/frontend/js/admin/sections/stats.js deleted file mode 100644 index 432a929..0000000 --- a/frontend/js/admin/sections/stats.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict'; -/* admin → stats section */ -(function () { - 'use strict'; - let inited = false; - - async function load() { - try { - const s = await LS.adminGetStats(); - document.getElementById('stats-grid').innerHTML = ` -
-
-
${s.totalUsers}
-
Пользователей
-
-
-
-
${s.totalTests}
-
Тестов пройдено
-
-
-
-
${s.avgScore ?? '—'}%
-
Средний результат
-
`; - if (window.lucide) lucide.createIcons(); - const subjEl = document.getElementById('subj-stats'); - if (!s.bySubject?.length) { subjEl.innerHTML = '
Нет данных
'; return; } - subjEl.innerHTML = s.bySubject.map(b => { - const pct = b.avg_pct ?? 0; - const barColor = pct >= 75 ? 'var(--green)' : pct >= 50 ? 'var(--amber)' : 'var(--pink)'; - return `
-
${esc(b.name)}
${b.tests} тестов
-
-
${b.avg_pct ?? '—'}%
-
-
-
`; - }).join(''); - } catch (e) { - LS.state.error(document.getElementById('stats-grid'), e, load); - } - } - - window.AdminSections = window.AdminSections || {}; - window.AdminSections.stats = { - init: async () => { if (inited) return; inited = true; await load(); }, - reload: load, - }; -})();