From ac69a57290b4e7f8380b6ac2f901e6630a5804fb Mon Sep 17 00:00:00 2001 From: alekan Date: Thu, 2 Apr 2026 00:09:19 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B5=D1=80=D0=BA=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20"=D0=A1=D0=BE?= =?UTF-8?q?=D0=B7=D0=B4=D0=B0=D1=82=D1=8C=20=D0=B7=D0=B0=D0=BD=D1=8F=D1=82?= =?UTF-8?q?=D0=B8=D0=B5"=20=D0=B2=20"=D0=A0=D0=B0=D1=81=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=BD=D1=8F=D1=82=D0=B8?= =?UTF-8?q?=D0=B9"=20=D0=B8=D0=B7=20"=D0=9F=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D0=B8",=20=D0=B2=D0=B8=D0=B7?= =?UTF-8?q?=D1=83=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=B8=D0=B7=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=8D=D1=82=D0=B8=D1=85?= =?UTF-8?q?=20=D0=BC=D0=BE=D0=B4=D0=B0=D0=BB=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/admin/css/department.css | 29 +++ frontend/admin/js/views/schedule.js | 374 ++++++++++++++++++++++++--- frontend/admin/js/views/users.js | 154 +++--------- frontend/admin/views/schedule.html | 140 ++++++++++- frontend/admin/views/users.html | 376 ++++++++++++++-------------- 5 files changed, 739 insertions(+), 334 deletions(-) diff --git a/frontend/admin/css/department.css b/frontend/admin/css/department.css index 93c8913..88b83c9 100644 --- a/frontend/admin/css/department.css +++ b/frontend/admin/css/department.css @@ -312,4 +312,33 @@ details.table-item .content td{ details.table-item .content{ overflow-x: auto; } + +/* ===== Контейнер занятий преподавателя в модалках ===== */ +.cs-modal-table .lessons-container { + max-height: 50vh; + overflow-y: auto; + padding-right: 0.5rem; + scrollbar-width: thin; + scrollbar-color: rgba(99, 102, 241, 0.55) rgba(255, 255, 255, 0.06); +} + +.cs-modal-table .lessons-container::-webkit-scrollbar { + width: 10px; +} + +.cs-modal-table .lessons-container::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.06); + border-radius: 10px; +} + +.cs-modal-table .lessons-container::-webkit-scrollbar-thumb { + background: rgba(99, 102, 241, 0.55); + border-radius: 10px; + border: 2px solid rgba(0, 0, 0, 0); + background-clip: padding-box; +} + +.cs-modal-table .lessons-container::-webkit-scrollbar-thumb:hover { + background: rgba(99, 102, 241, 0.75); +} \ No newline at end of file diff --git a/frontend/admin/js/views/schedule.js b/frontend/admin/js/views/schedule.js index 03c2950..57ca394 100755 --- a/frontend/admin/js/views/schedule.js +++ b/frontend/admin/js/views/schedule.js @@ -1,5 +1,5 @@ import { api } from '../api.js'; -import { escapeHtml } from '../utils.js'; +import { escapeHtml, showAlert, hideAlert } from '../utils.js'; export async function initSchedule() { const tbody = document.getElementById('schedule-tbody'); @@ -20,7 +20,6 @@ export async function initSchedule() { // ===================== Фильтрация ===================== - // Извлечение отображаемого значения поля для фильтрации function getDisplayValue(lesson, key) { switch (key) { case 'teacher': @@ -38,20 +37,17 @@ export async function initSchedule() { } } - // Собрать уникальные значения из данных function getUniqueValues(key) { const vals = new Set(); lessonsData.forEach(lesson => { vals.add(getDisplayValue(lesson, key)); }); - // Для дней — сортируем по порядку if (key === 'day') { return [...vals].sort((a, b) => (dayOrder[a.toLowerCase()] ?? 99) - (dayOrder[b.toLowerCase()] ?? 99)); } return [...vals].sort((a, b) => a.localeCompare(b, 'ru')); } - // Применить все фильтры function applyFilters(lessons) { return lessons.filter(lesson => { for (const key of Object.keys(activeFilters)) { @@ -79,7 +75,6 @@ export async function initSchedule() { function onDocumentClick(e) { if (currentPopup && !currentPopup.contains(e.target)) { - // Проверяем, не кликнули ли по иконке фильтра if (!e.target.closest('.filter-icon')) { closePopup(); } @@ -87,7 +82,6 @@ export async function initSchedule() { } function openFilterPopup(th, filterKey) { - // Если уже открыт этот же — закрыть if (currentPopup && currentPopup.dataset.filterKey === filterKey) { closePopup(); return; @@ -97,19 +91,16 @@ export async function initSchedule() { const uniqueValues = getUniqueValues(filterKey); const currentFilter = activeFilters[filterKey]; - // Создаём попап const popup = document.createElement('div'); popup.className = 'filter-popup'; popup.dataset.filterKey = filterKey; - // Поисковое поле const searchInput = document.createElement('input'); searchInput.type = 'text'; searchInput.className = 'filter-search'; searchInput.placeholder = 'Поиск...'; popup.appendChild(searchInput); - // Кнопки «Выбрать все» / «Сбросить» const btnRow = document.createElement('div'); btnRow.className = 'filter-btn-row'; @@ -133,7 +124,6 @@ export async function initSchedule() { btnRow.appendChild(btnNone); popup.appendChild(btnRow); - // Список чекбоксов const listWrap = document.createElement('div'); listWrap.className = 'filter-list'; @@ -146,7 +136,6 @@ export async function initSchedule() { const cb = document.createElement('input'); cb.type = 'checkbox'; cb.value = val; - // Если фильтр активен — отмечаем только выбранные; если нет — все отмечены cb.checked = currentFilter ? currentFilter.has(val) : true; const span = document.createElement('span'); @@ -160,7 +149,6 @@ export async function initSchedule() { popup.appendChild(listWrap); - // Кнопка «Применить» const btnApply = document.createElement('button'); btnApply.className = 'filter-btn-apply'; btnApply.textContent = 'Применить'; @@ -171,7 +159,6 @@ export async function initSchedule() { if (cb.checked) selected.add(cb.value); }); - // Если все выбраны — снимаем фильтр if (selected.size === uniqueValues.length) { delete activeFilters[filterKey]; th.classList.remove('filter-active'); @@ -185,7 +172,6 @@ export async function initSchedule() { }); popup.appendChild(btnApply); - // Поиск по чекбоксам searchInput.addEventListener('input', () => { const query = searchInput.value.toLowerCase(); listWrap.querySelectorAll('.filter-item').forEach(item => { @@ -194,28 +180,22 @@ export async function initSchedule() { }); }); - // Предотвращаем всплытие кликов внутри попапа (чтобы не срабатывала сортировка th) popup.addEventListener('click', (e) => e.stopPropagation()); searchInput.addEventListener('click', (e) => e.stopPropagation()); - // Позиционируем попап под th th.style.position = 'relative'; th.appendChild(popup); currentPopup = popup; - // Фокус на поиск setTimeout(() => searchInput.focus(), 50); - // Закрытие по клику вне setTimeout(() => { document.addEventListener('click', onDocumentClick, true); }, 10); } - // Обработчики кликов по заголовкам с фильтрами (клик по всей ячейке) table.querySelectorAll('thead th.filterable').forEach(th => { th.addEventListener('click', (e) => { - // Не открываем попап при клике внутри самого попапа if (e.target.closest('.filter-popup')) return; const filterKey = th.dataset.filterKey; openFilterPopup(th, filterKey); @@ -249,7 +229,6 @@ export async function initSchedule() { case 'week': return (lesson.week || '').toLowerCase(); case 'time': { - // Составной ключ: день + время для правильной сортировки const d = (lesson.day || '').toLowerCase(); const dayNum = dayOrder[d] ?? 99; const t = lesson.time || '99:99'; @@ -287,10 +266,8 @@ export async function initSchedule() { }); } - // Навешиваем обработчики клика на заголовки (сортировка) table.querySelectorAll('thead th.sortable').forEach(th => { th.addEventListener('click', (e) => { - // Не сортируем, если кликнули по иконке фильтра или внутри попапа if (e.target.closest('.filter-icon') || e.target.closest('.filter-popup')) return; const key = th.dataset.sortKey; @@ -310,7 +287,7 @@ export async function initSchedule() { }); }); - // ===================== Загрузка и рендер ===================== + // ===================== Загрузка и рендер таблицы ===================== async function loadSchedule() { try { @@ -318,21 +295,20 @@ export async function initSchedule() { lessonsData = lessons; renderSchedule(lessons); } catch (e) { - tbody.innerHTML = `Ошибка загрузки: ${escapeHtml(e.message)}`; + tbody.innerHTML = `Ошибка загрузки: ${escapeHtml(e.message)}`; } } function renderSchedule(lessons) { if (!lessons || !lessons.length) { - tbody.innerHTML = 'Нет занятий'; + tbody.innerHTML = 'Нет занятий'; return; } - // Сначала фильтруем, потом сортируем const filtered = applyFilters(lessons); if (!filtered.length) { - tbody.innerHTML = 'Нет занятий по выбранным фильтрам'; + tbody.innerHTML = 'Нет занятий по выбранным фильтрам'; return; } @@ -366,5 +342,343 @@ export async function initSchedule() { }).join(''); } - await loadSchedule(); + // ===================== Модалки добавления занятия ===================== + + const overlay = document.getElementById('sch-overlay'); + const modalForm = document.getElementById('sch-modal-form'); + const modalLessons = document.getElementById('sch-modal-lessons'); + const btnAddLesson = document.getElementById('sch-btn-add-lesson'); + const btnClose = document.getElementById('sch-modal-close'); + const addForm = document.getElementById('sch-add-lesson-form'); + + const schTeacherSelect = document.getElementById('sch-teacher'); + const schGroupSelect = document.getElementById('sch-group'); + const schDisciplineSelect = document.getElementById('sch-discipline'); + const schClassroomSelect = document.getElementById('sch-classroom'); + const schDaySelect = document.getElementById('sch-day'); + const schTimeSelect = document.getElementById('sch-time'); + const schTypeSelect = document.getElementById('sch-type'); + const schWeekUpper = document.getElementById('sch-week-upper'); + const schWeekLower = document.getElementById('sch-week-lower'); + const schFormatOffline = document.getElementById('sch-format-offline'); + + const schTeacherName = document.getElementById('sch-teacher-name'); + const schLessonsContainer = document.getElementById('sch-lessons-container'); + + let groups = []; + let subjects = []; + let classrooms = []; + let teachers = []; + + const weekdaysTimes = [ + "8:00-9:30", "9:40-11:10", "11:40-13:10", + "13:20-14:50", "15:00-16:30", "16:50-18:20", "18:30-19:00" + ]; + const saturdayTimes = [ + "8:20-9:50", "10:00-11:30", "11:40-13:10", "13:20-14:50" + ]; + + // ===== Загрузка справочников ===== + async function loadGroups() { + try { + groups = await api.get('/api/groups'); + schGroupSelect.innerHTML = '' + + groups.map(g => { + let text = escapeHtml(g.name); + if (g.groupSize) text += ` (числ: ${g.groupSize} чел.)`; + return ``; + }).join(''); + } catch (e) { console.error('Ошибка загрузки групп:', e); } + } + + async function loadSubjects() { + try { + subjects = await api.get('/api/subjects'); + schDisciplineSelect.innerHTML = '' + + subjects.map(s => ``).join(''); + } catch (e) { console.error('Ошибка загрузки дисциплин:', e); } + } + + async function loadClassrooms() { + try { + classrooms = await api.get('/api/classrooms'); + renderClassroomOptions(); + } catch (e) { console.error('Ошибка загрузки аудиторий:', e); } + } + + async function loadTeachers() { + try { + teachers = await api.get('/api/users/teachers'); + schTeacherSelect.innerHTML = '' + + teachers.map(t => ``).join(''); + } catch (e) { console.error('Ошибка загрузки преподавателей:', e); } + } + + function renderClassroomOptions() { + if (!classrooms || classrooms.length === 0) { + schClassroomSelect.innerHTML = ''; + return; + } + const selectedGroupId = schGroupSelect.value; + const selectedGroup = groups?.find(g => g.id == selectedGroupId); + const groupSize = selectedGroup?.groupSize || 0; + + schClassroomSelect.innerHTML = '' + + classrooms.map(c => { + let text = escapeHtml(c.name); + if (c.capacity) text += ` (вместимость: ${c.capacity} чел.)`; + if (c.isAvailable === false) { + text += ` ❌ Занята`; + } else if (selectedGroupId && groupSize > 0 && c.capacity && groupSize > c.capacity) { + text += ` ⚠️ Недостаточно места`; + } + return ``; + }).join(''); + } + + schGroupSelect.addEventListener('change', () => renderClassroomOptions()); + + function updateTimeOptions(dayValue) { + let times = []; + if (dayValue === "Суббота") { + times = saturdayTimes; + } else if (dayValue && dayValue !== '') { + times = weekdaysTimes; + } else { + schTimeSelect.innerHTML = ''; + schTimeSelect.disabled = true; + return; + } + schTimeSelect.innerHTML = '' + + times.map(t => ``).join(''); + schTimeSelect.disabled = false; + } + + schDaySelect.addEventListener('change', function () { + updateTimeOptions(this.value); + }); + + // ===== Автозаполнение преподавателя из фильтра ===== + function getFilteredTeacherId() { + const teacherFilter = activeFilters['teacher']; + if (teacherFilter && teacherFilter.size === 1) { + const teacherName = [...teacherFilter][0]; + // Сопоставляем по username, fullName и их комбинациям + const match = teachers.find(t => + t.username === teacherName || + t.fullName === teacherName || + (t.fullName || t.username) === teacherName + ); + return match ? String(match.id) : ''; + } + return ''; + } + + // ===== Загрузка занятий преподавателя ===== + async function loadTeacherLessons(teacherId) { + const teacher = teachers.find(t => t.id == teacherId); + const name = teacher ? (teacher.fullName || teacher.username) : ''; + schTeacherName.textContent = name + ? `Занятия преподавателя: ${name}` + : 'Занятия преподавателя'; + + modalLessons.style.display = ''; + schLessonsContainer.innerHTML = '
Загрузка занятий...
'; + + try { + const lessons = await api.get(`/api/users/lessons/${teacherId}`); + + if (!lessons || !Array.isArray(lessons) || lessons.length === 0) { + schLessonsContainer.innerHTML = '
У преподавателя пока нет занятий
'; + return; + } + + const daysOrder = ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота']; + const lessonsByDay = {}; + lessons.forEach(l => { + if (!lessonsByDay[l.day]) lessonsByDay[l.day] = []; + lessonsByDay[l.day].push(l); + }); + Object.keys(lessonsByDay).forEach(day => { + lessonsByDay[day].sort((a, b) => a.time.localeCompare(b.time)); + }); + + let html = ''; + daysOrder.forEach(day => { + if (!lessonsByDay[day]) return; + html += `
${day}
`; + lessonsByDay[day].forEach(lesson => { + html += ` +
+
+ ${escapeHtml(lesson.groupName)} + ${escapeHtml(lesson.time)} +
+
+
${escapeHtml(lesson.subjectName)}
+
+ ${escapeHtml(lesson.typeLesson)} + ${escapeHtml(lesson.lessonFormat)} + ${escapeHtml(lesson.week)} + ${escapeHtml(lesson.classroomName)} +
+
+
`; + }); + }); + + schLessonsContainer.innerHTML = html; + } catch (e) { + schLessonsContainer.innerHTML = `
Ошибка загрузки: ${escapeHtml(e.message)}
`; + } + } + + // ===== При смене преподавателя — подгрузить его занятия ===== + schTeacherSelect.addEventListener('change', function () { + const teacherId = this.value; + if (teacherId) { + loadTeacherLessons(teacherId); + } else { + modalLessons.style.display = 'none'; + schLessonsContainer.innerHTML = '
Выберите преподавателя для просмотра занятий
'; + } + }); + + // ===== Открытие / закрытие оверлея ===== + function openOverlay() { + // Автозаполнение преподавателя из фильтра таблицы + const autoTeacherId = getFilteredTeacherId(); + if (autoTeacherId) { + schTeacherSelect.value = autoTeacherId; + loadTeacherLessons(autoTeacherId); + } + + overlay.classList.add('open'); + } + + function closeOverlay() { + overlay.classList.remove('open'); + resetForm(); + } + + function resetForm() { + addForm.reset(); + schTeacherSelect.value = ''; + schGroupSelect.value = ''; + schDisciplineSelect.value = ''; + schClassroomSelect.value = ''; + schDaySelect.value = ''; + schTypeSelect.value = ''; + schTimeSelect.innerHTML = ''; + schTimeSelect.disabled = true; + if (schWeekUpper) schWeekUpper.checked = false; + if (schWeekLower) schWeekLower.checked = false; + if (schFormatOffline) schFormatOffline.checked = true; + modalLessons.style.display = 'none'; + schLessonsContainer.innerHTML = '
Выберите преподавателя для просмотра занятий
'; + hideAlert('sch-add-alert'); + } + + btnAddLesson.addEventListener('click', openOverlay); + btnClose.addEventListener('click', closeOverlay); + + // Закрытие по клику на оверлей (мимо модалок) + overlay.addEventListener('click', (e) => { + if (e.target === overlay || e.target.classList.contains('cs-overlay-scroll')) { + closeOverlay(); + } + }); + + // Закрытие по Escape + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && overlay.classList.contains('open')) { + closeOverlay(); + } + }); + + // ===== Отправка формы ===== + addForm.addEventListener('submit', async (e) => { + e.preventDefault(); + hideAlert('sch-add-alert'); + + const teacherId = schTeacherSelect.value; + const groupId = schGroupSelect.value; + const subjectId = schDisciplineSelect.value; + const classroomId = schClassroomSelect.value; + const lessonType = schTypeSelect.value; + const dayOfWeek = schDaySelect.value; + const timeSlot = schTimeSelect.value; + const lessonFormat = document.querySelector('input[name="schLessonFormat"]:checked')?.value; + + if (!teacherId) { showAlert('sch-add-alert', 'Выберите преподавателя', 'error'); return; } + if (!groupId) { showAlert('sch-add-alert', 'Выберите группу', 'error'); return; } + if (!subjectId) { showAlert('sch-add-alert', 'Выберите дисциплину', 'error'); return; } + if (!classroomId) { showAlert('sch-add-alert', 'Выберите аудиторию', 'error'); return; } + if (!dayOfWeek) { showAlert('sch-add-alert', 'Выберите день недели', 'error'); return; } + if (!timeSlot) { showAlert('sch-add-alert', 'Выберите время', 'error'); return; } + + const weekUpperChecked = schWeekUpper?.checked || false; + const weekLowerChecked = schWeekLower?.checked || false; + + if (!weekUpperChecked && !weekLowerChecked) { + showAlert('sch-add-alert', 'Не выбран тип недели', 'error'); + return; + } + + let weekType = null; + if (weekUpperChecked && weekLowerChecked) weekType = 'Обе'; + else if (weekUpperChecked) weekType = 'Верхняя'; + else if (weekLowerChecked) weekType = 'Нижняя'; + + try { + await api.post('/api/users/lessons/create', { + teacherId: parseInt(teacherId), + groupId: parseInt(groupId), + subjectId: parseInt(subjectId), + classroomId: parseInt(classroomId), + typeLesson: lessonType, + lessonFormat: lessonFormat, + day: dayOfWeek, + week: weekType, + time: timeSlot + }); + + showAlert('sch-add-alert', 'Занятие добавлено ✓', 'success'); + + // Очистить все поля кроме преподавателя (для массового добавления) + schGroupSelect.selectedIndex = 0; + schDisciplineSelect.selectedIndex = 0; + schClassroomSelect.selectedIndex = 0; + schTypeSelect.selectedIndex = 0; + schDaySelect.selectedIndex = 0; + schTimeSelect.innerHTML = ''; + schTimeSelect.disabled = true; + schWeekUpper.checked = false; + schWeekLower.checked = false; + document.querySelector('input[name="schLessonFormat"][value="Очно"]').checked = true; + + // Обновить занятия преподавателя в модалке 2 + if (teacherId) { + await loadTeacherLessons(teacherId); + } + + // Обновить основную таблицу + await loadSchedule(); + + setTimeout(() => { + hideAlert('sch-add-alert'); + }, 4000); + } catch (err) { + showAlert('sch-add-alert', err.message || 'Ошибка добавления занятия', 'error'); + } + }); + + // ===================== Инициализация ===================== + await Promise.all([ + loadSchedule(), + loadGroups(), + loadSubjects(), + loadClassrooms(), + loadTeachers() + ]); } \ No newline at end of file diff --git a/frontend/admin/js/views/users.js b/frontend/admin/js/views/users.js index e77ee8c..2189442 100755 --- a/frontend/admin/js/views/users.js +++ b/frontend/admin/js/views/users.js @@ -7,7 +7,9 @@ const ROLE_BADGE = { ADMIN: 'badge-admin', TEACHER: 'badge-teacher', STUDENT: 'b export async function initUsers() { const usersTbody = document.getElementById('users-tbody'); const createForm = document.getElementById('create-form'); - const modalBackdrop = document.getElementById('modal-backdrop'); + + // ===== Оверлей (cs-overlay) ===== + const usersOverlay = document.getElementById('users-overlay'); // ===== 1-е модальное окно: Добавить занятие ===== const modalAddLesson = document.getElementById('modal-add-lesson'); @@ -28,7 +30,6 @@ export async function initUsers() { // ===== 2-е модальное окно: Просмотр занятий ===== const modalViewLessons = document.getElementById('modal-view-lessons'); - const modalViewLessonsClose = document.getElementById('modal-view-lessons-close'); const lessonsContainer = document.getElementById('lessons-container'); const modalTeacherName = document.getElementById('modal-teacher-name'); @@ -56,36 +57,6 @@ export async function initUsers() { "13:20-14:50" ]; - // ========================================================= - // СИНХРОНИЗАЦИЯ ВЫСОТЫ 1-й МОДАЛКИ -> CSS переменная - // ========================================================= - const addLessonContent = document.querySelector('#modal-add-lesson .modal-content'); - - function setAddLessonHeightVar(px) { - const h = Math.max(0, Math.ceil(px || 0)); - document.documentElement.style.setProperty('--add-lesson-height', `${h}px`); - } - - function syncAddLessonHeight() { - if (!addLessonContent) return; - - if (!modalAddLesson?.classList.contains('open')) { - // если первая модалка закрыта — "шапки" нет - setAddLessonHeightVar(0); - return; - } - - setAddLessonHeightVar(addLessonContent.getBoundingClientRect().height); - } - - // Авто-обновление при любом изменении размеров первой модалки - if (addLessonContent && 'ResizeObserver' in window) { - const ro = new ResizeObserver(() => syncAddLessonHeight()); - ro.observe(addLessonContent); - } - - window.addEventListener('resize', () => syncAddLessonHeight()); - // ========================================================= // Загрузка справочников // ========================================================= @@ -225,25 +196,15 @@ export async function initUsers() { `).join(''); } - function updateBackdrop() { - if(!modalBackdrop) return; - const anyOpen = - modalAddLesson?.classList.contains('open') || - modalViewLessons?.classList.contains('open'); - - modalBackdrop.classList.toggle('open', anyOpen); + // ===== Открытие / закрытие оверлея ===== + function openOverlay() { + if (usersOverlay) usersOverlay.classList.add('open'); } - // Клик мимо модалок закроет их, если не надо, то закомментить этот код - modalBackdrop?.addEventListener('click', () => { - if (modalAddLesson?.classList.contains('open')) { - modalAddLesson.classList.remove('open'); + function closeOverlay() { + if (usersOverlay) usersOverlay.classList.remove('open'); + if (modalViewLessons) modalViewLessons.style.display = 'none'; resetLessonForm(); - syncAddLessonHeight(); - } - if (modalViewLessons?.classList.contains('open')) { - closeViewLessonsModal(); - } - }); + } // ========================================================= // 1-я модалка: добавление занятия @@ -270,9 +231,7 @@ export async function initUsers() { lessonDaySelect.value = ''; updateTimeOptions(''); - modalAddLesson.classList.add('open'); - updateBackdrop(); - requestAnimationFrame(() => syncAddLessonHeight()); + openOverlay(); } addLessonForm.addEventListener('submit', async (e) => { @@ -289,15 +248,20 @@ export async function initUsers() { const lessonFormat = document.querySelector('input[name="lessonFormat"]:checked')?.value; - if (!groupId) { showAlert('add-lesson-alert', 'Выберите группу', 'error'); requestAnimationFrame(() => syncAddLessonHeight()); return; } - if (!subjectId) { showAlert('add-lesson-alert', 'Выберите дисциплину', 'error'); requestAnimationFrame(() => syncAddLessonHeight()); return; } - if (!classroomId) { showAlert('add-lesson-alert', 'Выберите аудиторию', 'error'); requestAnimationFrame(() => syncAddLessonHeight()); return; } - if (!dayOfWeek) { showAlert('add-lesson-alert', 'Выберите день недели', 'error'); requestAnimationFrame(() => syncAddLessonHeight()); return; } - if (!timeSlot) { showAlert('add-lesson-alert', 'Выберите время', 'error'); requestAnimationFrame(() => syncAddLessonHeight()); return; } + if (!groupId) { showAlert('add-lesson-alert', 'Выберите группу', 'error'); return; } + if (!subjectId) { showAlert('add-lesson-alert', 'Выберите дисциплину', 'error'); return; } + if (!classroomId) { showAlert('add-lesson-alert', 'Выберите аудиторию', 'error'); return; } + if (!dayOfWeek) { showAlert('add-lesson-alert', 'Выберите день недели', 'error'); return; } + if (!timeSlot) { showAlert('add-lesson-alert', 'Выберите время', 'error'); return; } const weekUpperChecked = weekUpper?.checked || false; const weekLowerChecked = weekLower?.checked || false; + if (!weekUpperChecked && !weekLowerChecked) { + showAlert('add-lesson-alert', 'Не выбран тип недели', 'error'); + return; + } + let weekType = null; if (weekUpperChecked && weekLowerChecked) weekType = 'Обе'; else if (weekUpperChecked) weekType = 'Верхняя'; @@ -316,57 +280,45 @@ export async function initUsers() { time: timeSlot }); - if (modalViewLessons?.classList.contains('open') && currentLessonsTeacherId == userId) { + if (modalViewLessons?.style.display !== 'none' && currentLessonsTeacherId == userId) { await loadTeacherLessons(currentLessonsTeacherId, currentLessonsTeacherName); } - showAlert('add-lesson-alert', 'Занятие добавлено', 'success'); + showAlert('add-lesson-alert', 'Занятие добавлено ✓', 'success'); - lessonGroupSelect.value = ''; - lessonDisciplineSelect.value = ''; - lessonClassroomSelect.value = ''; - lessonTypeSelect.value = ''; - lessonDaySelect.value = ''; - lessonTimeSelect.value = ''; + lessonGroupSelect.selectedIndex = 0; + lessonDisciplineSelect.selectedIndex = 0; + lessonClassroomSelect.selectedIndex = 0; + lessonTypeSelect.selectedIndex = 0; + lessonDaySelect.selectedIndex = 0; + lessonTimeSelect.innerHTML = ''; lessonTimeSelect.disabled = true; weekUpper.checked = false; weekLower.checked = false; document.querySelector('input[name="lessonFormat"][value="Очно"]').checked = true; - requestAnimationFrame(() => syncAddLessonHeight()); - setTimeout(() => { hideAlert('add-lesson-alert'); - syncAddLessonHeight(); }, 3000); } catch (err) { showAlert('add-lesson-alert', err.message || 'Ошибка добавления занятия', 'error'); - requestAnimationFrame(() => syncAddLessonHeight()); } }); lessonDaySelect.addEventListener('change', function () { updateTimeOptions(this.value); - requestAnimationFrame(() => syncAddLessonHeight()); }); if (modalAddLessonClose) { - modalAddLessonClose.addEventListener('click', () => { - modalAddLesson.classList.remove('open'); - resetLessonForm(); - syncAddLessonHeight(); - updateBackdrop(); - }); + modalAddLessonClose.addEventListener('click', () => closeOverlay()); } - if (modalAddLesson) { - modalAddLesson.addEventListener('click', (e) => { - if (e.target === modalAddLesson) { - modalAddLesson.classList.remove('open'); - resetLessonForm(); - syncAddLessonHeight(); - updateBackdrop(); + // Клик по оверлею (мимо модалок) закрывает всё + if (usersOverlay) { + usersOverlay.querySelector('.cs-overlay-scroll')?.addEventListener('click', (e) => { + if (e.target.classList.contains('cs-overlay-scroll')) { + closeOverlay(); } }); } @@ -481,48 +433,20 @@ export async function initUsers() { currentLessonsTeacherId = teacherId; currentLessonsTeacherName = teacherName || ''; + if (modalViewLessons) modalViewLessons.style.display = ''; loadTeacherLessons(teacherId, teacherName); - - requestAnimationFrame(() => syncAddLessonHeight()); - - modalViewLessons.classList.add('open'); - updateBackdrop(); -// document.body.style.overflow = 'hidden'; } function closeViewLessonsModal() { - modalViewLessons.classList.remove('open'); - updateBackdrop(); -// document.body.style.overflow = ''; - + if (modalViewLessons) modalViewLessons.style.display = 'none'; currentLessonsTeacherId = null; currentLessonsTeacherName = ''; } - if (modalViewLessonsClose) { - modalViewLessonsClose.addEventListener('click', closeViewLessonsModal); - } - - if (modalViewLessons) { - modalViewLessons.addEventListener('click', (e) => { - if (e.target === modalViewLessons) closeViewLessonsModal(); - }); - } - document.addEventListener('keydown', (e) => { if (e.key !== 'Escape') return; - - if (modalAddLesson?.classList.contains('open')) { - modalAddLesson.classList.remove('open'); - resetLessonForm(); - syncAddLessonHeight(); - updateBackdrop(); - return; - } - - if (modalViewLessons?.classList.contains('open')) { - closeViewLessonsModal(); - return; + if (usersOverlay?.classList.contains('open')) { + closeOverlay(); } }); diff --git a/frontend/admin/views/schedule.html b/frontend/admin/views/schedule.html index 35d4701..8104356 100755 --- a/frontend/admin/views/schedule.html +++ b/frontend/admin/views/schedule.html @@ -1,5 +1,8 @@
-

Расписание занятий

+
+

Расписание занятий

+ +
@@ -35,9 +38,142 @@ - +
Загрузка...Загрузка...
+
+ + +
+
+ + +
+
+

Добавить занятие

+ +
+ +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+ + +
+
+ + +
+ + +
+ + +
+ +
+ + +
+
+ + +
+ + +
+ + +
+ +
+ +
+ + +
+
+ + + + +
\ No newline at end of file diff --git a/frontend/admin/views/users.html b/frontend/admin/views/users.html index 199750e..bf282f3 100755 --- a/frontend/admin/views/users.html +++ b/frontend/admin/views/users.html @@ -1,187 +1,189 @@ - -
-

Новый пользователь

-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- -
- -
-
- -
-

Все пользователи

-
- - - - - - - - - - - - - - - - - -
IDИмя пользователяФИОДолжностьКафедраРольДействия
Загрузка...
-
-
- - - - \ No newline at end of file + +
+

Новый пользователь

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+ +
+

Все пользователи

+
+ + + + + + + + + + + + + + + + + +
IDИмя пользователяФИОДолжностьКафедраРольДействия
Загрузка...
+
+
+ + +
+
+ + + + + + + +
+
\ No newline at end of file