feat(lessons): группа в отчёте о занятии подбирается по расписанию
В модалке отчёта о занятии группа выбиралась всегда первой по списку.
Теперь при открытии модалки и при смене даты/времени группа
подбирается автоматически по параметрам группы: день недели
(day_of_week) совпадает с датой занятия, а время попадает в окно
[time_start, time_end].
Что добавлено:
- lessonDowFromIso — ISO-дата в день недели через Date.UTC/getUTCDay,
тем же приёмом, что isoAddDays в js/datetime.js: прямой
new Date('YYYY-MM-DD') сдвинул бы дату на день назад
- lessonMinutes — разбор 'HH:MM(:SS)' из PostgreSQL в минуты
- lessonGroupForDateTime — подбор группы; при пересечении расписаний
побеждает группа с самым поздним началом
- applyLessonGroupAuto — ставит значение в #lessonGroup
Ручной выбор пользователя закрепляется флагом lessonGroupTouched
(ставится только реальным событием change на самом селекте), поэтому
автоподбор не перебивает его и не зацикливается. Флаг сбрасывается
в resetLessonModalFields. Явный opts.groupId из журнала/отчётов и
редактирование по opts.id автоподбор не трогают.
Бэкенд, схема БД и миграции не менялись: /api/groups уже отдаёт
day_of_week/time_start/time_end через SELECT g.*
This commit is contained in:
+55
-2
@@ -489,6 +489,7 @@ function copyText(text) {
|
||||
|
||||
let lessonModalGroups = null;
|
||||
let lessonModalGroupsPromise = null;
|
||||
let lessonGroupTouched = false;
|
||||
|
||||
function lessonTodayIso() {
|
||||
return todayIso();
|
||||
@@ -498,6 +499,49 @@ function lessonNowTime() {
|
||||
return nowHm();
|
||||
}
|
||||
|
||||
function lessonDowFromIso(iso) {
|
||||
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(iso || ''));
|
||||
if (!m) return -1;
|
||||
return new Date(Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]))).getUTCDay();
|
||||
}
|
||||
|
||||
function lessonMinutes(hm) {
|
||||
const m = /^\s*(\d{1,2}):(\d{2})/.exec(String(hm || ''));
|
||||
if (!m) return null;
|
||||
return Number(m[1]) * 60 + Number(m[2]);
|
||||
}
|
||||
|
||||
function lessonGroupForDateTime(isoDate, hmTime) {
|
||||
const groups = lessonModalGroups || [];
|
||||
const dow = lessonDowFromIso(isoDate);
|
||||
const minutes = lessonMinutes(hmTime);
|
||||
if (dow < 0 || minutes == null) return null;
|
||||
let best = null;
|
||||
let bestStart = -1;
|
||||
for (const g of groups) {
|
||||
if (g.day_of_week == null || !g.time_start || !g.time_end) continue;
|
||||
if (Number(g.day_of_week) !== dow) continue;
|
||||
const start = lessonMinutes(g.time_start);
|
||||
const end = lessonMinutes(g.time_end);
|
||||
if (start == null || end == null || end < start) continue;
|
||||
if (minutes < start || minutes > end) continue;
|
||||
if (start > bestStart) { bestStart = start; best = g; }
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
function applyLessonGroupAuto(modal) {
|
||||
if (lessonGroupTouched) return null;
|
||||
const groupSel = modal.querySelector('#lessonGroup');
|
||||
if (!groupSel) return null;
|
||||
const g = lessonGroupForDateTime(modal.querySelector('#lessonDate').value, modal.querySelector('#lessonTime').value);
|
||||
if (!g) return null;
|
||||
const value = String(g.id);
|
||||
if (!Array.from(groupSel.options).some(o => o.value === value)) return null;
|
||||
groupSel.value = value;
|
||||
return g;
|
||||
}
|
||||
|
||||
async function loadLessonGroups() {
|
||||
if (lessonModalGroups) return lessonModalGroups;
|
||||
if (!lessonModalGroupsPromise) {
|
||||
@@ -576,8 +620,15 @@ function ensureLessonModal() {
|
||||
const id = modal.querySelector('#lessonId').value;
|
||||
if (id) openLessonVersions(id);
|
||||
});
|
||||
groupSel.addEventListener('change', () => prefetchLessonForGroupDate());
|
||||
modal.querySelector('#lessonDate').addEventListener('change', () => prefetchLessonForGroupDate());
|
||||
groupSel.addEventListener('change', () => { lessonGroupTouched = true; prefetchLessonForGroupDate(); });
|
||||
modal.querySelector('#lessonDate').addEventListener('change', () => {
|
||||
applyLessonGroupAuto(modal);
|
||||
prefetchLessonForGroupDate();
|
||||
});
|
||||
modal.querySelector('#lessonTime').addEventListener('change', () => {
|
||||
applyLessonGroupAuto(modal);
|
||||
prefetchLessonForGroupDate();
|
||||
});
|
||||
modal.querySelector('#lessonDupGo').addEventListener('click', () => {
|
||||
const id = modal.querySelector('#lessonDup').dataset.id;
|
||||
if (!id) return;
|
||||
@@ -718,6 +769,7 @@ function updateLessonAiRow(modal, r) {
|
||||
|
||||
function resetLessonModalFields(modal) {
|
||||
lessonDupSeq++;
|
||||
lessonGroupTouched = false;
|
||||
modal.querySelector('#lessonId').value = '';
|
||||
showLessonDuplicate(modal, null);
|
||||
modal.querySelector('#lessonDate').value = lessonTodayIso();
|
||||
@@ -857,6 +909,7 @@ async function openLessonModal(opts = {}) {
|
||||
resetLessonModalFields(modal);
|
||||
const wanted = opts.groupId ? String(opts.groupId) : previous;
|
||||
if (wanted && groups.some(g => String(g.id) === wanted)) groupSel.value = wanted;
|
||||
if (!opts.id && !opts.groupId) applyLessonGroupAuto(modal);
|
||||
|
||||
if (opts.text) {
|
||||
const textEl = modal.querySelector('#lessonText');
|
||||
|
||||
Reference in New Issue
Block a user