'use strict';
/* admin → sims (simulations) section — контент-движок, Фазы 4-5.
*
* Каталог берётся из БД (/api/lab/sims), а НЕ из захардкоженного списка.
* Управление: вкл/выкл (зеркалится в legacy sim_disabled_ids), «рекомендуемая»,
* курикулумные связи (Фаза 5). Мастер-тумблер модуля — /api/settings/sims. */
(function () {
'use strict';
let inited = false;
const CAT_LABEL = { math: 'Математика', phys: 'Физика', chem: 'Химия', bio: 'Биология', game: 'Игры' };
const CAT_ORDER = ['math', 'phys', 'chem', 'bio', 'game'];
let _moduleDisabled = false;
let _sims = []; // [{id,cat,title,enabled,featured,tags,subject,grade,sort}]
let _textbooks = null; // кэш каталога учебников для выпадающего списка связей
function esc(s) {
return String(s == null ? '' : s).replace(/[&<>"']/g, c =>
({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
}
async function load() {
try {
const data = await LS.api('/api/lab/sims');
_moduleDisabled = !!data.module_disabled;
_sims = Array.isArray(data.sims) ? data.sims : [];
_render();
} catch (e) { LS.toast('Ошибка загрузки симуляций: ' + e.message, 'error'); }
}
function _render() {
const masterChk = document.getElementById('sims-master-chk');
if (masterChk) masterChk.checked = !_moduleDisabled;
const grid = document.getElementById('sims-grid');
if (!grid) return;
// group by category, preserving catalogue sort within group
const byCat = {};
_sims.forEach(s => { (byCat[s.cat] = byCat[s.cat] || []).push(s); });
const cats = CAT_ORDER.filter(c => byCat[c]).concat(
Object.keys(byCat).filter(c => !CAT_ORDER.includes(c)));
let html = '';
cats.forEach(cat => {
html += `
${esc(CAT_LABEL[cat] || cat)}
`;
byCat[cat].forEach(s => {
const tags = (s.tags || []).map(t => esc(t)).join(', ');
html += `
${esc(s.id)}${tags ? ' · ' + tags : ''}
`;
});
});
grid.innerHTML = html;
if (window.lucide) lucide.createIcons();
}
async function simsMasterToggle(checked) {
try {
await LS.api('/api/settings/sims', { method: 'PUT', body: JSON.stringify({ module_disabled: !checked }) });
_moduleDisabled = !checked;
LS.toast(checked ? 'Модуль симуляций включён' : 'Модуль симуляций отключён', checked ? 'success' : 'warning');
} catch (e) { LS.toast('Ошибка: ' + e.message, 'error'); }
}
async function simToggleOne(simId, enabled) {
try {
await LS.api('/api/lab/sims/' + encodeURIComponent(simId), { method: 'PATCH', body: JSON.stringify({ enabled }) });
const s = _sims.find(x => x.id === simId);
if (s) s.enabled = enabled;
const card = document.getElementById('simcard-' + simId);
if (card) card.classList.toggle('enabled', enabled);
LS.toast(enabled ? `«${simId}» включена` : `«${simId}» отключена`, enabled ? 'success' : 'warning');
} catch (e) { LS.toast('Ошибка: ' + e.message, 'error'); }
}
async function simToggleFeatured(simId, featured) {
try {
await LS.api('/api/lab/sims/' + encodeURIComponent(simId), { method: 'PATCH', body: JSON.stringify({ featured }) });
const s = _sims.find(x => x.id === simId);
if (s) s.featured = featured;
_render();
LS.toast(featured ? `«${simId}» в рекомендуемых` : `«${simId}» убрана из рекомендуемых`, 'success');
} catch (e) { LS.toast('Ошибка: ' + e.message, 'error'); }
}
/* ── Фаза 5: редактор курикулумных связей (inline-панель под карточкой) ── */
async function _ensureTextbooks() {
if (_textbooks) return _textbooks;
try {
const data = await LS.api('/api/access/catalog');
_textbooks = (data && data.textbooks) || [];
} catch (e) { _textbooks = []; }
return _textbooks;
}
async function simToggleLinks(simId) {
const panel = document.getElementById('simlinks-' + simId);
if (!panel) return;
if (panel.style.display !== 'none') { panel.style.display = 'none'; return; }
panel.style.display = 'block';
panel.innerHTML = 'Загрузка связей…
';
try {
const [rel] = await Promise.all([
LS.api('/api/lab/sims/' + encodeURIComponent(simId) + '/related'),
_ensureTextbooks(),
]);
_renderLinksPanel(simId, rel);
} catch (e) {
panel.innerHTML = 'Ошибка: ' + esc(e.message) + '
';
}
}
function _renderLinksPanel(simId, rel) {
const panel = document.getElementById('simlinks-' + simId);
if (!panel) return;
const links = (rel && rel.links) || {};
const tb = links.textbook || [];
let html = 'Связи с учебниками
';
if (tb.length) {
html += '';
tb.forEach(l => {
html += `
${esc(l.label || l.ref_id)}
`;
});
html += '
';
} else {
html += 'Пока нет связей
';
}
// add-form: textbook