feat(lessons): тема занятия в отчёте о занятии
В модалку #lessonModal добавлено текстовое поле «Тема занятия» (#lessonTopic) между датой/временем и текстом отчёта. Поле необязательное, лимит 300 символов. Протянуто по всему срезу: - колонка lesson_reports.topic в db/init.sql, db/migration.sql и ensureLessonReportsTable() — миграция идемпотентная, ALTER IF NOT EXISTS - POST /api/lesson-reports принимает topic, PUT обновляет; в GET-списке колонка добавлена в явный SELECT (там не SELECT *, без правки не пришла бы) - тема в normalizeRestoreData, чтобы бэкап/restore её не теряли - тема уходит в ИИ-контекст воркера строкой «Тема занятия: ...» - вывод в списке отчётов (lessons.js + .lesson-topic в admin.css) В PUT пустая строка очищает тему, а отсутствие поля в теле запроса её не трогает: CASE WHEN $6::boolean THEN $3::text ELSE topic END, а не COALESCE — при COALESCE пустая тема затиралась бы старым значением и поле нельзя было бы очистить. Версии не трогаем: lesson_report_versions хранит только text. Тесты: +5 проверок (создание, список, редактирование, очистка, >300 символов), api.smoketest.js — 76 PASS / 0 FAIL.
This commit is contained in:
@@ -250,6 +250,37 @@ async function main() {
|
||||
plain.status === 201 && plain.data.ai_status === 'none' && plain.data.text_original === null,
|
||||
{ status: plain.status, ai_status: plain.data.ai_status });
|
||||
|
||||
const topicName = 'Циклы for и while';
|
||||
const withTopic = await api('/api/lesson-reports', {
|
||||
token, method: 'POST',
|
||||
body: { group_id: gid, lesson_date: '2019-05-18', lesson_time: '10:00', topic: topicName, text: 'Тема занятия для смоук-теста.', ai_check: false },
|
||||
});
|
||||
ok('lesson-report: тема занятия сохраняется при создании',
|
||||
withTopic.status === 201 && withTopic.data.topic === topicName, { status: withTopic.status, topic: withTopic.data.topic });
|
||||
|
||||
const listed = await api(`/api/lesson-reports?group_id=${gid}&date_from=2019-05-18&date_to=2019-05-18`, { token });
|
||||
ok('lesson-report: тема занятия в списке',
|
||||
listed.status === 200 && (listed.data.items || []).some(i => i.topic === topicName),
|
||||
{ status: listed.status, item: (listed.data.items || [])[0] && (listed.data.items || [])[0].topic });
|
||||
|
||||
const edited = await api(`/api/lesson-reports/${withTopic.data.id}`, {
|
||||
token, method: 'PUT', body: { topic: 'Обновлённая тема' },
|
||||
});
|
||||
ok('lesson-report: тема занятия обновляется при редактировании',
|
||||
edited.status === 200 && edited.data.topic === 'Обновлённая тема', { status: edited.status, topic: edited.data.topic });
|
||||
|
||||
const cleared = await api(`/api/lesson-reports/${withTopic.data.id}`, {
|
||||
token, method: 'PUT', body: { topic: '' },
|
||||
});
|
||||
ok('lesson-report: тему занятия можно очистить',
|
||||
cleared.status === 200 && !cleared.data.topic, { status: cleared.status, topic: cleared.data.topic });
|
||||
|
||||
const tooLong = await api(`/api/lesson-reports/${withTopic.data.id}`, {
|
||||
token, method: 'PUT', body: { topic: 'я'.repeat(301) },
|
||||
});
|
||||
ok('lesson-report: слишком длинная тема -> 400', tooLong.status === 400, { status: tooLong.status, error: tooLong.data && tooLong.data.error });
|
||||
await api(`/api/lesson-reports/${withTopic.data.id}`, { token, method: 'DELETE' });
|
||||
|
||||
const versions = await api(`/api/lesson-reports/${plain.data.id}/versions`, { token });
|
||||
ok('lesson-report: история версий содержит исходный текст',
|
||||
versions.status === 200 && Array.isArray(versions.data.items) && versions.data.items.length >= 1
|
||||
|
||||
Reference in New Issue
Block a user