feat(lesson-ai): новый шаблон промпта + предупреждение о дубле отчёта
- заменён дефолтный промпт редактуры отчёта о занятии на формат «сообщение тьютора»: связный текст от 3-го лица, 1 абзац 2–4 предложения, начало «На занятии ребята …», практика «В конце занятия …», без группы/даты/времени и markdown, обращение на «вы» убрано, «они» → «каждый» - синхронизированы три копии промпта: LESSON_AI_DEFAULT_PROMPT в server.js и сид lesson_ai_prompt в db/init.sql и db/migration.sql; добавлен пример «плохо → хорошо» с запретом переносить факты примера - дубль отчёта в модалке создания больше не автозаполняет форму: показывается баннер #lessonDup с кнопкой перехода к существующему отчёту - проверка на дубль срабатывает и при смене даты (#lessonDate), защищена счётчиком поколений lessonDupSeq от гонки при быстрой смене группы/даты - стили .lesson-dup в public/admin.css, уточнена подсказка в карточке шаблона отчёта на public/settings.html - DDL не менялся: ни таблиц, ни колонок, ни индексов
This commit is contained in:
+33
-8
@@ -541,6 +541,10 @@ function ensureLessonModal() {
|
||||
placeholder="Например: разобрали циклы for и while, решали задачи на списки, домашнее задание — функции"></textarea>
|
||||
<span class="hint" id="lessonHint">0 / 5000</span>
|
||||
</div>
|
||||
<div class="lesson-dup" id="lessonDup" hidden>
|
||||
<span>За эту группу и дату отчёт уже есть — создать второй нельзя.</span>
|
||||
<button type="button" class="btn-link" id="lessonDupGo">Перейти к существующему отчёту</button>
|
||||
</div>
|
||||
<label class="toggle lesson-ai-toggle" id="lessonAiRow">
|
||||
<input type="checkbox" id="lessonAiCheck" checked>
|
||||
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
||||
@@ -569,6 +573,13 @@ function ensureLessonModal() {
|
||||
if (id) openLessonVersions(id);
|
||||
});
|
||||
groupSel.addEventListener('change', () => prefetchLessonForGroupDate());
|
||||
modal.querySelector('#lessonDate').addEventListener('change', () => prefetchLessonForGroupDate());
|
||||
modal.querySelector('#lessonDupGo').addEventListener('click', () => {
|
||||
const id = modal.querySelector('#lessonDup').dataset.id;
|
||||
if (!id) return;
|
||||
closeLessonModal();
|
||||
openLessonModal({ id });
|
||||
});
|
||||
modal.querySelector('#lessonForm').addEventListener('submit', e => submitLessonModal(e));
|
||||
return modal;
|
||||
}
|
||||
@@ -601,9 +612,8 @@ async function submitLessonModal(e) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (!res.ok) {
|
||||
if (res.status === 409 && data.id) {
|
||||
closeLessonModal();
|
||||
openLessonModal({ id: data.id });
|
||||
showToast('За эту дату отчёт уже есть — открыт для редактирования');
|
||||
showLessonDuplicate(modal, data.id);
|
||||
showToast('За эту группу и дату отчёт уже есть — новый создать нельзя');
|
||||
return;
|
||||
}
|
||||
showToast(data.error || 'Не удалось сохранить отчёт');
|
||||
@@ -625,22 +635,35 @@ async function submitLessonModal(e) {
|
||||
}
|
||||
}
|
||||
|
||||
let lessonDupSeq = 0;
|
||||
|
||||
async function prefetchLessonForGroupDate() {
|
||||
const modal = document.getElementById('lessonModal');
|
||||
if (!modal || !modal.classList.contains('open')) return;
|
||||
if (modal.querySelector('#lessonId').value) return;
|
||||
const groupId = modal.querySelector('#lessonGroup').value;
|
||||
const date = modal.querySelector('#lessonDate').value;
|
||||
if (!groupId || !date) return;
|
||||
if (!groupId || !date) { showLessonDuplicate(modal, null); return; }
|
||||
const seq = ++lessonDupSeq;
|
||||
try {
|
||||
const q = `group_id=${encodeURIComponent(groupId)}&date_from=${encodeURIComponent(date)}&date_to=${encodeURIComponent(date)}&limit=1`;
|
||||
const res = await fetch(`${API}/api/lesson-reports?${q}`, { headers: hdr() });
|
||||
if (!res.ok) return;
|
||||
if (seq !== lessonDupSeq) return;
|
||||
if (!res.ok) { showLessonDuplicate(modal, null); return; }
|
||||
const data = await res.json();
|
||||
if (seq !== lessonDupSeq) return;
|
||||
const item = (data.items || [])[0];
|
||||
if (!item) return;
|
||||
fillLessonModalFromReport(modal, item);
|
||||
} catch {}
|
||||
showLessonDuplicate(modal, item ? item.id : null);
|
||||
} catch {
|
||||
if (seq === lessonDupSeq) showLessonDuplicate(modal, null);
|
||||
}
|
||||
}
|
||||
|
||||
function showLessonDuplicate(modal, existingId) {
|
||||
const row = modal.querySelector('#lessonDup');
|
||||
if (!row) return;
|
||||
row.dataset.id = existingId ? String(existingId) : '';
|
||||
row.hidden = !existingId;
|
||||
}
|
||||
|
||||
function fillLessonModalFromReport(modal, r) {
|
||||
@@ -686,7 +709,9 @@ function updateLessonAiRow(modal, r) {
|
||||
}
|
||||
|
||||
function resetLessonModalFields(modal) {
|
||||
lessonDupSeq++;
|
||||
modal.querySelector('#lessonId').value = '';
|
||||
showLessonDuplicate(modal, null);
|
||||
modal.querySelector('#lessonDate').value = lessonTodayIso();
|
||||
modal.querySelector('#lessonTime').value = lessonNowTime();
|
||||
const textEl = modal.querySelector('#lessonText');
|
||||
|
||||
Reference in New Issue
Block a user