import { api } from '../api.js'; import { escapeHtml, showAlert, hideAlert } from '../utils.js'; export async function initSubjects() { const subjectsTbody = document.getElementById('subjects-tbody'); const createSubjectForm = document.getElementById('create-subject-form'); const newSubjectDeptSelect = document.getElementById('new-subject-department'); // Модалка управления преподавателями const modalManageTeachers = document.getElementById('modal-manage-teachers'); const modalManageTeachersClose = document.getElementById('modal-manage-teachers-close'); const subjectTeachersContext = document.getElementById('subject-teachers-context'); const manageTeacherSubjectForm = document.getElementById('manage-teacher-subject-form'); const manageTsSubjectIdInput = document.getElementById('manage-ts-subject-id'); const manageTsTeacherSelect = document.getElementById('manage-ts-teacher-select'); const manageTsTbody = document.getElementById('manage-ts-tbody'); let allSubjects = []; let allTeachers = []; let allDepartments = []; let teacherSubjects = []; bindEvents(); await loadInitialData(); function bindEvents() { createSubjectForm.addEventListener('submit', createSubject); subjectsTbody.addEventListener('click', handleSubjectsTableClick); manageTeacherSubjectForm.addEventListener('submit', saveTeacherAssignment); manageTsTbody.addEventListener('click', handleTeacherAssignmentTableClick); modalManageTeachersClose.addEventListener('click', () => modalManageTeachers.classList.remove('open')); modalManageTeachers.addEventListener('click', (e) => { if (e.target === modalManageTeachers) modalManageTeachers.classList.remove('open'); }); } async function loadInitialData() { try { [allTeachers, allDepartments] = await Promise.all([ api.get('/api/users/teachers'), api.get('/api/departments') ]); populateDepartmentsSelect(); await loadSubjects(); } catch (e) { subjectsTbody.innerHTML = `Ошибка загрузки справочников: ${escapeHtml(e.message)}`; } } function populateDepartmentsSelect() { newSubjectDeptSelect.innerHTML = '' + allDepartments.map(d => { const name = d.departmentName || d.name || 'Без названия'; const code = d.departmentCode || d.code || d.id; return ``; }).join(''); } async function loadSubjects() { subjectsTbody.innerHTML = 'Загрузка...'; try { [allSubjects, teacherSubjects] = await Promise.all([ api.get('/api/subjects'), api.get('/api/teacher-subjects') ]); renderSubjects(); } catch (e) { subjectsTbody.innerHTML = `Ошибка загрузки: ${escapeHtml(e.message)}`; } } function renderSubjects() { if (!allSubjects || !allSubjects.length) { subjectsTbody.innerHTML = 'Нет дисциплин'; return; } // Группируем преподавателей по дисциплинам const teachersBySubject = new Map(); teacherSubjects.forEach(ts => { if (!teachersBySubject.has(ts.subjectId)) { teachersBySubject.set(ts.subjectId, []); } teachersBySubject.get(ts.subjectId).push(ts); }); subjectsTbody.innerHTML = allSubjects.map(s => { const teachers = teachersBySubject.get(s.id) || []; const teachersHtml = teachers.length ? teachers.map(t => `${escapeHtml(t.fullName || t.username)}`).join('') : 'Нет преподавателей'; return ` ${s.id} ${escapeHtml(s.name)} ${escapeHtml(s.code || '-')} ${escapeHtml(departmentLabel(s.departmentId))}
${teachersHtml}
`; }).join(''); } async function createSubject(e) { e.preventDefault(); hideAlert('create-subject-alert'); const name = document.getElementById('new-subject-name').value.trim(); const code = document.getElementById('new-subject-code').value.trim(); const departmentId = newSubjectDeptSelect.value; if (!name || !code || !departmentId) { showAlert('create-subject-alert', 'Заполните все поля', 'error'); return; } try { const data = await api.post('/api/subjects', { name, code, departmentId: Number(departmentId) }); showAlert('create-subject-alert', `Дисциплина "${escapeHtml(data.name || name)}" создана`, 'success'); createSubjectForm.reset(); await loadSubjects(); } catch (error) { showAlert('create-subject-alert', error.message || 'Ошибка создания', 'error'); } } async function handleSubjectsTableClick(e) { const btnDelete = e.target.closest('.btn-delete-subject'); const btnTeachers = e.target.closest('.btn-manage-teachers'); if (btnDelete) { if (!confirm('Удалить дисциплину? Это удалит её привязки к преподавателям.')) return; try { await api.delete('/api/subjects/' + btnDelete.dataset.id); await loadSubjects(); } catch (error) { alert(error.message || 'Ошибка удаления'); } } if (btnTeachers) { const subjectId = btnTeachers.dataset.id; const subject = allSubjects.find(s => s.id == subjectId); if (subject) openTeachersModal(subject); } } // --- Управление привязками преподавателей к дисциплине --- function openTeachersModal(subject) { subjectTeachersContext.textContent = `${subject.code ? `${subject.code} · ` : ''}${subject.name}`; manageTsSubjectIdInput.value = subject.id; hideAlert('manage-ts-alert'); modalManageTeachers.classList.add('open'); renderSubjectTeachers(subject.id); populateTeachersSelect(subject.id); } function renderSubjectTeachers(subjectId) { const list = teacherSubjects.filter(ts => String(ts.subjectId) === String(subjectId)); if (!list.length) { manageTsTbody.innerHTML = 'Преподаватели не привязаны'; return; } manageTsTbody.innerHTML = list.map(ts => ` ${escapeHtml(ts.fullName || ts.username)} (${escapeHtml(ts.username)}) `).join(''); } function populateTeachersSelect(subjectId) { // Показываем только тех преподавателей, которые еще не привязаны к этой дисциплине const assignedUserIds = new Set( teacherSubjects .filter(ts => String(ts.subjectId) === String(subjectId)) .map(ts => String(ts.userId)) ); const availableTeachers = allTeachers.filter(t => !assignedUserIds.has(String(t.id))); if (!availableTeachers.length) { manageTsTeacherSelect.innerHTML = ''; return; } manageTsTeacherSelect.innerHTML = '' + availableTeachers.map(t => ``).join(''); } async function saveTeacherAssignment(e) { e.preventDefault(); hideAlert('manage-ts-alert'); const subjectId = manageTsSubjectIdInput.value; const userId = manageTsTeacherSelect.value; if (!userId) { showAlert('manage-ts-alert', 'Выберите преподавателя', 'error'); return; } try { await api.post('/api/teacher-subjects', { userId: Number(userId), subjectId: Number(subjectId) }); showAlert('manage-ts-alert', 'Преподаватель успешно привязан', 'success'); // Перезагрузим привязки и обновим интерфейс teacherSubjects = await api.get('/api/teacher-subjects'); renderSubjectTeachers(subjectId); populateTeachersSelect(subjectId); // Также обновим главную таблицу renderSubjects(); } catch (error) { showAlert('manage-ts-alert', error.message || 'Ошибка привязки', 'error'); } } async function handleTeacherAssignmentTableClick(e) { const btnDelete = e.target.closest('.btn-delete-ts'); if (!btnDelete) return; if (!confirm('Удалить привязку преподавателя?')) return; const subjectId = manageTsSubjectIdInput.value; try { await api.delete('/api/teacher-subjects', { userId: Number(btnDelete.dataset.userId), subjectId: Number(btnDelete.dataset.subjectId) }); // Перезагрузим привязки teacherSubjects = await api.get('/api/teacher-subjects'); renderSubjectTeachers(subjectId); populateTeachersSelect(subjectId); // Обновим главную таблицу renderSubjects(); } catch (error) { alert(error.message || 'Ошибка удаления привязки'); } } // --- Вспомогательные функции --- function departmentLabel(departmentId) { const department = allDepartments.find(item => item.id == departmentId); if (!department) return departmentId || '-'; return department.departmentName || department.name || departmentId; } }