refactor: ещё 6 модалок → LS.modal (dashboard, theory, course)

dashboard.html: 2 → 0 
  - join-modal — вступить в класс
  - qs-modal — быстрый тест с выбором предмета + режим + кол-во

theory.html: 1 → 0 
  - new-course-modal — создание нового курса учителем

course.html: 4 → 0 
  - add-section-modal — новый раздел курса
  - edit-course-modal — редактирование курса
  - add-lesson-modal — новый урок
  - save-course-tpl-modal — сохранить курс как шаблон

Везде:
  - Inline <div class=\"modal-overlay\">...</div> → удалён
  - openX(): создаёт modal через LS.modal({content, actions})
  - closeX() удалена — _xModal.close()
  - Глобальный selectQsSubject() inline'нут как listener на body модалки
  - Enter-handler на главных inputs сохранён

Не трогаю:
  - biochem.html#lib-modal — кастомная тёмная тема, не подходит под
    светлый LS.modal без редизайна
  - library.html — 3 сложные модалки (folder-access, assign, upload)
    с tabs и dynamic state — отдельный заход
  - classes.html — modal-assign (128 строк, complex) + review-modal
  - flashcards.html — fc-modal (не modal-overlay, своя CSS)

Прогресс миграции: 12 простых модалок → LS.modal за серию (4 ранее
+ 2 ранее + 6 сейчас). 4 страницы полностью очищены от
modal-overlay. Унифицированы:
  - ESC/backdrop/focus поведение
  - z-index (9000)
  - Анимация (scale .22s)
  - Адаптив на мобилке
