'use strict';
/* admin → sessions section: sessions timeline + drawer detail */
(function () {
'use strict';
let inited = false;
let allSessions = [];
let openDrawerId = null;
async function load() {
const subject = document.getElementById('t-subject').value;
document.getElementById('t-body').innerHTML = '
';
openDrawerId = null;
try {
allSessions = await LS.adminGetSessions({ subject: subject || undefined });
renderSessions();
} catch (e) {
document.getElementById('t-body').innerHTML = `Ошибка: ${esc(e.message)}
`;
}
}
function sessPctRing(pct) {
const { pctClass } = AdminCtx;
const pc = pctClass(pct);
const colorMap = {'pct-hi':'var(--green)','pct-mid':'var(--amber)','pct-lo':'var(--pink)'};
const color = colorMap[pc] || 'var(--text-3)';
const circ = 106.8;
const dash = (pct / 100 * circ).toFixed(1);
return ``;
}
function renderSessions() {
const { MODES, fmtDate, fmtTime } = AdminCtx;
const modeF = document.getElementById('t-mode').value;
const searchF = document.getElementById('t-search').value.toLowerCase();
const filtered = allSessions.filter(s => {
if (modeF && s.mode !== modeF) return false;
if (searchF && !s.user_name.toLowerCase().includes(searchF) && !s.user_email.toLowerCase().includes(searchF)) return false;
return true;
});
document.getElementById('t-count').textContent = `${filtered.length} тестов`;
if (!filtered.length) {
document.getElementById('t-body').innerHTML = 'Нет тестов
';
return;
}
const groups = {};
filtered.forEach(s => {
const key = fmtDate(s.started_at);
(groups[key] = groups[key] || []).push(s);
});
document.getElementById('t-body').innerHTML = Object.entries(groups).map(([date, sessions]) =>
`${date}
${sessions.map(s => {
const ring = s.percent !== null
? sessPctRing(s.percent)
: `
—
`;
return `
${ring}
${esc(s.user_name)}
${esc(s.subject_name||'?')} · ${MODES[s.mode]||s.mode}
${s.score??'—'} / ${s.total}
${fmtTime(s.duration_sec)}
`;
}).join('')}
`
).join('');
}
async function toggleDrawer(id) {
const drawerEl = document.getElementById('tdrawer-' + id);
const drawer = document.getElementById('drawer-' + id);
const trow = document.getElementById('trow-' + id);
if (openDrawerId && openDrawerId !== id) {
document.getElementById('tdrawer-' + openDrawerId)?.classList.remove('open');
document.getElementById('drawer-' + openDrawerId)?.classList.remove('open');
document.getElementById('trow-' + openDrawerId)?.classList.remove('open');
}
if (openDrawerId === id) {
drawerEl.classList.remove('open'); drawer.classList.remove('open'); trow.classList.remove('open');
openDrawerId = null; return;
}
openDrawerId = id; trow.classList.add('open');
drawerEl.classList.add('open');
requestAnimationFrame(() => drawer.classList.add('open'));
const inner = document.getElementById('drawer-inner-' + id);
if (inner.dataset.loaded) return;
inner.dataset.loaded = '1';
try {
const d = await LS.adminGetSessionDetail(id);
renderDrawer(inner, d);
} catch (e) { inner.innerHTML = `Ошибка: ${esc(e.message)}
`; }
}
function renderDrawer(el, d) {
const { MODES, pctClass, fmtDate, fmtTime, renderMath } = AdminCtx;
const pct = d.score !== null && d.total ? Math.round((d.score/d.total)*100) : null;
const pc = pctClass(pct);
const correct = d.questions.filter(q => q.is_correct).length;
const wrong = d.questions.filter(q => !q.is_correct && q.chosen_option_id).length;
const skipped = d.questions.filter(q => !q.chosen_option_id).length;
const qHtml = d.questions.map((q,i) => {
const status = !q.chosen_option_id ? 'skipped' : q.is_correct ? 'correct' : 'wrong';
const badgeTxt = { correct:'Верно', wrong:'Неверно', skipped:'Пропущено' }[status];
const opts = q.options.map(o => {
const isCor = o.is_correct, isCho = o.id === q.chosen_option_id;
let cls='', icon='';
if (isCor) { cls='correct-opt'; icon=''; }
else if (isCho && !isCor) { cls='chosen-wrong'; icon=''; }
return `${icon}${esc(o.text)}
`;
}).join('');
const expl = q.explanation ? `Пояснение: ${esc(q.explanation)}
` : '';
return `
${esc(q.text)}
${opts}
${expl}
`;
}).join('');
el.innerHTML = `
${qHtml||'
Вопросы не найдены
'}
`;
renderMath(el);
if (window.lucide) lucide.createIcons();
}
// Expose handlers
window.loadSessions = load;
window.renderSessions = renderSessions;
window.toggleDrawer = toggleDrawer;
window.AdminSections = window.AdminSections || {};
window.AdminSections.sessions = {
init: async () => { if (inited) return; inited = true; await load(); },
reload: load,
};
})();