diff --git a/backend/src/controllers/courseController.js b/backend/src/controllers/courseController.js index 79ad37d..974ce88 100644 --- a/backend/src/controllers/courseController.js +++ b/backend/src/controllers/courseController.js @@ -45,6 +45,10 @@ function list(req, res) { let where = role === 'student' ? 'WHERE c.is_published = 1' : 'WHERE 1=1'; const args = []; if (subject) { where += ' AND c.subject_slug = ?'; args.push(subject); } + // Personal "container" courses (quick lessons) are hidden from the catalog; + // their owner still sees their own. + if (role === 'student') { where += ' AND c.is_personal = 0'; } + else { where += ' AND (c.is_personal = 0 OR c.created_by = ?)'; args.push(uid); } const rows = db.prepare(` SELECT c.*, diff --git a/backend/src/controllers/lessonController.js b/backend/src/controllers/lessonController.js index a3ead48..7e404af 100644 --- a/backend/src/controllers/lessonController.js +++ b/backend/src/controllers/lessonController.js @@ -302,4 +302,28 @@ function deleteComment(req, res) { res.json({ ok: true }); } -module.exports = { get, create, update, remove, saveBlocks, markComplete, saveNote, listComments, addComment, deleteComment }; +/* ── POST /api/lessons/quick ────────────────────────────────────────────── + Create a standalone "quick lesson" without manually building a course: + reuse (or lazily create) the teacher's hidden personal container course, + add one lesson to it, and return its id for the editor. */ +function quickLesson(req, res) { + const uid = req.user.id; + let container = db.prepare( + 'SELECT id FROM courses WHERE created_by = ? AND is_personal = 1 ORDER BY id LIMIT 1' + ).get(uid); + if (!container) { + const r = db.prepare(` + INSERT INTO courses (subject_slug, title, description, cover_emoji, order_index, is_published, is_personal, created_by) + VALUES ('personal', 'Мои материалы', 'Отдельные уроки без курса', '', 0, 1, 1, ?) + `).run(uid); + container = { id: Number(r.lastInsertRowid) }; + } + const title = (req.body && req.body.title && String(req.body.title).trim()) || 'Новый урок'; + const n = db.prepare('SELECT COUNT(*) AS c FROM lessons WHERE course_id = ?').get(container.id).c; + const r2 = db.prepare( + 'INSERT INTO lessons (course_id, title, order_index) VALUES (?, ?, ?)' + ).run(container.id, title, n); + res.status(201).json({ lessonId: Number(r2.lastInsertRowid), courseId: container.id }); +} + +module.exports = { get, create, update, remove, saveBlocks, markComplete, saveNote, listComments, addComment, deleteComment, quickLesson }; diff --git a/backend/src/db/migrations/059_courses_personal.sql b/backend/src/db/migrations/059_courses_personal.sql new file mode 100644 index 0000000..8394626 --- /dev/null +++ b/backend/src/db/migrations/059_courses_personal.sql @@ -0,0 +1,11 @@ +-- ═══════════════════════════════════════════════════════════════ +-- 059: Personal "container" course for standalone quick lessons +-- +-- A teacher who just wants to author a single lesson (without manually +-- creating a whole course) gets a hidden personal container course +-- (is_personal = 1, one per teacher). Quick lessons are added there. +-- The course catalog hides personal containers from everyone except their +-- owner (see courseController.list). +-- ═══════════════════════════════════════════════════════════════ + +ALTER TABLE courses ADD COLUMN is_personal INTEGER NOT NULL DEFAULT 0; diff --git a/backend/src/routes/lessons.js b/backend/src/routes/lessons.js index 5ea0f58..602d771 100644 --- a/backend/src/routes/lessons.js +++ b/backend/src/routes/lessons.js @@ -13,6 +13,7 @@ router.post('/:id/comments', c.addComment); router.delete('/:id/comments/:cid', c.deleteComment); // Teacher/admin only +router.post('/quick', requireRole('teacher','admin'), c.quickLesson); router.post('/', requireRole('teacher','admin'), c.create); router.put('/:id', requireRole('teacher','admin'), c.update); router.delete('/:id', requireRole('teacher','admin'), c.remove); diff --git a/frontend/theory.html b/frontend/theory.html index d933c31..ec609d4 100644 --- a/frontend/theory.html +++ b/frontend/theory.html @@ -304,6 +304,9 @@ + @@ -350,6 +353,7 @@ document.getElementById('btn-classes').style.display = ''; document.getElementById('btn-new-course').style.display = ''; document.getElementById('btn-from-tpl').style.display = ''; + document.getElementById('btn-quick-lesson').style.display = ''; } if (isAdmin) { document.getElementById('btn-admin').style.display = ''; @@ -586,6 +590,24 @@ } } + /* Быстрый урок: создаёт отдельный урок в личном курсе-контейнере и сразу + открывает редактор — без ручного создания курса. */ + async function createQuickLesson() { + const btn = document.getElementById('btn-quick-lesson'); + if (btn) btn.disabled = true; + try { + const res = await LS.api('/api/lessons/quick', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({}), + }); + location.href = '/lesson-editor.html?id=' + res.lessonId; + } catch (e) { + LS.toast(e.message || 'Ошибка', 'error'); + if (btn) btn.disabled = false; + } + } + /* ══════════════════════════════════════════════════════════════════ TEMPLATE BROWSER ══════════════════════════════════════════════════════════════════ */