This commit is contained in:
Maxim Dolgolyov
2026-05-16 19:33:39 +03:00
parent d3b16f55c8
commit 5c66105fc2
3 changed files with 183 additions and 241 deletions
+96 -140
View File
@@ -448,70 +448,6 @@
</div> </div>
<!-- Add section modal --> <!-- Add section modal -->
<div class="modal-overlay" id="add-section-modal" onclick="if(event.target===this)closeAddSectionModal()">
<div class="modal">
<div class="modal-title">Новый раздел</div>
<div class="form-group">
<label class="form-label">Название раздела</label>
<input class="form-input" id="as-title" placeholder="Например: Часть 1. Введение" />
</div>
<div class="modal-footer">
<button class="btn-cancel" onclick="closeAddSectionModal()">Отмена</button>
<button class="btn-primary" id="btn-do-add-section" onclick="doAddSection()">Создать</button>
</div>
</div>
</div>
<!-- Edit course modal -->
<div class="modal-overlay" id="edit-course-modal" onclick="if(event.target===this)closeEditModal()">
<div class="modal">
<div class="modal-title">Редактировать курс</div>
<div class="form-group">
<label class="form-label">Название</label>
<input class="form-input" id="ec-title" placeholder="Название курса" />
</div>
<div class="form-group" style="margin-top:12px">
<label class="form-label">Описание</label>
<textarea class="form-input" id="ec-desc" rows="3" placeholder="Краткое описание курса" style="resize:vertical"></textarea>
</div>
<div style="display:flex;gap:12px;margin-top:12px">
<div class="form-group" style="flex:0 0 80px">
<label class="form-label">Эмодзи</label>
<input class="form-input" id="ec-emoji" placeholder="" maxlength="4" style="text-align:center;font-size:1.4rem" />
</div>
<div class="form-group" style="flex:1">
<label class="form-label">Предмет</label>
<select class="form-input" id="ec-subject">
<option value="">— Не указан —</option>
<option value="bio">Биология</option>
<option value="chem">Химия</option>
<option value="math">Математика</option>
<option value="phys">Физика</option>
</select>
</div>
</div>
<div class="modal-footer">
<button class="btn-cancel" onclick="closeEditModal()">Отмена</button>
<button class="btn-primary" id="btn-do-edit-course" onclick="doEditCourse()">Сохранить</button>
</div>
</div>
</div>
<!-- Add lesson modal -->
<div class="modal-overlay" id="add-lesson-modal" onclick="if(event.target===this)closeAddLessonModal()">
<div class="modal">
<div class="modal-title">Новый урок</div>
<div class="form-group">
<label class="form-label">Название урока</label>
<input class="form-input" id="al-title" placeholder="Например: Строение клетки" />
</div>
<div class="modal-footer">
<button class="btn-cancel" onclick="closeAddLessonModal()">Отмена</button>
<button class="btn-primary" id="btn-do-add-lesson" onclick="doAddLesson()">Создать</button>
</div>
</div>
</div>
<script src="/js/api.js"></script> <script src="/js/api.js"></script>
<script src="/js/sidebar.js"></script> <script src="/js/sidebar.js"></script>
<script> <script>
@@ -900,17 +836,42 @@
} }
/* ── edit course modal ── */ /* ── edit course modal ── */
let _editCourseModal = null;
function openEditModal() { function openEditModal() {
if (!course) return; if (!course) return;
document.getElementById('ec-title').value = course.title || ''; const body = `
document.getElementById('ec-desc').value = course.description || ''; <div class="form-group">
document.getElementById('ec-emoji').value = course.coverEmoji || ''; <label class="form-label">Название</label>
document.getElementById('ec-subject').value = course.subjectSlug || ''; <input class="form-input" id="ec-title" value="${LS.esc(course.title || '')}" />
document.getElementById('edit-course-modal').classList.add('open'); </div>
setTimeout(() => document.getElementById('ec-title').focus(), 50); <div class="form-group" style="margin-top:12px">
} <label class="form-label">Описание</label>
function closeEditModal() { <textarea class="form-input" id="ec-desc" rows="3" style="resize:vertical">${LS.esc(course.description || '')}</textarea>
document.getElementById('edit-course-modal').classList.remove('open'); </div>
<div style="display:flex;gap:12px;margin-top:12px">
<div class="form-group" style="flex:0 0 80px">
<label class="form-label">Эмодзи</label>
<input class="form-input" id="ec-emoji" maxlength="4" style="text-align:center;font-size:1.4rem" value="${LS.esc(course.coverEmoji || '')}" />
</div>
<div class="form-group" style="flex:1">
<label class="form-label">Предмет</label>
<select class="form-input" id="ec-subject">
<option value="">— Не указан —</option>
<option value="bio">Биология</option>
<option value="chem">Химия</option>
<option value="math">Математика</option>
<option value="phys">Физика</option>
</select>
</div>
</div>`;
_editCourseModal = LS.modal({
title: 'Редактировать курс', content: body, size: 'sm',
actions: [
{ label: 'Отмена', onClick: () => _editCourseModal.close() },
{ label: 'Сохранить', primary: true, id: 'btn-do-edit-course', onClick: doEditCourse },
],
});
_editCourseModal.body.querySelector('#ec-subject').value = course.subjectSlug || '';
} }
async function doEditCourse() { async function doEditCourse() {
const btn = document.getElementById('btn-do-edit-course'); const btn = document.getElementById('btn-do-edit-course');
@@ -926,10 +887,9 @@
subjectSlug: document.getElementById('ec-subject').value || null, subjectSlug: document.getElementById('ec-subject').value || null,
}), }),
}); });
closeEditModal(); _editCourseModal?.close();
loadCourse(); loadCourse();
} catch (e) { LS.toast(e.message || 'Ошибка', 'error'); } } catch (e) { LS.toast(e.message || 'Ошибка', 'error'); btn.disabled = false; }
finally { btn.disabled = false; }
} }
/* ── toggle publish ── */ /* ── toggle publish ── */
@@ -1000,13 +960,21 @@
} }
/* ── add section modal ── */ /* ── add section modal ── */
let _addSectionModal = null;
function openAddSectionModal() { function openAddSectionModal() {
document.getElementById('as-title').value = ''; const body = `
document.getElementById('add-section-modal').classList.add('open'); <div class="form-group">
setTimeout(() => document.getElementById('as-title').focus(), 50); <label class="form-label">Название раздела</label>
} <input class="form-input" id="as-title" placeholder="Например: Часть 1. Введение" />
function closeAddSectionModal() { </div>`;
document.getElementById('add-section-modal').classList.remove('open'); _addSectionModal = LS.modal({
title: 'Новый раздел', content: body, size: 'sm',
actions: [
{ label: 'Отмена', onClick: () => _addSectionModal.close() },
{ label: 'Создать', primary: true, id: 'btn-do-add-section', onClick: doAddSection },
],
});
_addSectionModal.body.querySelector('#as-title').addEventListener('keydown', e => { if (e.key === 'Enter') doAddSection(); });
} }
async function doAddSection() { async function doAddSection() {
const title = document.getElementById('as-title').value.trim(); const title = document.getElementById('as-title').value.trim();
@@ -1019,24 +987,27 @@
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title, orderIndex: (course?.sections?.length || 0) }), body: JSON.stringify({ title, orderIndex: (course?.sections?.length || 0) }),
}); });
closeAddSectionModal(); _addSectionModal?.close();
loadCourse(); loadCourse();
} catch (e) { } catch (e) { LS.toast(e.message || 'Ошибка', 'error'); btn.disabled = false; }
LS.toast(e.message || 'Ошибка', 'error');
} finally { btn.disabled = false; }
} }
document.getElementById('as-title').addEventListener('keydown', e => {
if (e.key === 'Enter') doAddSection();
});
/* ── add lesson modal ── */ /* ── add lesson modal ── */
let _addLessonModal = null;
function openAddLessonModal() { function openAddLessonModal() {
document.getElementById('al-title').value = ''; const body = `
document.getElementById('add-lesson-modal').classList.add('open'); <div class="form-group">
setTimeout(() => document.getElementById('al-title').focus(), 50); <label class="form-label">Название урока</label>
} <input class="form-input" id="al-title" placeholder="Например: Строение клетки" />
function closeAddLessonModal() { </div>`;
document.getElementById('add-lesson-modal').classList.remove('open'); _addLessonModal = LS.modal({
title: 'Новый урок', content: body, size: 'sm',
actions: [
{ label: 'Отмена', onClick: () => _addLessonModal.close() },
{ label: 'Создать', primary: true, id: 'btn-do-add-lesson', onClick: doAddLesson },
],
});
_addLessonModal.body.querySelector('#al-title').addEventListener('keydown', e => { if (e.key === 'Enter') doAddLesson(); });
} }
async function doAddLesson() { async function doAddLesson() {
const title = document.getElementById('al-title').value.trim(); const title = document.getElementById('al-title').value.trim();
@@ -1049,26 +1020,40 @@
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ courseId: parseInt(courseId), title }), body: JSON.stringify({ courseId: parseInt(courseId), title }),
}); });
closeAddLessonModal(); _addLessonModal?.close();
location.href = '/lesson-editor?id=' + res.id; location.href = '/lesson-editor?id=' + res.id;
} catch (e) { } catch (e) { LS.toast(e.message || 'Ошибка', 'error'); btn.disabled = false; }
LS.toast(e.message || 'Ошибка', 'error');
} finally { btn.disabled = false; }
} }
document.getElementById('al-title').addEventListener('keydown', e => {
if (e.key === 'Enter') doAddLesson();
});
/* ── save course as template ── */ /* ── save course as template ── */
let _saveCtModal = null;
function openSaveCourseTplModal() { function openSaveCourseTplModal() {
if (!course) return; if (!course) return;
document.getElementById('ct-title').value = course.title || ''; const body = `
document.getElementById('ct-desc').value = course.description || ''; <div class="form-group">
document.getElementById('save-course-tpl-modal').classList.add('open'); <label class="form-label">Название шаблона</label>
setTimeout(() => document.getElementById('ct-title').focus(), 50); <input class="form-input" id="ct-title" value="${LS.esc(course.title || '')}" />
} </div>
function closeSaveCourseTplModal() { <div class="form-group">
document.getElementById('save-course-tpl-modal').classList.remove('open'); <label class="form-label">Описание</label>
<textarea class="form-input" id="ct-desc" rows="2" style="resize:vertical">${LS.esc(course.description || '')}</textarea>
</div>
<div class="form-group">
<label class="form-label">Категория</label>
<select class="form-input" id="ct-cat">
<option value="general">Общее</option>
<option value="lecture">Лекционный курс</option>
<option value="practice">Практикум</option>
<option value="exam">Подготовка к экзамену</option>
</select>
</div>`;
_saveCtModal = LS.modal({
title: 'Сохранить курс как шаблон', content: body, size: 'sm',
actions: [
{ label: 'Отмена', onClick: () => _saveCtModal.close() },
{ label: 'Сохранить', primary: true, id: 'btn-do-save-ct', onClick: doSaveCourseTpl },
],
});
} }
async function doSaveCourseTpl() { async function doSaveCourseTpl() {
const title = document.getElementById('ct-title').value.trim(); const title = document.getElementById('ct-title').value.trim();
@@ -1083,43 +1068,14 @@
subject_slug: course.subjectSlug || null, subject_slug: course.subjectSlug || null,
courseId: parseInt(courseId), courseId: parseInt(courseId),
}); });
closeSaveCourseTplModal(); _saveCtModal?.close();
LS.toast('Шаблон курса сохранён', 'success'); LS.toast('Шаблон курса сохранён', 'success');
} catch (e) { LS.toast(e.message || 'Ошибка', 'error'); } } catch (e) { LS.toast(e.message || 'Ошибка', 'error'); btn.disabled = false; }
finally { btn.disabled = false; }
} }
loadCourse(); loadCourse();
</script> </script>
<!-- Save course template modal -->
<div class="modal-overlay" id="save-course-tpl-modal" onclick="if(event.target===this)closeSaveCourseTplModal()">
<div class="modal">
<div class="modal-title">Сохранить курс как шаблон</div>
<div class="form-group">
<label class="form-label">Название шаблона</label>
<input class="form-input" id="ct-title" placeholder="Название шаблона" />
</div>
<div class="form-group">
<label class="form-label">Описание</label>
<textarea class="form-input" id="ct-desc" rows="2" placeholder="Краткое описание" style="resize:vertical"></textarea>
</div>
<div class="form-group">
<label class="form-label">Категория</label>
<select class="form-input" id="ct-cat">
<option value="general">Общее</option>
<option value="lecture">Лекционный курс</option>
<option value="practice">Практикум</option>
<option value="exam">Подготовка к экзамену</option>
</select>
</div>
<div class="modal-footer">
<button class="btn-cancel" onclick="closeSaveCourseTplModal()">Отмена</button>
<button class="btn-primary" id="btn-do-save-ct" onclick="doSaveCourseTpl()">Сохранить</button>
</div>
</div>
</div>
<script src="/js/notifications.js"></script> <script src="/js/notifications.js"></script>
<script src="/js/search.js"></script> <script src="/js/search.js"></script>
<script src="/js/mobile.js"></script> <script src="/js/mobile.js"></script>
+52 -57
View File
@@ -1526,45 +1526,8 @@
</div> </div>
<!-- Join modal --> <!-- Join modal -->
<div class="modal-overlay" id="join-modal" onclick="if(event.target===this)closeJoinModal()"> <!-- Quick-start test modal -->
<div class="modal"> <script src="/js/api.js"></script>
<div class="modal-title">Вступить в класс</div>
<input class="form-input" id="join-code" placeholder="Код приглашения" />
<div style="font-size:0.78rem;color:var(--text-3);margin-top:8px">Попросите учителя поделиться кодом или ссылкой</div>
<div class="modal-footer">
<button class="btn-cancel" onclick="closeJoinModal()">Отмена</button>
<button class="btn-join" id="btn-do-join" onclick="doJoin()">Вступить</button>
</div>
</div>
</div>
<!-- Quick-start test modal -->
<div class="modal-overlay" id="qs-modal" onclick="if(event.target===this)closeQuickStart()">
<div class="modal" style="max-width:480px">
<div class="modal-title">Быстрый тест</div>
<div class="qs-subjects" id="qs-subjects"></div>
<div class="qs-options">
<div class="qs-row">
<span class="qs-label">Режим</span>
<select class="qs-select" id="qs-mode">
<option value="exam">Экзамен</option>
<option value="practice">Тренировка</option>
<option value="random">Случайный</option>
</select>
</div>
<div class="qs-row">
<span class="qs-label">Вопросов</span>
<input class="qs-input" id="qs-count" type="number" min="5" max="50" value="25">
</div>
</div>
<div class="modal-footer">
<button class="btn-cancel" onclick="closeQuickStart()">Отмена</button>
<button class="btn-join" onclick="doQuickStart()">Начать</button>
</div>
</div>
</div>
<script src="/js/api.js"></script>
<script src="/js/sound.js"></script> <script src="/js/sound.js"></script>
<script src="/js/sidebar.js"></script> <script src="/js/sidebar.js"></script>
<script src="/js/notifications.js"></script> <script src="/js/notifications.js"></script>
@@ -2801,12 +2764,19 @@
/* ══ УВЕДОМЛЕНИЯ — handled by notifications.js ══ */ /* ══ УВЕДОМЛЕНИЯ — handled by notifications.js ══ */
/* ══ JOIN MODAL ════════════════════════════════════════════════════════ */ /* ══ JOIN MODAL ════════════════════════════════════════════════════════ */
let _joinModal = null;
function openJoinModal(code) { function openJoinModal(code) {
document.getElementById('join-code').value = code || ''; const body = `
document.getElementById('join-modal').classList.add('open'); <input class="form-input" id="join-code" placeholder="Код приглашения" value="${LS.esc(code || '')}" />
setTimeout(() => document.getElementById('join-code').focus(), 50); <div style="font-size:0.78rem;color:var(--text-3);margin-top:8px">Попросите учителя поделиться кодом или ссылкой</div>`;
_joinModal = LS.modal({
title: 'Вступить в класс', content: body, size: 'sm',
actions: [
{ label: 'Отмена', onClick: () => _joinModal.close() },
{ label: 'Вступить', primary: true, id: 'btn-do-join', onClick: doJoin },
],
});
} }
function closeJoinModal() { document.getElementById('join-modal').classList.remove('open'); }
async function doJoin() { async function doJoin() {
const code = document.getElementById('join-code').value.trim(); const code = document.getElementById('join-code').value.trim();
if (!code) return; if (!code) return;
@@ -2814,11 +2784,10 @@
btn.disabled = true; btn.disabled = true;
try { try {
const r = await LS.joinClass(code); const r = await LS.joinClass(code);
closeJoinModal(); _joinModal?.close();
LS.toast(`Вы вступили в класс «${r.class_name}»!`, 'success'); LS.toast(`Вы вступили в класс «${r.class_name}»!`, 'success');
loadAssignments(); loadAssignments();
} catch (e) { LS.toast('Ошибка: ' + e.message, 'error'); } } catch (e) { LS.toast('Ошибка: ' + e.message, 'error'); btn.disabled = false; }
finally { btn.disabled = false; }
} }
/* ══ SUBMISSIONS (student) ════════════════════════════════════════════ */ /* ══ SUBMISSIONS (student) ════════════════════════════════════════════ */
@@ -3439,33 +3408,59 @@
/* ══ B1: QUICK-START MODAL ═════════════════════════════════════════ */ /* ══ B1: QUICK-START MODAL ═════════════════════════════════════════ */
let _qsSlug = null; let _qsSlug = null;
let _qsModal = null;
async function openQuickStart(slug) { async function openQuickStart(slug) {
_qsSlug = slug || null; _qsSlug = slug || null;
let subjectsHtml = '';
try { try {
const subjects = await LS.getSubjects(); const subjects = await LS.getSubjects();
document.getElementById('qs-subjects').innerHTML = subjects.map(s => { subjectsHtml = subjects.map(s => {
const color = SUBJ_COLORS[s.slug] || '#9B5DE5'; const color = SUBJ_COLORS[s.slug] || '#9B5DE5';
const active = s.slug === _qsSlug ? ' active' : ''; const active = s.slug === _qsSlug ? ' active' : '';
return `<button class="qs-subj-btn${active}" onclick="selectQsSubject('${s.slug}',this)"> return `<button class="qs-subj-btn${active}" data-slug="${s.slug}">
<div class="qs-subj-icon" style="background:${color}">${lci(ICONS[s.slug]||'book-open')}</div> <div class="qs-subj-icon" style="background:${color}">${lci(ICONS[s.slug]||'book-open')}</div>
${esc(s.name)} ${esc(s.name)}
</button>`; </button>`;
}).join(''); }).join('');
reIcons();
} catch {} } catch {}
document.getElementById('qs-modal').classList.add('open'); const body = `
<div class="qs-subjects" id="qs-subjects">${subjectsHtml}</div>
<div class="qs-options">
<div class="qs-row">
<span class="qs-label">Режим</span>
<select class="qs-select" id="qs-mode">
<option value="exam">Экзамен</option>
<option value="practice">Тренировка</option>
<option value="random">Случайный</option>
</select>
</div>
<div class="qs-row">
<span class="qs-label">Вопросов</span>
<input class="qs-input" id="qs-count" type="number" min="5" max="50" value="25">
</div>
</div>`;
_qsModal = LS.modal({
title: 'Быстрый тест', content: body, size: 'sm',
actions: [
{ label: 'Отмена', onClick: () => _qsModal.close() },
{ label: 'Начать', primary: true, onClick: doQuickStart },
],
});
reIcons();
// Wire subject selection within modal
_qsModal.body.querySelectorAll('.qs-subj-btn').forEach(btn => {
btn.addEventListener('click', () => {
_qsSlug = btn.dataset.slug;
_qsModal.body.querySelectorAll('.qs-subj-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
});
});
} }
function selectQsSubject(slug, btn) {
_qsSlug = slug;
document.querySelectorAll('.qs-subj-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
}
function closeQuickStart() { document.getElementById('qs-modal').classList.remove('open'); }
function doQuickStart() { function doQuickStart() {
if (!_qsSlug) { LS.toast('Выберите предмет', 'warn'); return; } if (!_qsSlug) { LS.toast('Выберите предмет', 'warn'); return; }
const mode = document.getElementById('qs-mode').value; const mode = document.getElementById('qs-mode').value;
const count = parseInt(document.getElementById('qs-count').value) || 25; const count = parseInt(document.getElementById('qs-count').value) || 25;
closeQuickStart(); _qsModal?.close();
window.location.href = `/test-run?subject=${_qsSlug}&mode=${mode}&count=${count}`; window.location.href = `/test-run?subject=${_qsSlug}&mode=${mode}&count=${count}`;
} }
+35 -44
View File
@@ -340,37 +340,6 @@
</div><!-- /app-layout --> </div><!-- /app-layout -->
<!-- New course modal --> <!-- New course modal -->
<div class="modal-overlay" id="new-course-modal" onclick="if(event.target===this)closeNewCourseModal()">
<div class="modal">
<div class="modal-title">Новый курс</div>
<div class="form-group">
<label class="form-label">Предмет</label>
<select class="form-input" id="nc-subject">
<option value="bio">Биология</option>
<option value="chem">Химия</option>
<option value="math">Математика</option>
<option value="phys">Физика</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Название</label>
<input class="form-input" id="nc-title" placeholder="Например: Молекулярная биология" />
</div>
<div class="form-group">
<label class="form-label">Описание (необязательно)</label>
<textarea class="form-input" id="nc-desc" rows="2" placeholder="Кратко о курсе…" style="resize:vertical"></textarea>
</div>
<div class="form-group">
<label class="form-label">Эмодзи обложки</label>
<input class="form-input" id="nc-emoji" placeholder="" maxlength="4" style="max-width:90px" />
</div>
<div class="modal-footer">
<button class="btn-cancel" onclick="closeNewCourseModal()">Отмена</button>
<button class="btn-primary" id="btn-do-create" onclick="doCreateCourse()">Создать</button>
</div>
</div>
</div>
<script src="/js/api.js"></script> <script src="/js/api.js"></script>
<script src="/js/sidebar.js"></script> <script src="/js/sidebar.js"></script>
<script src="/js/notifications.js"></script> <script src="/js/notifications.js"></script>
@@ -559,15 +528,39 @@
loadContinue(); loadContinue();
/* ── modal ── */ /* ── modal ── */
let _newCourseModal = null;
function openNewCourseModal() { function openNewCourseModal() {
document.getElementById('nc-title').value = ''; const body = `
document.getElementById('nc-desc').value = ''; <div class="form-group">
document.getElementById('nc-emoji').value = ''; <label class="form-label">Предмет</label>
document.getElementById('new-course-modal').classList.add('open'); <select class="form-input" id="nc-subject">
setTimeout(() => document.getElementById('nc-title').focus(), 50); <option value="bio">Биология</option>
} <option value="chem">Химия</option>
function closeNewCourseModal() { <option value="math">Математика</option>
document.getElementById('new-course-modal').classList.remove('open'); <option value="phys">Физика</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Название</label>
<input class="form-input" id="nc-title" placeholder="Например: Молекулярная биология" />
</div>
<div class="form-group">
<label class="form-label">Описание (необязательно)</label>
<textarea class="form-input" id="nc-desc" rows="2" placeholder="Кратко о курсе…" style="resize:vertical"></textarea>
</div>
<div class="form-group">
<label class="form-label">Эмодзи обложки</label>
<input class="form-input" id="nc-emoji" placeholder="" maxlength="4" style="max-width:90px" />
</div>`;
_newCourseModal = LS.modal({
title: 'Новый курс', content: body, size: 'sm',
actions: [
{ label: 'Отмена', onClick: () => _newCourseModal.close() },
{ label: 'Создать', primary: true, id: 'btn-do-create', onClick: doCreateCourse },
],
});
const titleEl = _newCourseModal.body.querySelector('#nc-title');
titleEl.addEventListener('keydown', e => { if (e.key === 'Enter') doCreateCourse(); });
} }
async function doCreateCourse() { async function doCreateCourse() {
const title = document.getElementById('nc-title').value.trim(); const title = document.getElementById('nc-title').value.trim();
@@ -585,15 +578,13 @@
coverEmoji: document.getElementById('nc-emoji').value.trim() || '', coverEmoji: document.getElementById('nc-emoji').value.trim() || '',
}), }),
}); });
closeNewCourseModal(); _newCourseModal?.close();
location.href = '/course?id=' + res.id; location.href = '/course?id=' + res.id;
} catch (e) { } catch (e) {
LS.toast(e.message || 'Ошибка', 'error'); LS.toast(e.message || 'Ошибка', 'error');
} finally { btn.disabled = false; } btn.disabled = false;
}
} }
document.getElementById('nc-title').addEventListener('keydown', e => {
if (e.key === 'Enter') doCreateCourse();
});
/* ══════════════════════════════════════════════════════════════════ /* ══════════════════════════════════════════════════════════════════
TEMPLATE BROWSER TEMPLATE BROWSER