Files
Learn_System/frontend/js/admin/sections/games.js
T
Maxim Dolgolyov c370eaa803 feat(trainer): ИИ-тренажёр — генераторы задач + SimExpr-верификатор, прогресс, фича-флаг
- движок _trainer_engine.js: instantiate/generateBatch/verifyRoot/checkStudentAnswer/exprToLatex
- 5 генераторов уравнений 7 класса (generators.js), приём «корень-вперёд» → целые ответы
- страница /trainer: KaTeX-рендер, чипы-темы, мгновенная проверка, подсказка/решение, авто-выбор навыка
- прогресс practice_progress (мигр.081) + /api/practice/progress|attempt + LS.practiceProgressList/Submit
- фича-флаг trainer: тумблер в админке (Модули), requireFeature, FEATURE_HREFS (скрытие сайдбара+редирект), MODULE_CATALOG
- fix: подключён Lucide CDN на странице (иначе иконки сайдбара пустые)
- тесты practice.test.js (10/10); план развития plans/ai-trainer/PLAN.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 13:11:47 +03:00

145 lines
11 KiB
JavaScript

'use strict';
/* admin → games (game features + free-student features) section */
(function () {
'use strict';
let inited = false;
const GAME_FEATURES = [
{ key: 'gamification', label: 'Геймификация (всё)', desc: 'Мастер-выключатель: XP, уровни, достижения, монеты, стрики, магазин, лидерборд, испытания, рамки. Выкл → всё это скрыто и не начисляется у ВСЕХ', icon: 'trophy' },
{ key: 'hangman', label: 'Виселица', desc: 'Игра «Угадай слово» — отгадывание терминов по буквам', icon: 'gamepad-2' },
{ key: 'crossword', label: 'Кроссворд', desc: 'Кроссворд из терминов — генерируется автоматически по темам', icon: 'grid-3x3' },
{ key: 'pet', label: 'Питомец', desc: 'Виртуальный питомец, отражающий активность ученика', icon: 'heart' },
{ key: 'red_book', label: 'Красная книга', desc: 'Интерактивная Красная книга РБ: виды, биомы, пищевые сети, квесты', icon: 'leaf' },
{ key: 'collection', label: 'Коллекция', desc: 'Коллекция карточек и достижений — игровой прогресс ученика', icon: 'layers' },
{ key: 'flashcards', label: 'Флеш-карточки', desc: 'Карточки для запоминания терминов и понятий методом интервальных повторений', icon: 'square-stack' },
{ key: 'imggen', label: 'Генерация картинок (ИИ)', desc: 'ИИ-генерация изображений в ассистенте, флэшкартах, уроках, питомце, аватаре, доске', icon: 'image' },
{ key: 'knowledge_map', label: 'Карта знаний', desc: 'Визуальная карта тем и связей между биологическими понятиями', icon: 'share-2' },
{ key: 'sitemap', label: 'Путеводитель', desc: 'Пункт «Путеводитель» в меню — обзорная карта разделов системы', icon: 'map' },
{ key: 'lab', label: 'Лаборатория', desc: 'Раздел «Лаборатория»: виртуальные симуляции и интерактивные опыты', icon: 'atom' },
{ key: 'theory', label: 'Теория', desc: 'Раздел «Теория»: учебные курсы и уроки для учеников', icon: 'brain' },
{ key: 'board', label: 'Доска', desc: 'Классная доска с объявлениями, постами и обсуждениями', icon: 'layout-dashboard'},
{ key: 'biochem', label: 'Биохимия', desc: 'Молекулярный редактор, задачи на построение молекул и реакции', icon: 'flask-conical' },
{ key: 'live_quiz', label: 'Живая викторина', desc: 'Синхронная викторина в реальном времени для всего класса', icon: 'radio' },
{ key: 'classroom', label: 'Онлайн-уроки (classroom)', desc: 'Синхронные онлайн-уроки с доской и видео', icon: 'video' },
{ key: 'sim_builder', label: 'Конструктор симуляций', desc: 'Создание учителем своих интерактивных симуляций (рабочее поле, формулы, физика, графики)', icon: 'pencil-ruler' },
{ key: 'quantik', label: 'Квантик: Законы Мира', desc: '2D физика-головоломка: уровни на движке симуляций, прогресс, скины', icon: 'rocket' },
{ key: 'trainer', label: 'Тренажёр', desc: 'ИИ-тренажёр: бесконечные сгенерированные задачи по темам (уравнения 7 класс), мгновенная проверка ответа подстановкой, прогресс по навыкам', icon: 'dumbbell' },
{ key: 'wishes', label: 'Пожелания', desc: 'Трекер пожеланий по улучшению: пользователи подают идеи, админ ведёт по статусам', icon: 'lightbulb' },
];
const FS_FEATURES = [
{ key: 'gamification', label: 'Геймификация', desc: 'XP, уровни, достижения, монеты, стрики, магазин', icon: 'trophy' },
{ key: 'hangman', label: 'Виселица', desc: 'Игра «Угадай слово» — отгадывание терминов по буквам', icon: 'gamepad-2' },
{ key: 'crossword', label: 'Кроссворд', desc: 'Кроссворд из терминов — генерируется автоматически', icon: 'grid-3x3' },
{ key: 'pet', label: 'Питомец', desc: 'Виртуальный питомец, отражающий активность ученика', icon: 'heart' },
{ key: 'red_book', label: 'Красная книга', desc: 'Интерактивная Красная книга РБ: виды, биомы, квесты', icon: 'leaf' },
{ key: 'collection', label: 'Коллекция', desc: 'Коллекция карточек и игровой прогресс ученика', icon: 'layers' },
{ key: 'lab', label: 'Лаборатория', desc: 'Виртуальные симуляции и интерактивные опыты', icon: 'flask-conical' },
{ key: 'quantik', label: 'Квантик: Законы Мира', desc: '2D физика-головоломка на движке симуляций', icon: 'rocket' },
{ key: 'knowledge_map',label: 'Карта знаний', desc: 'Визуальная карта тем и связей между понятиями', icon: 'map' },
{ key: 'flashcards', label: 'Флеш-карточки', desc: 'Карточки для повторения терминов и понятий', icon: 'square-stack' },
{ key: 'imggen', label: 'Генерация картинок (ИИ)', desc: 'ИИ-генерация изображений в ассистенте, флэшкартах, уроках, питомце', icon: 'image' },
{ key: 'board', label: 'Доска', desc: 'Классная доска с объявлениями и постами', icon: 'layout-dashboard' },
{ key: 'biochem', label: 'Биохимия', desc: 'Молекулярный редактор, задачи на построение молекул и реакции', icon: 'flask-conical' },
{ key: 'live_quiz', label: 'Живая викторина', desc: 'Синхронная викторина в реальном времени для всего класса', icon: 'radio' },
];
async function loadGamesAdmin() {
const grid = document.getElementById('games-features-grid');
try {
const features = await LS.api('/api/admin/features');
grid.innerHTML = '';
for (const f of GAME_FEATURES) {
const enabled = features[f.key] !== false;
const card = document.createElement('div');
card.className = 'perm-card' + (enabled ? ' enabled' : '');
card.innerHTML = `
<div class="perm-info">
<div class="perm-label"><i data-lucide="${f.icon}" style="width:14px;height:14px;vertical-align:-2px;margin-right:6px"></i>${f.label}</div>
<div class="perm-desc">${f.desc}</div>
</div>
<label class="perm-toggle">
<input type="checkbox" ${enabled ? 'checked' : ''} onchange="toggleGameFeature('${f.key}', this.checked, this)" />
<span class="perm-track"></span>
<span class="perm-thumb"></span>
</label>`;
grid.appendChild(card);
}
if (window.lucide) lucide.createIcons();
} catch(e) {
grid.innerHTML = '<div class="error">Ошибка загрузки</div>';
}
}
async function toggleGameFeature(key, enabled, checkbox) {
try {
await LS.api('/api/admin/features', {
method: 'PATCH',
body: JSON.stringify({ [key]: enabled }),
});
const card = checkbox.closest('.perm-card');
if (card) card.classList.toggle('enabled', enabled);
LS.toast(enabled ? 'Функция включена' : 'Функция отключена', 'success');
} catch(e) {
checkbox.checked = !enabled;
LS.toast('Ошибка: ' + e.message, 'error');
}
}
async function loadFsFeatures() {
const grid = document.getElementById('fs-features-grid');
try {
const features = await LS.api('/api/admin/free-student-features');
grid.innerHTML = '';
for (const f of FS_FEATURES) {
const enabled = features[f.key] !== false;
const card = document.createElement('div');
card.className = 'perm-card' + (enabled ? ' enabled' : '');
card.innerHTML = `
<div class="perm-info">
<div class="perm-label"><i data-lucide="${f.icon}" style="width:14px;height:14px;vertical-align:-2px;margin-right:6px"></i>${f.label}</div>
<div class="perm-desc">${f.desc}</div>
</div>
<label class="perm-toggle">
<input type="checkbox" ${enabled ? 'checked' : ''} onchange="toggleFsFeature('${f.key}', this.checked, this)" />
<span class="perm-track"></span>
<span class="perm-thumb"></span>
</label>`;
grid.appendChild(card);
}
if (window.lucide) lucide.createIcons();
} catch(e) {
grid.innerHTML = '<div class="error">Ошибка загрузки</div>';
}
}
async function toggleFsFeature(key, enabled, checkbox) {
try {
await LS.api('/api/admin/free-student-features', {
method: 'PATCH',
body: JSON.stringify({ [key]: enabled }),
});
const card = checkbox.closest('.perm-card');
if (card) card.classList.toggle('enabled', enabled);
LS.toast(enabled ? 'Модуль включён' : 'Модуль отключён', 'success');
} catch(e) {
checkbox.checked = !enabled;
LS.toast('Ошибка: ' + e.message, 'error');
}
}
async function load() {
await loadGamesAdmin();
await loadFsFeatures();
}
window.toggleGameFeature = toggleGameFeature;
window.toggleFsFeature = toggleFsFeature;
window.AdminSections = window.AdminSections || {};
window.AdminSections.games = {
init: async () => { if (inited) return; inited = true; await load(); },
reload: load,
};
})();