'use strict'; /* admin → subjects (доступные тесты) section */ (function () { 'use strict'; let inited = false; const SC_MODES = { exam: 'Экзамен', practice: 'Пробный тест', topic: 'По теме', random: 'Случайный' }; const SC_ICONS = { bio:'dna', chem:'flask-conical', math:'calculator', phys:'zap' }; const SC_COLORS = { bio:'#9B5DE5', chem:'#06D6A0', math:'#06B6D4', phys:'#F59E0B' }; // кэш тестов по предмету для селектора const _scTests = {}; async function loadScTests(slug) { if (_scTests[slug]) return _scTests[slug]; const tests = await LS.getTests(slug); _scTests[slug] = tests; return tests; } function setSrcMode(slug, src) { const rndBtn = document.getElementById(`sc-src-rnd-${slug}`); const fixBtn = document.getElementById(`sc-src-fix-${slug}`); const pick = document.getElementById(`sc-test-pick-${slug}`); const cntWrap = document.getElementById(`sc-count-wrap-${slug}`); rndBtn.classList.toggle('active', src === 'random'); fixBtn.classList.toggle('active', src === 'fixed'); pick.classList.toggle('open', src === 'fixed'); cntWrap.style.display = src === 'random' ? '' : 'none'; if (src === 'fixed') { loadAndRenderTestPick(slug); } else { const dr = document.getElementById(`sc-qdr-${slug}`); if (dr) { dr.style.display = 'none'; } } } async function loadAndRenderTestPick(slug) { const sel = document.getElementById(`sc-test-sel-${slug}`); if (sel.dataset.loaded) return; sel.innerHTML = ''; try { const tests = await loadScTests(slug); const cur = document.getElementById(`sc-card-${slug}`)?.dataset.testId || ''; sel.innerHTML = `` + tests.map(t => ``).join(''); sel.dataset.loaded = '1'; } catch(e) { sel.innerHTML = ''; } } async function load() { const wrap = document.getElementById('subj-config-list'); wrap.innerHTML = LS.skeleton(4); try { const subjects = await LS.getSubjects(); wrap.innerHTML = subjects.map(s => { const hasFix = !!s.default_test_id; const color = SC_COLORS[s.slug] || '#9B5DE5'; const mode = s.default_mode || 'exam'; const count = s.default_count || 25; const srcLabel = hasFix ? 'Фикс. тест' : `${count} вопросов`; return `
${esc(s.name)}
${SC_MODES[mode]} ${srcLabel} ${s.question_count ?? 0} в базе
Режим
Источник
Вопросов
Тест
`; }).join(''); if (window.lucide) lucide.createIcons(); subjects.filter(s => s.default_test_id).forEach(s => { loadAndRenderTestPick(s.slug); const btn = document.getElementById(`sc-qdr-btn-${s.slug}`); if (btn) btn.style.display = ''; }); } catch (e) { wrap.innerHTML = `
Ошибка: ${esc(e.message)}
`; } } function toggleScCard(slug) { const card = document.getElementById('sc-card-' + slug); if (!card) return; const wasOpen = card.classList.contains('open'); document.querySelectorAll('.sc-card.open').forEach(c => c.classList.remove('open')); if (!wasOpen) { card.classList.add('open'); if (window.lucide) lucide.createIcons({ nodes: [card] }); } } function applyPreset(slug, mode, count) { document.getElementById('sc-mode-' + slug).value = mode; document.getElementById('sc-count-' + slug).value = count; setSrcMode(slug, 'random'); const card = document.getElementById('sc-card-' + slug); card.querySelectorAll('.sc-preset').forEach(p => p.classList.remove('active')); const isFix = document.getElementById('sc-src-fix-' + slug).classList.contains('active'); card.querySelectorAll('.sc-preset').forEach(p => { const txt = p.textContent.trim(); const mLabel = SC_MODES[mode]; if (txt === mLabel + ' ' + count && !isFix) p.classList.add('active'); }); saveSubjectConfig(slug); } function updateScSummary(slug) { const el = document.getElementById('sc-sum-' + slug); if (!el) return; const mode = document.getElementById('sc-mode-' + slug).value; const isFix = document.getElementById('sc-src-fix-' + slug).classList.contains('active'); const count = document.getElementById('sc-count-' + slug).value; const srcLabel = isFix ? 'Фикс. тест' : count + ' вопросов'; el.innerHTML = `${SC_MODES[mode]}${srcLabel}`; } async function saveSubjectConfig(slug) { const btn = document.getElementById(`sc-save-btn-${slug}`); const mode = document.getElementById(`sc-mode-${slug}`).value; const isFix = document.getElementById(`sc-src-fix-${slug}`).classList.contains('active'); const count = Number(document.getElementById(`sc-count-${slug}`)?.value || 25); const testId = isFix ? (document.getElementById(`sc-test-sel-${slug}`).value || null) : null; if (btn) { btn.disabled = true; btn.textContent = '...'; } const payload = { default_mode: mode, default_count: count, default_test_id: testId ? Number(testId) : null }; try { await LS.updateSubject(slug, payload); document.getElementById(`sc-card-${slug}`).dataset.testId = testId || ''; if (isFix) document.getElementById(`sc-test-sel-${slug}`).dataset.loaded = ''; updateScSummary(slug); if (btn) { btn.classList.add('saved'); btn.textContent = 'Сохранено'; } setTimeout(() => { if (btn) { btn.classList.remove('saved'); btn.textContent = 'Сохранить'; btn.disabled = false; } }, 1500); } catch (e) { LS.toast('Ошибка: ' + e.message, 'error'); if (btn) { btn.disabled = false; btn.textContent = 'Сохранить'; } } } function onScTestChange(slug) { const tid = document.getElementById(`sc-test-sel-${slug}`).value; const btn = document.getElementById(`sc-qdr-btn-${slug}`); btn.style.display = tid ? '' : 'none'; const dr = document.getElementById(`sc-qdr-${slug}`); dr.style.display = 'none'; document.getElementById(`sc-qdr-inner-${slug}`).innerHTML = ''; } async function toggleScDrawer(slug) { const dr = document.getElementById(`sc-qdr-${slug}`); const tid = Number(document.getElementById(`sc-test-sel-${slug}`).value); if (!tid) return; if (dr.style.display !== 'none') { dr.style.display = 'none'; return; } dr.style.display = ''; await renderScDrawer(slug, tid); } const _scCache = {}; // tid → { test, subjectQs } async function renderScDrawer(slug, tid) { const inner = document.getElementById(`sc-qdr-inner-${slug}`); inner.innerHTML = LS.skeleton(3, 'row'); try { const [t, subjectQs] = await Promise.all([ LS.getTest(tid), LS.getQuestions(slug, null, 'date_asc').catch(() => []), ]); _scCache[tid] = { test: t, subjectQs }; inner.innerHTML = `
Вопросы в тесте (${t.questions.length})
${renderScQList(t.questions, tid, slug)}
Добавить из базы
${renderScPicker(subjectQs, new Set(t.questions.map(q=>q.id)), tid, slug)}
`; AdminCtx.renderMath(inner); } catch(e) { inner.innerHTML = `
Ошибка: ${esc(e.message)}
`; } } function renderScQList(questions, tid, slug) { const { DIFF_LABELS, qTypeBadge, qOptsPreview } = AdminCtx; if (!questions.length) return '
Пусто. Добавьте вопросы справа
'; return questions.map((q,i) => `
${i+1}.
${esc(q.text)}
${DIFF_LABELS[q.difficulty]||q.difficulty} ${qTypeBadge(q.type)} ${qOptsPreview(q)}
`).join(''); } function renderScPicker(questions, inIds, tid, slug) { const { DIFF_LABELS, qTypeBadge } = AdminCtx; if (!questions.length) return '
Вопросов нет в этом предмете
'; return questions.map(q => { const added = inIds.has(q.id); return `
${esc(q.text)}
${DIFF_LABELS[q.difficulty]||q.difficulty} ${qTypeBadge(q.type)} ${q.topic ? `${esc(q.topic)}` : ''}
`; }).join(''); } function filterScPicker(tid, slug, q) { const cache = _scCache[tid]; if (!cache) return; const lq = q.toLowerCase(); const filtered = lq.length < 1 ? cache.subjectQs : cache.subjectQs.filter(x => x.text.toLowerCase().includes(lq) || (x.topic||'').toLowerCase().includes(lq)); const inIds = new Set(cache.test.questions.map(x=>x.id)); document.getElementById(`sc-pick-${tid}`).innerHTML = renderScPicker(filtered, inIds, tid, slug); } async function scAddQ(tid, slug, qid, btn) { btn.disabled = true; btn.textContent = '…'; try { await LS.addQuestionsToTest(tid, [qid]); const t = await LS.getTest(tid); _scCache[tid].test = t; document.getElementById(`sc-ql-${tid}`).innerHTML = renderScQList(t.questions, tid, slug); document.getElementById(`sc-qcnt-${tid}`).textContent = t.questions.length; const item = document.getElementById(`sc-pick-item-${tid}-${qid}`); if (item) { item.style.opacity='0.4'; item.style.pointerEvents='none'; } const addBtn = document.getElementById(`sc-add-btn-${tid}-${qid}`); if (addBtn) { addBtn.innerHTML = ''; if(window.lucide)lucide.createIcons(); } AdminCtx.renderMath(document.getElementById(`sc-ql-${tid}`)); } catch(e) { LS.toast('Ошибка: ' + e.message, 'error'); btn.disabled=false; btn.textContent='+'; } } async function scRemoveQ(tid, slug, qid) { try { await LS.removeQFromTest(tid, qid); const t = await LS.getTest(tid); _scCache[tid].test = t; document.getElementById(`sc-ql-${tid}`).innerHTML = renderScQList(t.questions, tid, slug); document.getElementById(`sc-qcnt-${tid}`).textContent = t.questions.length; const item = document.getElementById(`sc-pick-item-${tid}-${qid}`); if (item) { item.style.opacity=''; item.style.pointerEvents=''; } const addBtn = document.getElementById(`sc-add-btn-${tid}-${qid}`); if (addBtn) { addBtn.textContent='+'; addBtn.disabled=false; } } catch(e) { LS.toast('Ошибка: ' + e.message, 'error'); } } // Expose onclick handlers window.toggleScCard = toggleScCard; window.applyPreset = applyPreset; window.setSrcMode = setSrcMode; window.saveSubjectConfig = saveSubjectConfig; window.onScTestChange = onScTestChange; window.toggleScDrawer = toggleScDrawer; window.filterScPicker = filterScPicker; window.scAddQ = scAddQ; window.scRemoveQ = scRemoveQ; window.AdminSections = window.AdminSections || {}; window.AdminSections.subjects = { init: async () => { if (inited) return; inited = true; await load(); }, reload: load, }; })();