(() => { 'use strict'; const token = localStorage.getItem('token'); const role = localStorage.getItem('role'); if (!token || role !== 'ADMIN') { window.location.href = '/'; return; } // ---- DOM refs ---- const pageTitle = document.getElementById('page-title'); const btnLogout = document.getElementById('btn-logout'); const menuToggle = document.getElementById('menu-toggle'); const sidebar = document.querySelector('.sidebar'); const sidebarOverlay = document.getElementById('sidebar-overlay'); // Global Ripple Effect document.addEventListener('click', function (e) { const btn = e.target.closest('.btn-create, .btn-delete, .btn-logout'); if (!btn) return; const rect = btn.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; const ripple = document.createElement('span'); ripple.classList.add('ripple'); ripple.style.left = `${x}px`; ripple.style.top = `${y}px`; if (getComputedStyle(btn).position === 'static') { btn.style.position = 'relative'; } btn.style.overflow = 'hidden'; btn.appendChild(ripple); setTimeout(() => ripple.remove(), 600); }); // Users const usersTbody = document.getElementById('users-tbody'); const createForm = document.getElementById('create-form'); const createAlert = document.getElementById('create-alert'); // Groups const groupsTbody = document.getElementById('groups-tbody'); const createGroupForm = document.getElementById('create-group-form'); const createGroupAlert = document.getElementById('create-group-alert'); const newGroupEfSelect = document.getElementById('new-group-ef'); const filterEfSelect = document.getElementById('filter-ef'); // Education Forms const efTbody = document.getElementById('ef-tbody'); const createEfForm = document.getElementById('create-ef-form'); const createEfAlert = document.getElementById('create-ef-alert'); // Classrooms const classroomsTbody = document.getElementById('classrooms-tbody'); const createClassroomForm = document.getElementById('create-classroom-form'); const createClassroomAlert = document.getElementById('create-classroom-alert'); const modalEditClassroom = document.getElementById('modal-edit-classroom'); const modalEditClassroomClose = document.getElementById('modal-edit-classroom-close'); const editClassroomForm = document.getElementById('edit-classroom-form'); const editClassroomAlert = document.getElementById('edit-classroom-alert'); const editEquipmentCheckboxes = document.getElementById('edit-equipment-checkboxes'); // Equipments const equipmentsTbody = document.getElementById('equipments-tbody'); const createEquipmentForm = document.getElementById('create-equipment-form'); const createEquipmentAlert = document.getElementById('create-equipment-alert'); const equipmentCheckboxes = document.getElementById('equipment-checkboxes'); // --- Multi-select logic --- function updateSelectText(containerId, textId) { const container = document.getElementById(containerId); const textEl = document.getElementById(textId); if (!container || !textEl) return; const checked = Array.from(container.querySelectorAll('input:checked')); if (checked.length === 0) { textEl.textContent = 'Выберите оборудование...'; } else if (checked.length === 1) { textEl.textContent = checked[0].parentElement.textContent.trim(); } else { textEl.textContent = `Выбрано: ${checked.length}`; } } function initMultiSelect(boxId, menuId, textId, checkboxContainerId) { const box = document.getElementById(boxId); const menu = document.getElementById(menuId); const container = document.getElementById(checkboxContainerId); if (!box || !menu || !container) return; box.addEventListener('click', (e) => { e.stopPropagation(); const isOpen = menu.classList.contains('open'); document.querySelectorAll('.dropdown-menu').forEach(m => m.classList.remove('open')); document.querySelectorAll('.select-box').forEach(b => b.classList.remove('active')); if (!isOpen) { menu.classList.add('open'); box.classList.add('active'); } }); menu.addEventListener('click', (e) => { e.stopPropagation(); }); container.addEventListener('change', () => { updateSelectText(checkboxContainerId, textId); }); } initMultiSelect('equipment-select-box', 'equipment-dropdown-menu', 'equipment-select-text', 'equipment-checkboxes'); initMultiSelect('edit-equipment-select-box', 'edit-equipment-dropdown-menu', 'edit-equipment-select-text', 'edit-equipment-checkboxes'); document.addEventListener('click', () => { document.querySelectorAll('.dropdown-menu').forEach(m => m.classList.remove('open')); document.querySelectorAll('.select-box').forEach(b => b.classList.remove('active')); }); // -------------------------- const navItems = document.querySelectorAll('.nav-item[data-tab]'); const tabContents = document.querySelectorAll('.tab-content'); // ---- State ---- let allGroups = []; let allEducationForms = []; let allEquipments = []; // ---- Tab Switching ---- const TAB_TITLES = { users: 'Управление пользователями', groups: 'Управление группами', 'edu-forms': 'Формы обучения', equipments: 'Оборудование', classrooms: 'Аудитории' }; navItems.forEach(item => { item.addEventListener('click', (e) => { e.preventDefault(); switchTab(item.dataset.tab); }); }); function switchTab(tab) { navItems.forEach(n => n.classList.remove('active')); document.querySelector(`.nav-item[data-tab="${tab}"]`)?.classList.add('active'); tabContents.forEach(tc => tc.style.display = 'none'); const target = document.getElementById('tab-' + tab); if (target) target.style.display = ''; pageTitle.textContent = TAB_TITLES[tab] || ''; if (tab === 'users') loadUsers(); if (tab === 'groups') { loadEducationForms().then(() => loadGroups()); } if (tab === 'edu-forms') loadEducationForms(); if (tab === 'equipments') loadEquipments(); if (tab === 'classrooms') { loadEquipments().then(() => loadClassrooms()); } sidebar.classList.remove('open'); sidebarOverlay.classList.remove('open'); } // ---- Mobile Menu ---- menuToggle.addEventListener('click', () => { sidebar.classList.toggle('open'); sidebarOverlay.classList.toggle('open'); }); sidebarOverlay.addEventListener('click', () => { sidebar.classList.remove('open'); sidebarOverlay.classList.remove('open'); }); // ---- Helpers ---- const ROLE_LABELS = { ADMIN: 'Администратор', TEACHER: 'Преподаватель', STUDENT: 'Студент' }; const ROLE_BADGE = { ADMIN: 'badge-admin', TEACHER: 'badge-teacher', STUDENT: 'badge-student' }; function escapeHtml(str) { const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } function showAlert(el, msg, type) { el.className = 'form-alert ' + type; el.textContent = msg; } function hideAlert(el) { el.className = 'form-alert'; el.textContent = ''; } // ============================================================ // USERS // ============================================================ async function loadUsers() { try { const res = await fetch('/api/users', { headers: { 'Authorization': 'Bearer ' + token }, }); const users = await res.json(); renderUsers(users); } catch (e) { usersTbody.innerHTML = '
Ошибка загрузки
'; } } function renderEquipments(equipments) { if (!equipments.length) { equipmentsTbody.innerHTML = 'Нет доступного оборудования
'; return; } equipmentCheckboxes.innerHTML = equipments.map(eq => ` `).join(''); updateSelectText('equipment-checkboxes', 'equipment-select-text'); } createEquipmentForm.addEventListener('submit', async (e) => { e.preventDefault(); hideAlert(createEquipmentAlert); const name = document.getElementById('new-equipment-name').value.trim(); if (!name) { showAlert(createEquipmentAlert, 'Введите название', 'error'); return; } try { const res = await fetch('/api/equipments', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, body: JSON.stringify({ name }), }); const data = await res.json(); if (res.ok) { showAlert(createEquipmentAlert, `Оборудование "${data.name}" добавлено`, 'success'); createEquipmentForm.reset(); loadEquipments(); } else { showAlert(createEquipmentAlert, data.message || 'Ошибка создания', 'error'); } } catch (e) { showAlert(createEquipmentAlert, 'Ошибка соединения', 'error'); } }); equipmentsTbody.addEventListener('click', async (e) => { const btn = e.target.closest('.btn-delete'); if (!btn) return; if (!confirm('Удалить оборудование?')) return; try { const res = await fetch('/api/equipments/' + btn.dataset.id, { method: 'DELETE', headers: { 'Authorization': 'Bearer ' + token }, }); if (res.ok) { loadEquipments(); } else { const data = await res.json(); alert(data.message || 'Ошибка удаления'); } } catch (e) { alert('Ошибка соединения'); } }); // ============================================================ // CLASSROOMS // ============================================================ async function loadClassrooms() { try { const res = await fetch('/api/classrooms', { headers: { 'Authorization': 'Bearer ' + token }, }); const classrooms = await res.json(); renderClassrooms(classrooms); } catch (e) { classroomsTbody.innerHTML = 'Нет доступного оборудования
'; } updateSelectText('edit-equipment-checkboxes', 'edit-equipment-select-text'); hideAlert(editClassroomAlert); modalEditClassroom.classList.add('open'); } catch (e) { alert('Ошибка загрузки данных аудитории'); } } modalEditClassroomClose.addEventListener('click', () => { modalEditClassroom.classList.remove('open'); }); modalEditClassroom.addEventListener('click', (e) => { if (e.target === modalEditClassroom) { modalEditClassroom.classList.remove('open'); } }); editClassroomForm.addEventListener('submit', async (e) => { e.preventDefault(); hideAlert(editClassroomAlert); const id = document.getElementById('edit-classroom-id').value; const name = document.getElementById('edit-classroom-name').value.trim(); const capacity = parseInt(document.getElementById('edit-classroom-capacity').value, 10); const checkedBoxes = Array.from(editEquipmentCheckboxes.querySelectorAll('input:checked')); const equipmentIds = checkedBoxes.map(chk => parseInt(chk.value, 10)); if (!name || isNaN(capacity)) { showAlert(editClassroomAlert, 'Заполните обязательные поля', 'error'); return; } try { const res = await fetch('/api/classrooms/' + id, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, body: JSON.stringify({ name, capacity, equipmentIds, isAvailable: editingClassroomData.isAvailable }), }); const data = await res.json(); if (res.ok) { modalEditClassroom.classList.remove('open'); showAlert(createClassroomAlert, `Аудитория "${data.name}" обновлена`, 'success'); loadClassrooms(); } else { showAlert(editClassroomAlert, data.message || 'Ошибка обновления', 'error'); } } catch (e) { showAlert(editClassroomAlert, 'Ошибка соединения', 'error'); } }); // ============================================================ // LOGOUT & INIT // ============================================================ btnLogout.addEventListener('click', () => { localStorage.removeItem('token'); localStorage.removeItem('role'); window.location.href = '/'; }); loadUsers(); })();