import { clearAuthState, fetchWithAuth, logoutSession, restoreSession } from '../auth-session.js'; (async () => { const session = await restoreSession(); if (!session || session.role !== 'TEACHER') { window.location.replace('/'); return; } const teacherId = session.userId; const grid = document.getElementById('schedule-grid'); const statusLine = document.getElementById('status-line'); const weekLabel = document.getElementById('week-label'); const datePicker = document.getElementById('date-picker'); let weekStart = startOfWeek(new Date()); document.getElementById('logout-link').addEventListener('click', async (event) => { event.preventDefault(); await logoutSession(); window.location.replace('/'); }); document.getElementById('theme-toggle-local').addEventListener('click', () => { const next = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'; document.documentElement.dataset.theme = next; localStorage.setItem('theme', next); }); document.documentElement.dataset.theme = localStorage.getItem('theme') || 'light'; document.getElementById('prev-week').addEventListener('click', () => moveWeek(-1)); document.getElementById('today-week').addEventListener('click', () => { weekStart = startOfWeek(new Date()); loadSchedule(); }); document.getElementById('next-week').addEventListener('click', () => moveWeek(1)); datePicker.addEventListener('change', () => { if (datePicker.value) { weekStart = startOfWeek(new Date(datePicker.value)); loadSchedule(); } }); loadSchedule(); async function loadSchedule() { const startDate = toIsoDate(weekStart); const endDate = toIsoDate(addDays(weekStart, 6)); datePicker.value = startDate; weekLabel.textContent = `${formatDate(weekStart)} - ${formatDate(addDays(weekStart, 6))}`; if (!teacherId) { renderEmptyWeek('Не удалось определить преподавателя'); showStatus('Выполните вход заново, чтобы получить идентификатор пользователя', true); return; } renderEmptyWeek('Загрузка...'); try { const lessons = await fetchJson(`/api/schedule?teacherId=${encodeURIComponent(teacherId)}&startDate=${startDate}&endDate=${endDate}`); renderWeek(lessons); showStatus(lessons.length ? `Найдено занятий: ${lessons.length}` : 'На этой неделе занятий нет', false); } catch (error) { renderEmptyWeek('Ошибка загрузки'); showStatus(error.message, true); } } function renderWeek(lessons) { if (window.matchMedia('(max-width: 980px)').matches) { renderMobileWeek(lessons); return; } const slots = collectSlots(lessons); const lessonsByKey = new Map(); lessons.forEach(lesson => { lessonsByKey.set(`${lesson.date}:${lesson.timeSlotOrder}`, lesson); }); grid.innerHTML = [ '
', ...daysOfWeek().map(date => renderHead(date)), ...slots.flatMap(slot => [ `
${escapeHtml(slot.label)}
`, ...daysOfWeek().map(date => renderCell(lessonsByKey.get(`${toIsoDate(date)}:${slot.order}`))) ]) ].join(''); } function renderMobileWeek(lessons) { const lessonsByDate = new Map(); lessons.forEach(lesson => { if (!lessonsByDate.has(lesson.date)) lessonsByDate.set(lesson.date, []); lessonsByDate.get(lesson.date).push(lesson); }); grid.innerHTML = daysOfWeek().map(date => { const key = toIsoDate(date); const dayLessons = lessonsByDate.get(key) || []; return `
${renderHead(date)}
${dayLessons.length ? dayLessons.map(renderLesson).join('') : '
Занятий нет
'}
`; }).join(''); } function renderEmptyWeek(message) { if (window.matchMedia('(max-width: 980px)').matches) { grid.innerHTML = daysOfWeek().map(date => `
${renderHead(date)}
${escapeHtml(message)}
`).join(''); return; } grid.innerHTML = [ '
', ...daysOfWeek().map(date => renderHead(date)), `
Неделя
`, ...daysOfWeek().map(() => `
${escapeHtml(message)}
`) ].join(''); } function renderHead(date) { return `
${escapeHtml(dayName(date))} ${escapeHtml(formatDate(date))}
`; } function renderCell(lesson) { return `
${lesson ? renderLesson(lesson) : '
Нет занятия
'}
`; } function renderLesson(lesson) { const groups = Array.isArray(lesson.groupNames) ? lesson.groupNames.join(', ') : ''; const subgroups = Array.isArray(lesson.subgroupNames) && lesson.subgroupNames.length ? lesson.subgroupNames.join(', ') : lesson.subgroupName; return `
${escapeHtml(lesson.subjectName)}
${escapeHtml(trimTime(lesson.startTime))} - ${escapeHtml(trimTime(lesson.endTime))} ${escapeHtml(lesson.lessonTypeName)} ${subgroups ? `${escapeHtml(subgroups)}` : ''} ${escapeHtml(lesson.classroomName)} ${groups ? `${escapeHtml(groups)}` : ''}
`; } function collectSlots(lessons) { const slots = new Map(); lessons.forEach(lesson => { slots.set(lesson.timeSlotOrder, { order: lesson.timeSlotOrder, label: `${trimTime(lesson.startTime)} - ${trimTime(lesson.endTime)}` }); }); if (!slots.size) { slots.set(1, { order: 1, label: 'Расписание' }); } return [...slots.values()].sort((a, b) => a.order - b.order); } function moveWeek(delta) { weekStart = addDays(weekStart, delta * 7); loadSchedule(); } function daysOfWeek() { return Array.from({ length: 7 }, (_, index) => addDays(weekStart, index)); } async function fetchJson(url) { const response = await fetchWithAuth(url); const data = await response.json().catch(() => null); if (response.status === 401) { clearAuthState(); window.location.replace('/'); throw new Error('Требуется повторный вход в систему'); } if (!response.ok) { throw new Error(data?.message || `Ошибка HTTP: ${response.status}`); } return data; } function showStatus(message, isError) { statusLine.textContent = message; statusLine.classList.toggle('error', isError); } function startOfWeek(date) { const copy = new Date(date.getFullYear(), date.getMonth(), date.getDate()); const day = copy.getDay() || 7; copy.setDate(copy.getDate() - day + 1); return copy; } function addDays(date, days) { const copy = new Date(date); copy.setDate(copy.getDate() + days); return copy; } function toIsoDate(date) { return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; } function formatDate(date) { return date.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric' }); } function dayName(date) { const value = date.toLocaleDateString('ru-RU', { weekday: 'long' }); return value.charAt(0).toUpperCase() + value.slice(1); } function trimTime(value) { return String(value || '').slice(0, 5); } function escapeHtml(value) { return String(value ?? '') .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); } })();