import { clearAuthState, fetchWithAuth, logoutSession, restoreSession } from '../auth-session.js'; (async () => { const session = await restoreSession(); if (!session || session.role !== 'STUDENT') { window.location.replace('/'); return; } const groupSelect = document.getElementById('group-select'); 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(); } }); groupSelect.addEventListener('change', () => { localStorage.setItem('studentGroupId', groupSelect.value); loadSchedule(); }); init(); async function init() { await loadGroups(); await loadSchedule(); } async function loadGroups() { try { const groups = await fetchJson('/api/groups'); if (!groups.length) { groupSelect.innerHTML = ''; return; } const savedGroupId = localStorage.getItem('studentGroupId'); groupSelect.innerHTML = groups.map(group => ( `` )).join(''); groupSelect.value = savedGroupId && groups.some(group => String(group.id) === savedGroupId) ? savedGroupId : String(groups[0].id); } catch (error) { groupSelect.innerHTML = ''; showStatus(error.message, true); } } async function loadSchedule() { const groupId = groupSelect.value; const startDate = toIsoDate(weekStart); const endDate = toIsoDate(addDays(weekStart, 6)); datePicker.value = startDate; weekLabel.textContent = `${formatDate(weekStart)} - ${formatDate(addDays(weekStart, 6))}`; renderEmptyWeek('Загрузка...'); if (!groupId) { showStatus('Выберите группу', false); renderEmptyWeek('Нет выбранной группы'); return; } try { const lessons = await fetchJson(`/api/schedule?groupId=${encodeURIComponent(groupId)}&startDate=${startDate}&endDate=${endDate}`); renderWeek(lessons); showStatus(lessons.length ? `Найдено занятий: ${lessons.length}` : 'На этой неделе занятий нет', false); } catch (error) { renderEmptyWeek('Ошибка загрузки'); showStatus(error.message, true); } } function renderWeek(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 renderDay(date, dayLessons); }).join(''); } function renderEmptyWeek(message) { grid.innerHTML = daysOfWeek().map(date => renderDay(date, [], message)).join(''); } function renderDay(date, lessons, emptyMessage = 'Занятий нет') { return `
${escapeHtml(dayName(date))}
${escapeHtml(formatDate(date))}
${lessons.length ? lessons.map(renderLesson).join('') : `
${escapeHtml(emptyMessage)}
`}
`; } 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(trimTime(lesson.startTime))} - ${escapeHtml(trimTime(lesson.endTime))}
${escapeHtml(lesson.subjectName)}
${escapeHtml(lesson.lessonTypeName)} ${subgroups ? `${escapeHtml(subgroups)}` : ''} ${escapeHtml(lesson.lessonFormat)} ${escapeHtml(lesson.teacherName)} ${escapeHtml(lesson.classroomName)} ${groups ? `${escapeHtml(groups)}` : ''}
`; } 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("'", '''); } })();