import { api } from '../api.js'; import { escapeHtml, showAlert, hideAlert } from '../utils.js'; export async function initDepartment() { const form = document.getElementById('department-schedule-form'); const departmentSelect = document.getElementById('filter-department'); const container = document.getElementById('schedule-blocks-container'); let departments = []; // Загрузка кафедр try { departments = await api.get('/api/departments'); departmentSelect.innerHTML = '' + departments.map(d => ``).join(''); } catch (e) { departmentSelect.innerHTML = ''; } form.addEventListener('submit', async (e) => { e.preventDefault(); hideAlert('schedule-form-alert'); const departmentId = departmentSelect.value; const period = document.getElementById('filter-period').value; const semesterType = document.querySelector('input[name="semesterType"]:checked')?.value; if (!departmentId || !period || !semesterType) { showAlert('schedule-form-alert', 'Заполните все поля', 'error'); return; } const deptName = departmentSelect.options[departmentSelect.selectedIndex].text; try { const params = new URLSearchParams({ departmentId, semesterType, period }); const data = await api.get(`/api/department/schedule?${params.toString()}`); const semesterName = semesterType === 'spring' ? 'весенний' : (semesterType === 'autumn' ? 'осенний' : semesterType); const periodName = period.replace('-', '/'); renderScheduleBlock(deptName, semesterName, periodName, data); form.reset(); } catch (err) { showAlert('schedule-form-alert', err.message || 'Ошибка загрузки данных', 'error'); } }); function renderScheduleBlock(deptName, groupSemester, period, schedule) { const details = document.createElement('details'); details.className = 'table-item'; details.open = true; details.innerHTML = `
Данные к составлению расписания Кафедра: ${escapeHtml(deptName)} Семестр: ${escapeHtml(groupSemester)} Уч. год: ${escapeHtml(period)}
${schedule ? schedule.length : 0} записей
${renderRows(schedule)}
Специальность Курс/семестр Группа Дисциплина Вид занятий Часов в неделю Деление на подгруппы Преподаватель
`; container.prepend(details); } function renderRows(schedule) { if (!schedule || schedule.length === 0) { return 'Нет данных'; } return schedule.map(r => ` ${escapeHtml(r.specialityCode || '-')} ${(() => { const course = r.groupCourse || '-'; const groupSemester = r.groupSemester || '-'; if (course === '-' && groupSemester === '-') return '-'; return `${course} | ${groupSemester}`; })()} ${escapeHtml(r.groupName || '-')} ${escapeHtml(r.subjectName || '-')} ${escapeHtml(r.lessonType || '-')} ${escapeHtml(r.numberOfHours || '-')} ${r.division === true ? '✓' : ''} ${(() => { const jobTitle = r.teacherJobTitle || '-'; const teacherName = r.teacherName || '-'; if (jobTitle === '-' && teacherName === '-') return '-'; return `${jobTitle}, ${teacherName}`; })()} `).join(''); } // ========================================================= // ЛОГИКА ДЛЯ ФУНКЦИОНАЛА "СОЗДАТЬ ЗАПИСЬ (К/Ф)" // Два модальных окна поверх всего контента в одном оверлее // ========================================================= const btnCreateSchedule = document.getElementById('btn-create-schedule'); const csOverlay = document.getElementById('cs-overlay'); const modalCreateSchedule = document.getElementById('modal-create-schedule'); const modalCreateScheduleClose = document.getElementById('modal-create-schedule-close'); const formCreateSchedule = document.getElementById('create-schedule-form'); const modalViewSchedules = document.getElementById('modal-view-schedules'); const btnSaveSchedules = document.getElementById('btn-save-schedules'); const preparedSchedulesTbody = document.getElementById('prepared-schedules-tbody'); const csGroupSelect = document.getElementById('cs-group'); const csSubjectSelect = document.getElementById('cs-subject'); const csTeacherSelect = document.getElementById('cs-teacher'); const csDepartmentIdInput = document.getElementById('cs-department-id'); let preparedSchedules = []; let csGroups = []; let csSubjects = []; let csTeachers = []; const SEMESTER_LABELS = { autumn: 'Осенний', spring: 'Весенний' }; const LESSON_TYPE_LABELS = { 1: 'Лекция', 2: 'Практическая работа', 3: 'Лабораторная работа' }; const localDepartmentId = localStorage.getItem('departmentId'); // ===== Загрузка справочников ===== async function loadDictionariesForSchedule() { try { csGroups = await api.get('/api/groups'); csGroupSelect.innerHTML = '' + csGroups.map(g => ``).join(''); csSubjects = await api.get('/api/subjects'); csSubjectSelect.innerHTML = '' + csSubjects.map(s => ``).join(''); if (localDepartmentId) { csTeachers = await api.get(`/api/users/teachers/${localDepartmentId}`); csTeacherSelect.innerHTML = '' + csTeachers.map(t => ``).join(''); } else { csTeacherSelect.innerHTML = ''; } } catch (e) { console.error('Ошибка загрузки справочников:', e); } } loadDictionariesForSchedule(); // ===== Открытие / Закрытие оверлея ===== function openOverlay() { csOverlay.classList.add('open'); document.body.style.overflow = 'hidden'; // Предотвращаем скролл страницы } function closeOverlay() { csOverlay.classList.remove('open'); document.body.style.overflow = ''; hideAlert('create-schedule-alert'); hideAlert('save-schedules-alert'); } function updateTableVisibility() { modalViewSchedules.style.display = preparedSchedules.length > 0 ? '' : 'none'; } // ===== Кнопка «Создать запись» ===== btnCreateSchedule.addEventListener('click', () => { if (localDepartmentId) { csDepartmentIdInput.value = localDepartmentId; } else { showAlert('schedule-form-alert', 'Требуется перезайти (отсутствует ID кафедры)', 'error'); return; } openOverlay(); }); // ===== Закрытие ===== modalCreateScheduleClose.addEventListener('click', closeOverlay); csOverlay.addEventListener('click', (e) => { // Закрыть по клику на затемнённый фон (но не по клику на содержимое модалок) if (e.target === csOverlay || e.target.classList.contains('cs-overlay-scroll')) { closeOverlay(); } }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && csOverlay.classList.contains('open')) { closeOverlay(); } }); // ===== Рендер таблицы ===== function renderPreparedSchedules() { if (preparedSchedules.length === 0) { preparedSchedulesTbody.innerHTML = 'Нет записей'; return; } preparedSchedulesTbody.innerHTML = preparedSchedules.map((s, index) => { const groupName = csGroups.find(g => g.id == s.groupId)?.name || s.groupId; const subjectName = csSubjects.find(sub => sub.id == s.subjectsId)?.name || s.subjectsId; const teacherName = csTeachers.find(t => t.id == s.teacherId)?.fullName || csTeachers.find(t => t.id == s.teacherId)?.username || s.teacherId; const lessonTypeName = LESSON_TYPE_LABELS[s.lessonTypeId] || 'Неизвестно'; const semLabel = SEMESTER_LABELS[s.semesterType] || s.semesterType; const periodDisplay = s.period.replace('-', '/'); const divText = s.division ? '✓' : ''; const hasError = !!s._errorMsg; const rowStyle = hasError ? ' style="background: rgba(239, 68, 68, 0.08);"' : ''; let row = ` ${escapeHtml(periodDisplay)} ${escapeHtml(semLabel)} ${s.groupSemester} ${escapeHtml(String(groupName))} ${escapeHtml(String(subjectName))} ${escapeHtml(lessonTypeName)} ${s.numberOfHours} ${divText} ${escapeHtml(String(teacherName))} `; if (hasError) { row += ` ⚠ ${escapeHtml(s._errorMsg)} `; } return row; }).join(''); } // ===== Удаление строки из таблицы ===== preparedSchedulesTbody.addEventListener('click', (e) => { if (e.target.classList.contains('btn-delete')) { const idx = parseInt(e.target.getAttribute('data-index'), 10); preparedSchedules.splice(idx, 1); renderPreparedSchedules(); updateTableVisibility(); } }); // ===== Очистка полей формы (частичная) ===== // НЕ очищаем select'ы — они остаются заполненными для удобства. // Пользователь сам изменит нужные поля для следующей записи. function clearFormFields() { document.getElementById('cs-hours').value = ''; document.getElementById('cs-division').checked = false; } // ===== Добавление записи в список ===== formCreateSchedule.addEventListener('submit', (e) => { e.preventDefault(); hideAlert('create-schedule-alert'); const depId = csDepartmentIdInput.value; const period = document.getElementById('cs-period').value; const semesterType = document.querySelector('input[name="csSemesterType"]:checked')?.value; const groupSemester = document.getElementById('cs-semester').value; const groupId = csGroupSelect.value; const subjectId = csSubjectSelect.value; const lessonTypeId = document.getElementById('cs-lesson-type').value; const hours = document.getElementById('cs-hours').value; const division = document.getElementById('cs-division').checked; const teacherId = csTeacherSelect.value; if (!period || !semesterType || !groupId || !subjectId || !lessonTypeId || !hours || !teacherId) { showAlert('create-schedule-alert', 'Заполните все обязательные поля', 'error'); return; } const newRecord = { departmentId: Number(depId), groupSemester: Number(groupSemester), groupId: Number(groupId), subjectsId: Number(subjectId), lessonTypeId: Number(lessonTypeId), numberOfHours: Number(hours), division: division, teacherId: Number(teacherId), semesterType: semesterType, period: period }; // Проверка на дубликат в уже добавленных записях const isDuplicate = preparedSchedules.some(s => s.period === newRecord.period && s.semesterType === newRecord.semesterType && s.groupSemester === newRecord.groupSemester && s.groupId === newRecord.groupId && s.subjectsId === newRecord.subjectsId && s.lessonTypeId === newRecord.lessonTypeId && s.numberOfHours === newRecord.numberOfHours && s.division === newRecord.division && s.teacherId === newRecord.teacherId ); if (isDuplicate) { showAlert('create-schedule-alert', 'Такая запись уже есть в списке', 'error'); return; } preparedSchedules.push(newRecord); clearFormFields(); showAlert('create-schedule-alert', 'Запись добавлена ✓', 'success'); setTimeout(() => hideAlert('create-schedule-alert'), 2000); renderPreparedSchedules(); updateTableVisibility(); }); // ===== Сохранение в БД ===== btnSaveSchedules.addEventListener('click', async () => { if (preparedSchedules.length === 0) { showAlert('save-schedules-alert', 'Нет записей для сохранения', 'error'); return; } btnSaveSchedules.disabled = true; btnSaveSchedules.textContent = 'Сохранение...'; hideAlert('save-schedules-alert'); let errors = 0; let saved = 0; const failedRecords = []; for (const record of preparedSchedules) { try { await api.post('/api/department/schedule/create', record); saved++; } catch (err) { console.error('Ошибка сохранения записи:', err); errors++; // Помечаем запись как дубликат, если бэк вернул соответствующую ошибку const isDuplicate = err.status === 409 || (err.message && err.message.toLowerCase().includes('уже существует')); failedRecords.push({ ...record, _errorMsg: isDuplicate ? 'Такая запись уже есть в базе данных' : (err.message || 'Ошибка сохранения') }); } } btnSaveSchedules.disabled = false; btnSaveSchedules.textContent = 'Сохранить в БД'; if (errors === 0) { showAlert('save-schedules-alert', `Все записи (${saved}) успешно сохранены!`, 'success'); preparedSchedules = []; renderPreparedSchedules(); updateTableVisibility(); setTimeout(closeOverlay, 2000); } else { // Оставляем неудачные записи для повторной попытки / удаления preparedSchedules = failedRecords; renderPreparedSchedules(); if (saved > 0) { showAlert('save-schedules-alert', `Сохранено: ${saved}. Ошибок: ${errors}. Проблемные записи отмечены в таблице.`, 'error'); } else { showAlert('save-schedules-alert', `Не удалось сохранить. Ошибок: ${errors}. Проблемные записи отмечены в таблице.`, 'error'); } } }); }