108 lines
4.8 KiB
JavaScript
108 lines
4.8 KiB
JavaScript
'use strict';
|
||
/* ──────────────────────────────────────────────────────────────────
|
||
Dashboard view — landing screen of /exam-prep/:examKey
|
||
In F1: shows track meta + global counts + first-pass user progress.
|
||
Full live dashboard (slabnik themes, streak, plan) ships in F4 / F8 / F10.
|
||
────────────────────────────────────────────────────────────────── */
|
||
|
||
(async function () {
|
||
const { info } = await EP.boot();
|
||
|
||
const main = document.getElementById('ep-main');
|
||
if (!info?.track) {
|
||
main.innerHTML = `<div class="ep-empty">
|
||
<i data-lucide="alert-triangle"></i>
|
||
<h4>Не удалось загрузить данные экзамена</h4>
|
||
<p>Проверьте, что миграция применена и трек math9 включён.</p>
|
||
</div>`;
|
||
if (window.lucide) lucide.createIcons();
|
||
return;
|
||
}
|
||
|
||
const { track, counts, progress } = info;
|
||
const solvedPct = counts.total
|
||
? Math.round((progress.tasks_solved / counts.total) * 100)
|
||
: 0;
|
||
const accuracy = progress.total_attempts
|
||
? Math.round((progress.correct_attempts / progress.total_attempts) * 100)
|
||
: null;
|
||
|
||
main.innerHTML = `
|
||
<div class="ep-stats">
|
||
<div class="ep-stat">
|
||
<div class="ep-stat-label">Решено задач</div>
|
||
<div class="ep-stat-value ep-violet">${progress.tasks_solved} <span style="font-size:.7em;color:var(--text-3);font-weight:600">/ ${counts.total}</span></div>
|
||
<div class="ep-bar"><div class="ep-bar-fill" style="width:${solvedPct}%"></div></div>
|
||
<div class="ep-stat-sub">${solvedPct}% от банка</div>
|
||
</div>
|
||
<div class="ep-stat">
|
||
<div class="ep-stat-label">Точность</div>
|
||
<div class="ep-stat-value ${accuracy == null ? '' : accuracy >= 70 ? 'ep-good' : 'ep-warn'}">${accuracy == null ? '—' : accuracy + '%'}</div>
|
||
<div class="ep-stat-sub">${progress.correct_attempts} верно из ${progress.total_attempts} попыток</div>
|
||
</div>
|
||
<div class="ep-stat">
|
||
<div class="ep-stat-label">Серия (streak)</div>
|
||
<div class="ep-stat-value">—</div>
|
||
<div class="ep-stat-sub">Будет в F4</div>
|
||
</div>
|
||
<div class="ep-stat">
|
||
<div class="ep-stat-label">До экзамена</div>
|
||
<div class="ep-stat-value">—</div>
|
||
<div class="ep-stat-sub">Задайте дату в F10</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ep-card">
|
||
<h3>С чего начать</h3>
|
||
<div class="ep-card-hint">${escapeHtml(stripTags(track.intro_html || ''))}</div>
|
||
<div class="ep-cta-row">
|
||
<a class="ep-btn ep-btn-primary" href="/exam-prep/${track.exam_key}/practice">
|
||
<i data-lucide="play"></i> Начать тренировку
|
||
</a>
|
||
<a class="ep-btn" href="/exam-prep/${track.exam_key}/variants">
|
||
<i data-lucide="layout-grid"></i> Все варианты
|
||
</a>
|
||
<a class="ep-btn" href="/exam-prep/${track.exam_key}/mock">
|
||
<i data-lucide="timer"></i> Пробный экзамен
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ep-card">
|
||
<h3>Банк задач</h3>
|
||
<div class="ep-card-hint">Всего ${counts.total} задач в ${track.variants_count} вариантах.</div>
|
||
<div class="ep-stats" style="margin-bottom:0">
|
||
<div class="ep-stat">
|
||
<div class="ep-stat-label">Тестовая часть (А)</div>
|
||
<div class="ep-stat-value">${counts.mc}</div>
|
||
<div class="ep-stat-sub">выбор варианта а–д</div>
|
||
</div>
|
||
<div class="ep-stat">
|
||
<div class="ep-stat-label">Краткий ответ</div>
|
||
<div class="ep-stat-value">${counts.open}</div>
|
||
<div class="ep-stat-sub">число / дробь / пара</div>
|
||
</div>
|
||
<div class="ep-stat">
|
||
<div class="ep-stat-label">Развёрнутые</div>
|
||
<div class="ep-stat-value">${counts.long}</div>
|
||
<div class="ep-stat-sub">выражения, графики</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ep-card" style="opacity:.7">
|
||
<h3>Слабые темы</h3>
|
||
<div class="ep-card-hint">Топ-3 темы с худшей точностью появятся после фазы F6 (тегирование) и F8.</div>
|
||
</div>
|
||
`;
|
||
|
||
if (window.lucide) lucide.createIcons();
|
||
})();
|
||
|
||
function escapeHtml(s) {
|
||
return String(s || '').replace(/[&<>"']/g, c => ({ '&':'&', '<':'<', '>':'>', '"':'"', "'":''' }[c]));
|
||
}
|
||
function stripTags(s) {
|
||
return String(s || '').replace(/<[^>]+>/g, '');
|
||
}
|