feat(комментарии): письмо ученику об ответе под уроком

Ответы персонала в ветке комментариев никуда не уведомляли: ученик видел
ответ, только если сам возвращался в урок. У тредов вопросов письмо есть,
у комментариев не было — из-за этого ответ под уроком 6.3 «Холст» висел
непрочитанным, пока его не продублировали руками.

- sendCommentReplyEmail: тот же шаблон ДС-2, что у ответов в тредах, с текстом
  ответа внутри письма и кнопкой на урок. Свой лимит превью 4000 символов:
  ввод разрешает 10 000, а порог вопросов (2000) резал бы развёрнутые разборы.
- commentReplyRecipient: правила «шлём / не шлём» вынесены чистой функцией и
  покрыты тестами — не шлём при выключенной настройке, не-staff, удалённом
  родителе, ответе самому себе и пустой почте.
- notifyStudentOnCommentReply: тумблер в админке рядом с уведомлением о фидбеке,
  по умолчанию включён.
- Удалён неподключённый дубль comment-actions.ts: компонент комментариев ходит
  в lib/actions/student-actions.ts, а в дубле жил свой лимит 2000 — правка в нём
  не давала эффекта.

Проверено: type-check и lint чистые (3 ошибки линтера — существующий долг в
quick-enroll-modal и kinescope-player), vitest 13/13, письмо отправлено боем
и доставлено (Resend, last_event=delivered).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-15 11:49:45 +05:00
co-authored by Claude Opus 5
parent 273cb2d1b4
commit 8223cbb963
8 changed files with 321 additions and 72 deletions
+5
View File
@@ -289,6 +289,11 @@ export function SettingsForm({ initial }: { initial: Settings }) {
checked={bool("notifyStudentOnFeedback")}
onChange={(v) => set("notifyStudentOnFeedback", v ? "true" : "false")}
/>
<Toggle
label="Уведомлять ученика об ответе на его комментарий"
checked={bool("notifyStudentOnCommentReply")}
onChange={(v) => set("notifyStudentOnCommentReply", v ? "true" : "false")}
/>
</div>
</Section>