Files
magistr/frontend/admin/js/main.js

171 lines
6.3 KiB
JavaScript
Executable File

// OTel: загружаем только на продакшене (не на localhost)
if (!['localhost', '127.0.0.1'].includes(window.location.hostname)) {
import('./otel.js').catch(e => console.warn('OTel init skipped:', e.message));
}
import { isAuthenticatedAsAdmin } from './api.js';
import { applyRippleEffect, closeAllDropdownsOnOutsideClick } from './utils.js';
import { startDropdownAutoObserver, initAllCustomDropdowns } from './dropdown.js';
// Auth check
if (!isAuthenticatedAsAdmin()) {
window.location.href = '/';
}
// Global initialization for Custom Selects
document.addEventListener('DOMContentLoaded', () => {
initAllCustomDropdowns(document.body);
startDropdownAutoObserver();
});
import { initUsers } from './views/users.js';
import { initGroups } from './views/groups.js';
import { initEduForms } from './views/edu-forms.js';
import { initEquipments } from './views/equipments.js';
import { initClassrooms } from './views/classrooms.js';
import { initSubjects } from './views/subjects.js';
import {initSchedule} from "./views/schedule.js";
import {initDatabase} from "./views/database.js";
import {initDepartment} from "./views/department.js";
import {initDepartmentsData} from "./views/departments-data.js";
import {initAuditoriumWorkload} from "./views/auditorium-workload.js";
// Configuration
const ROUTES = {
users: { title: 'Управление пользователями', file: 'views/users.html', init: initUsers },
groups: { title: 'Управление группами', file: 'views/groups.html', init: initGroups },
'edu-forms': { title: 'Формы обучения', file: 'views/edu-forms.html', init: initEduForms },
equipments: { title: 'Оборудование', file: 'views/equipments.html', init: initEquipments },
classrooms: { title: 'Аудитории', file: 'views/classrooms.html', init: initClassrooms },
subjects: { title: 'Дисциплины и преподаватели', file: 'views/subjects.html', init: initSubjects },
schedule: { title: 'Расписание занятий', file: 'views/schedule.html', init: initSchedule },
'auditorium-workload': { title: 'Загруженность аудиторий', file: 'views/auditorium-workload.html', init: initAuditoriumWorkload },
database: { title: 'База данных', file: 'views/database.html', init: initDatabase },
department: { title: 'Кафедры', file: 'views/department.html', init: initDepartment },
'departments-data': { title: 'Создание кафедры/специальности', file: 'views/departments-data.html', init: initDepartmentsData },
};
let currentTab = null;
// DOM Elements
const appContent = document.getElementById('app-content');
const pageTitle = document.getElementById('page-title');
const navItems = document.querySelectorAll('.nav-item[data-tab]');
const sidebar = document.querySelector('.sidebar');
const sidebarOverlay = document.getElementById('sidebar-overlay');
const menuToggle = document.getElementById('menu-toggle');
const sidebarCloseBtn = document.getElementById('sidebar-close-btn');
const btnLogout = document.getElementById('btn-logout');
const main = document.querySelector('.main');
// Initial auth check
if (!isAuthenticatedAsAdmin()) {
window.location.href = '/';
}
// Setup Global Effects
applyRippleEffect();
closeAllDropdownsOnOutsideClick();
// Init sidebar state from localStorage on load
if (window.innerWidth > 768 && localStorage.getItem('sidebar-collapsed') === 'true') {
sidebar.classList.add('collapsed');
main.classList.add('sidebar-collapsed');
}
// Menu Toggle (Hamburger)
menuToggle.addEventListener('click', () => {
if (window.innerWidth <= 768) {
sidebar.classList.toggle('open');
sidebarOverlay.classList.toggle('open');
} else {
sidebar.classList.remove('collapsed');
main.classList.remove('sidebar-collapsed');
localStorage.setItem('sidebar-collapsed', 'false');
}
});
// Sidebar Close (X button)
sidebarCloseBtn?.addEventListener('click', () => {
if (window.innerWidth <= 768) {
sidebar.classList.remove('open');
sidebarOverlay.classList.remove('open');
} else {
sidebar.classList.toggle('collapsed');
main.classList.toggle('sidebar-collapsed');
localStorage.setItem('sidebar-collapsed', sidebar.classList.contains('collapsed'));
}
});
sidebarOverlay.addEventListener('click', () => {
sidebar.classList.remove('open');
sidebarOverlay.classList.remove('open');
});
// Settings Dropdown
const settingsDropdown = document.getElementById('settings-dropdown');
const btnSettings = document.getElementById('btn-settings');
btnSettings.addEventListener('click', (e) => {
e.stopPropagation();
settingsDropdown.classList.toggle('open');
});
document.addEventListener('click', (e) => {
if (!settingsDropdown.contains(e.target)) {
settingsDropdown.classList.remove('open');
}
});
// Logout
btnLogout.addEventListener('click', () => {
localStorage.removeItem('token');
localStorage.removeItem('role');
window.location.href = '/';
});
// Navigation
navItems.forEach(item => {
item.addEventListener('click', (e) => {
e.preventDefault();
const tab = item.dataset.tab;
switchTab(tab);
});
});
async function switchTab(tab) {
if (currentTab === tab || !ROUTES[tab]) return;
// UI Update
navItems.forEach(n => n.classList.remove('active'));
document.querySelector(`.nav-item[data-tab="${tab}"]`)?.classList.add('active');
pageTitle.textContent = ROUTES[tab].title;
// Load template
try {
appContent.innerHTML = '<div class="loading-row">Загрузка...</div>';
const response = await fetch(ROUTES[tab].file);
if (!response.ok) throw new Error('Failed to load view');
const html = await response.text();
appContent.innerHTML = html;
// Initialize logic for the tab
if (ROUTES[tab].init) {
ROUTES[tab].init();
}
currentTab = tab;
} catch (e) {
appContent.innerHTML = `<div class="form-alert error">Ошибка загрузки вкладки: ${e.message}</div>`;
console.error(e);
}
// Close mobile menu if open
sidebar.classList.remove('open');
sidebarOverlay.classList.remove('open');
}
// Load default tab
switchTab('users');