From bdccf744385c559c94a6b8762ee7fb8643b80b03 Mon Sep 17 00:00:00 2001 From: Alina Date: Sat, 11 Apr 2026 05:09:35 +0400 Subject: [PATCH 1/3] html --- requirements.txt | 5 +++ static/index.html | 101 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 requirements.txt create mode 100644 static/index.html diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..6f766b68d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +Flask==2.3.3 +requests==2.31.0 +beautifulsoup4==4.12.2 +cachetools==5.3.1 +flask-cors==4.0.0 \ No newline at end of file diff --git a/static/index.html b/static/index.html new file mode 100644 index 000000000..db47628e1 --- /dev/null +++ b/static/index.html @@ -0,0 +1,101 @@ + + + + + + Расписание СГАУ - Самарский университет + + + + + + +
+
+
+
Самарский национальный исследовательский университет
+
+
+
+
+ +
+
+

Самарский университет

+

Расписание занятий

+
+
+
+ +
+
+ +
+
+ + +
+ +
+
+ + +
Начните вводить номер группы для поиска
+
+
+ + + +
+
+ Учебная неделя + - + +
+
+
+ + + +
+
+ +
+
+

Загрузка расписания...

+
+ + + +
+
+
Выберите группу или преподавателя
+
Данные с официального сайта СГАУ
+
+
+
+ + + + + + \ No newline at end of file From 981012a052d425328ecf36c285cd2d051a1efafa Mon Sep 17 00:00:00 2001 From: Alina Date: Sat, 11 Apr 2026 05:42:46 +0400 Subject: [PATCH 2/3] =?UTF-8?q?html=20=D0=B8=20css?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/css/style.css | 315 +++++++++++++++++++++++++++++++++++++++++++ static/index.html | 101 ++++---------- 2 files changed, 340 insertions(+), 76 deletions(-) create mode 100644 static/css/style.css diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 000000000..57998e674 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,315 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + background: #f0f2f5; + color: #333; +} + +.header { + background: #2780E3; + color: white; + padding: 20px; + text-align: center; +} + +.header h1 { + font-size: 1.5rem; +} + +.header p { + font-size: 0.8rem; + opacity: 0.8; + margin-top: 5px; +} + +.main { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +.mode-switch { + display: flex; + gap: 10px; + margin-bottom: 20px; +} + +.mode-btn { + flex: 1; + padding: 10px; + background: white; + border: 1px solid #ddd; + border-radius: 5px; + cursor: pointer; + font-size: 1rem; +} + +.mode-btn.active { + background: #2780E3; + color: white; + border-color: #2780E3; +} + +#groupMode select, +#teacherMode input, +#teacherMode select { + width: 100%; + padding: 10px; + margin-bottom: 15px; + border: 1px solid #ddd; + border-radius: 5px; + font-size: 1rem; +} + +.week-controls { + display: flex; + gap: 10px; + align-items: center; + justify-content: center; + margin: 20px 0; +} + +.week-controls button { + padding: 8px 16px; + background: #2780E3; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; +} + +.week-controls button:hover { + background: #1a5cb3; +} + +#weekNumber { + font-weight: bold; + min-width: 100px; + text-align: center; +} + +.loader { + text-align: center; + padding: 40px; +} + +.error { + background: #fee; + color: #c33; + padding: 10px; + border-radius: 5px; + margin-bottom: 20px; + text-align: center; +} + +.hidden { + display: none; +} + +/* Десктопная таблица */ +.schedule-table { + overflow-x: auto; +} + +.schedule-table table { + width: 100%; + border-collapse: collapse; + background: white; + border-radius: 8px; + overflow: hidden; +} + +.schedule-table th, +.schedule-table td { + border: 1px solid #ddd; + padding: 12px; + text-align: left; + vertical-align: top; +} + +.schedule-table th { + background: #2780E3; + color: white; +} + +/* Карточка занятия */ +.lesson { + margin-bottom: 10px; + padding: 8px; + background: #f8f9fa; + border-radius: 5px; +} + +.lesson:last-child { + margin-bottom: 0; +} + +.lesson-time { + font-weight: bold; + color: #2780E3; + font-size: 0.8rem; + margin-bottom: 5px; +} + +.lesson-title { + font-weight: bold; + margin: 5px 0; +} + +.lesson-type { + display: inline-block; + padding: 2px 8px; + border-radius: 3px; + font-size: 0.7rem; + background: #e3f2fd; + color: #1565c0; +} + +.lesson-teacher, +.lesson-room, +.lesson-group { + font-size: 0.75rem; + color: #666; + margin-top: 3px; +} + +/* Мобильная навигация по дням */ +.mobile-day-nav { + display: flex; + align-items: center; + justify-content: space-between; + background: #2780E3; + padding: 15px; + border-radius: 8px; + margin-bottom: 15px; +} + +.day-nav-btn { + background: white; + color: #2780E3; + border: none; + width: 40px; + height: 40px; + border-radius: 50%; + font-size: 18px; + cursor: pointer; + font-weight: bold; +} + +.day-nav-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.day-nav-btn:hover:not(:disabled) { + background: #f0f0f0; +} + +.mobile-day-info { + text-align: center; + color: white; +} + +.mobile-day-info .day-name { + font-size: 1.1rem; + font-weight: bold; +} + +.mobile-day-info .day-date { + font-size: 0.8rem; + opacity: 0.8; + margin-top: 4px; +} + +.mobile-lessons { + display: flex; + flex-direction: column; + gap: 10px; +} + +.mobile-lessons .lesson { + background: white; + border-radius: 8px; + padding: 12px; + border-left: 3px solid #2780E3; + margin-bottom: 0; +} + +.mobile-lessons .lesson-time { + font-size: 0.75rem; + color: #2780E3; + font-weight: bold; + margin-bottom: 5px; +} + +.mobile-lessons .lesson-title { + font-size: 1rem; + font-weight: bold; + margin: 5px 0; +} + +.mobile-lessons .lesson-type { + display: inline-block; + padding: 2px 8px; + border-radius: 3px; + font-size: 0.7rem; + background: #e3f2fd; + color: #1565c0; + margin-bottom: 5px; +} + +.mobile-lessons .lesson-teacher, +.mobile-lessons .lesson-room, +.mobile-lessons .lesson-group { + font-size: 0.75rem; + color: #666; + margin-top: 3px; +} + +.no-lessons { + text-align: center; + padding: 40px; + color: #666; + background: white; + border-radius: 8px; +} + +/* Адаптивность */ +@media (max-width: 768px) { + .main { + padding: 15px; + } + + .schedule-table { + display: none; + } + + .mode-switch { + flex-direction: column; + } + + .week-controls { + flex-wrap: wrap; + } +} + +@media (min-width: 769px) { + .mobile-day-nav, + .mobile-lessons { + display: none; + } +} + +.footer { + background: #333; + color: white; + text-align: center; + padding: 15px; + margin-top: auto; + font-size: 0.8rem; +} \ No newline at end of file diff --git a/static/index.html b/static/index.html index db47628e1..4ade1aceb 100644 --- a/static/index.html +++ b/static/index.html @@ -2,99 +2,48 @@ - + Расписание СГАУ - Самарский университет - - - -
-
-
-
Самарский национальный исследовательский университет
-
-
-
-
- -
-
-

Самарский университет

-

Расписание занятий

-
-
-
- -
-
+
+

Расписание СГАУ

+

Самарский университет

+
-
+
-
-
- - -
Начните вводить номер группы для поиска
-
-
- -
- -
- -
+ From 23fbf43487e25b807475c6b3f1b0f2a195aa0003 Mon Sep 17 00:00:00 2001 From: Alina Date: Sat, 11 Apr 2026 06:01:29 +0400 Subject: [PATCH 3/3] =?UTF-8?q?=D0=9E=D1=81=D0=BD=D0=BE=D0=B2=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 279 ++++++++++++++++++++++++++++++++++ static/css/style.css | 7 +- static/index.html | 7 +- static/js/app.js | 346 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 634 insertions(+), 5 deletions(-) create mode 100644 app.py create mode 100644 static/js/app.js diff --git a/app.py b/app.py new file mode 100644 index 000000000..bd4a84725 --- /dev/null +++ b/app.py @@ -0,0 +1,279 @@ +from flask import Flask, jsonify, request, send_from_directory +from flask_cors import CORS +import requests +from bs4 import BeautifulSoup +import re +from datetime import datetime, timedelta + +app = Flask(__name__, static_folder='static', static_url_path='') +CORS(app) + +HEADERS = { + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" +} + + +def get_current_week(): + """Определение текущей учебной недели""" + now = datetime.now() + start_date = datetime(now.year, 9, 1) + if now < start_date: + start_date = datetime(now.year - 1, 9, 1) + week = (now - start_date).days // 7 + 1 + return week if week > 0 else 1 + + +def get_groups(): + """Получение списка групп с сайта ssau.ru""" + try: + url = "https://ssau.ru/rasp" + response = requests.get(url, headers=HEADERS, timeout=10) + soup = BeautifulSoup(response.text, 'html.parser') + + groups = [] + seen = set() + + for link in soup.find_all('a', href=True): + if 'groupId=' in link['href']: + match = re.search(r'groupId=(\d+)', link['href']) + if match: + group_id = match.group(1) + if group_id not in seen: + seen.add(group_id) + name = link.get_text().strip() + if name and len(name) > 3: + groups.append({"id": group_id, "name": name}) + + if groups: + return groups[:20] + except: + pass + + return [ + {"id": "1282690301", "name": "6411-100503D"}, + {"id": "1282690279", "name": "6412-100503D"}, + {"id": "1213641978", "name": "6413-100503D"}, + ] + + +def get_teachers(): + """Получение списка преподавателей с сайта ssau.ru""" + teachers = {} + groups = get_groups() + + for group in groups[:3]: + try: + url = f"https://ssau.ru/rasp?groupId={group['id']}" + response = requests.get(url, headers=HEADERS, timeout=10) + soup = BeautifulSoup(response.text, 'html.parser') + + for link in soup.find_all('a', href=True): + if 'staffId=' in link['href']: + name = link.get_text().strip() + if name and len(name) > 3: + teachers[name] = name + except: + continue + + result = [{ + "id": str(i), + "name": name + } for i, name in enumerate(sorted(teachers.keys()))] + return result[:30] if result else [{"id": "1", "name": "Иванов И.И."}] + + +def parse_group_schedule(group_id, week): + """Парсинг расписания группы с сайта ssau.ru""" + url = f"https://ssau.ru/rasp?groupId={group_id}" + if week: + url += f"&selectedWeek={week}" + + try: + response = requests.get(url, headers=HEADERS, timeout=15) + soup = BeautifulSoup(response.text, 'html.parser') + + days = [] + schedule_items = soup.select_one(".schedule__items") + + if schedule_items: + # Получаем названия дней + day_names = [] + for head in schedule_items.find_all('div', + class_='schedule__head'): + if not head.find('div', class_='schedule__time-item'): + wd = head.find('div', class_='schedule__head-weekday') + if wd: + day_names.append(wd.get_text().strip().capitalize()) + + # Парсим занятия по времени + time_blocks = schedule_items.find_all('div', + class_='schedule__time') + day_index = 0 + + for tb in time_blocks: + time_items = tb.find_all('div', class_='schedule__time-item') + time_range = "" + if len(time_items) >= 2: + start = time_items[0].get_text().strip().split()[0] + end = time_items[1].get_text().strip().split()[0] + time_range = f"{start} - {end}" + + next_elem = tb.find_next_sibling() + d_idx = 0 + + while next_elem and 'schedule__time' not in next_elem.get( + 'class', []): + if 'schedule__item' in next_elem.get('class', []): + lesson_div = next_elem.find('div', + class_='schedule__lesson') + if lesson_div and d_idx < len(day_names): + title_elem = lesson_div.find( + 'div', class_='schedule__discipline') + title = title_elem.get_text().strip( + ) if title_elem else "" + + if title: + type_elem = lesson_div.find( + 'div', class_='schedule__lesson-type-chip') + lesson_type = type_elem.get_text().strip( + ) if type_elem else "Лекция" + + place_elem = lesson_div.find( + 'div', class_='schedule__place') + room = place_elem.get_text().strip( + ) if place_elem else "" + + teacher_elem = lesson_div.find( + 'div', class_='schedule__teacher') + teacher = "" + if teacher_elem: + link = teacher_elem.find('a') + teacher = link.get_text().strip( + ) if link else teacher_elem.get_text( + ).strip() + + while len(days) <= d_idx: + days.append({ + 'day_name': + day_names[len(days)], + 'lessons': [] + }) + + days[d_idx]['lessons'].append({ + 'time': time_range, + 'title': title, + 'type': lesson_type, + 'teacher': teacher, + 'room': room + }) + d_idx += 1 + next_elem = next_elem.find_next_sibling() + day_index += 1 + + days = [d for d in days if d['lessons']] + + return { + 'mode': 'group', + 'id': group_id, + 'name': f'Группа {group_id}', + 'week': week or get_current_week(), + 'days': days + } + except Exception as e: + print(f"Ошибка: {e}") + return { + 'mode': 'group', + 'id': group_id, + 'name': f'Группа {group_id}', + 'week': week or get_current_week(), + 'days': [] + } + + +def parse_teacher_schedule(teacher_name, week): + """Парсинг расписания преподавателя""" + groups = get_groups() + all_lessons = [] + actual_week = week or get_current_week() + + for group in groups[:5]: + group_schedule = parse_group_schedule(group['id'], actual_week) + for day in group_schedule.get('days', []): + filtered = [] + for lesson in day.get('lessons', []): + if teacher_name.lower() in lesson.get('teacher', '').lower(): + filtered.append({ + 'time': lesson.get('time', ''), + 'title': lesson.get('title', ''), + 'type': lesson.get('type', ''), + 'room': lesson.get('room', ''), + 'group': group['name'] + }) + if filtered: + all_lessons.append({ + 'day_name': day['day_name'], + 'date': day.get('date', ''), + 'lessons': filtered + }) + + return { + 'mode': 'teacher', + 'id': teacher_name, + 'name': teacher_name, + 'week': actual_week, + 'days': all_lessons + } + + +@app.route('/') +def index(): + return send_from_directory('static', 'index.html') + + +@app.route('/css/') +def serve_css(path): + return send_from_directory('static/css', path) + + +@app.route('/js/') +def serve_js(path): + return send_from_directory('static/js', path) + + +@app.route('/api/groups') +def api_groups(): + return jsonify(get_groups()) + + +@app.route('/api/teachers') +def api_teachers(): + return jsonify(get_teachers()) + + +@app.route('/api/current-week') +def api_current_week(): + return jsonify({'week': get_current_week()}) + + +@app.route('/api/schedule/group') +def api_schedule_group(): + group_id = request.args.get('groupId') + week = request.args.get('week', type=int) + if not group_id: + return jsonify({'error': 'Не указана группа'}), 400 + return jsonify(parse_group_schedule(group_id, week)) + + +@app.route('/api/schedule/teacher') +def api_schedule_teacher(): + teacher_name = request.args.get('name') + week = request.args.get('week', type=int) + if not teacher_name: + return jsonify({'error': 'Не указан преподаватель'}), 400 + return jsonify(parse_teacher_schedule(teacher_name, week)) + + +if __name__ == '__main__': + print("\nСервер запущен: http://localhost:5000\n") + app.run(debug=True, host='0.0.0.0', port=5000) diff --git a/static/css/style.css b/static/css/style.css index 57998e674..e546440ed 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -284,21 +284,22 @@ body { .main { padding: 15px; } - + .schedule-table { display: none; } - + .mode-switch { flex-direction: column; } - + .week-controls { flex-wrap: wrap; } } @media (min-width: 769px) { + .mobile-day-nav, .mobile-lessons { display: none; diff --git a/static/index.html b/static/index.html index 4ade1aceb..5bd4c0c7c 100644 --- a/static/index.html +++ b/static/index.html @@ -1,11 +1,13 @@ - + + Расписание СГАУ - Самарский университет - + +

Расписание СГАУ

@@ -47,4 +49,5 @@

Расписание СГАУ

+ \ No newline at end of file diff --git a/static/js/app.js b/static/js/app.js new file mode 100644 index 000000000..abb5f2bc2 --- /dev/null +++ b/static/js/app.js @@ -0,0 +1,346 @@ +let currentMode = 'group'; +let currentGroupId = null; +let currentTeacherName = null; +let currentWeek = null; +let selectedWeek = null; +let currentDayIndex = 0; +let currentScheduleData = null; + +const groupMode = document.getElementById('groupMode'); +const teacherMode = document.getElementById('teacherMode'); +const groupSelect = document.getElementById('groupSelect'); +const teacherInput = document.getElementById('teacherInput'); +const teacherSelect = document.getElementById('teacherSelect'); +const weekNumberSpan = document.getElementById('weekNumber'); +const prevWeekBtn = document.getElementById('prevWeek'); +const nextWeekBtn = document.getElementById('nextWeek'); +const currentWeekBtn = document.getElementById('currentWeekBtn'); +const loader = document.getElementById('loader'); +const errorDiv = document.getElementById('error'); +const scheduleDiv = document.getElementById('schedule'); + +const DAYS_ORDER = ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота']; + +let isMobile = window.innerWidth <= 768; + +window.addEventListener('resize', () => { + isMobile = window.innerWidth <= 768; + if (currentScheduleData) { + if (isMobile) { + renderMobileSchedule(currentScheduleData); + } else { + renderDesktopSchedule(currentScheduleData); + } + } +}); + +document.addEventListener('DOMContentLoaded', () => { + setupEventListeners(); + loadGroups(); + loadCurrentWeek(); +}); + +// Настройка всех обработчиков событий +function setupEventListeners() { + document.querySelectorAll('.mode-btn').forEach(btn => { + btn.addEventListener('click', () => { + const mode = btn.dataset.mode; + switchMode(mode); + }); + }); + + groupSelect.addEventListener('change', () => { + currentGroupId = groupSelect.value; + if (currentGroupId) loadSchedule(); + }); + + let timeout; + teacherInput.addEventListener('input', () => { + clearTimeout(timeout); + const query = teacherInput.value.trim(); + if (query.length >= 2) { + timeout = setTimeout(() => searchTeachers(query), 500); + } else { + teacherSelect.classList.add('hidden'); + } + }); + + teacherSelect.addEventListener('change', () => { + currentTeacherName = teacherSelect.value; + if (currentTeacherName) loadSchedule(); + }); + + prevWeekBtn.addEventListener('click', () => { + if (selectedWeek > 1) { + selectedWeek--; + updateWeekDisplay(); + loadSchedule(); + } + }); + + nextWeekBtn.addEventListener('click', () => { + selectedWeek++; + updateWeekDisplay(); + loadSchedule(); + }); + + currentWeekBtn.addEventListener('click', () => { + if (currentWeek) { + selectedWeek = currentWeek; + updateWeekDisplay(); + loadSchedule(); + } + }); +} + +// Переключение между режимами "По группе" и "По преподавателю" +function switchMode(mode) { + currentMode = mode; + + document.querySelectorAll('.mode-btn').forEach(btn => { + if (btn.dataset.mode === mode) { + btn.classList.add('active'); + } else { + btn.classList.remove('active'); + } + }); + + if (mode === 'group') { + groupMode.classList.remove('hidden'); + teacherMode.classList.add('hidden'); + if (currentGroupId) loadSchedule(); + } else { + groupMode.classList.add('hidden'); + teacherMode.classList.remove('hidden'); + if (currentTeacherName) loadSchedule(); + } +} + +// Загрузка списка групп +function loadGroups() { + fetch('/api/groups') + .then(res => res.json()) + .then(groups => { + groupSelect.innerHTML = ''; + groups.forEach(g => { + groupSelect.innerHTML += ``; + }); + if (groups.length) { + currentGroupId = groups[0].id; + groupSelect.value = currentGroupId; + loadSchedule(); + } + }) + .catch(() => showError('Ошибка загрузки групп')); +} + +// Поиск преподавателей по введённой фамилии +function searchTeachers(query) { + fetch(`/api/teachers?q=${encodeURIComponent(query)}`) + .then(res => res.json()) + .then(teachers => { + teacherSelect.innerHTML = ''; + teachers.forEach(t => { + teacherSelect.innerHTML += ``; + }); + teacherSelect.classList.remove('hidden'); + }) + .catch(() => showError('Ошибка поиска')); +} + +// Загрузка текущей учебной недели +function loadCurrentWeek() { + fetch('/api/current-week') + .then(res => res.json()) + .then(data => { + currentWeek = data.week; + selectedWeek = currentWeek; + updateWeekDisplay(); + }) + .catch(() => { + currentWeek = 31; + selectedWeek = 31; + updateWeekDisplay(); + }); +} + +function updateWeekDisplay() { + weekNumberSpan.textContent = `${selectedWeek} неделя`; +} + +// Загрузка расписания (группы или преподавателя) +function loadSchedule() { + showLoader(true); + hideError(); + currentDayIndex = 0; + + let url; + if (currentMode === 'group') { + url = `/api/schedule/group?groupId=${currentGroupId}&week=${selectedWeek}`; + } else { + url = `/api/schedule/teacher?name=${encodeURIComponent(currentTeacherName)}&week=${selectedWeek}`; + } + + fetch(url) + .then(res => res.json()) + .then(data => { + currentScheduleData = data; + if (isMobile) { + renderMobileSchedule(data); + } else { + renderDesktopSchedule(data); + } + showLoader(false); + }) + .catch(() => { + showError('Ошибка загрузки расписания'); + showLoader(false); + }); +} + +// Отображение расписания +function renderDesktopSchedule(data) { + if (!data.days || data.days.length === 0) { + scheduleDiv.innerHTML = '
На этой неделе занятий нет
'; + return; + } + + const daysMap = {}; + data.days.forEach(day => { + daysMap[day.day_name] = day; + }); + + const allTimes = new Set(); + data.days.forEach(day => { + day.lessons.forEach(l => { + if (l.time) allTimes.add(l.time); + }); + }); + + const times = Array.from(allTimes).sort((a, b) => { + return parseInt(a.split('-')[0]) - parseInt(b.split('-')[0]); + }); + + let html = '
'; + DAYS_ORDER.forEach(day => html += ``); + html += ''; + + for (const time of times) { + html += ``; + + for (const day of DAYS_ORDER) { + const dayData = daysMap[day]; + let lessons = []; + if (dayData) { + lessons = dayData.lessons.filter(l => l.time === time); + } + + html += ``; + } + html += ``; + } + + html += '
Время${day}
${time}`; + if (lessons.length) { + lessons.forEach(l => { + html += `
+
${l.title}
+
${l.type || 'Лекция'}
`; + if (l.teacher) html += `
${l.teacher}
`; + if (l.room) html += `
${l.room}
`; + if (l.group) html += `
${l.group}
`; + html += `
`; + }); + } else { + html += '—'; + } + html += `
'; + scheduleDiv.innerHTML = html; +} + +// Отображение расписания на мобильных устройствах +function renderMobileSchedule(data) { + if (!data.days || data.days.length === 0) { + scheduleDiv.innerHTML = '
На этой неделе занятий нет
'; + return; + } + + if (currentDayIndex >= data.days.length) { + currentDayIndex = 0; + } + + const currentDay = data.days[currentDayIndex]; + + let html = ` +
+ +
+
${currentDay.day_name}
+
${currentDay.date || ''}
+
+ +
+
+ `; + + if (currentDay.lessons.length === 0) { + html += '
Занятий нет
'; + } else { + currentDay.lessons.forEach(lesson => { + html += ` +
+
${lesson.time || 'Время не указано'}
+
${lesson.title}
+
${lesson.type || 'Лекция'}
+ `; + if (lesson.teacher) html += `
${lesson.teacher}
`; + if (lesson.room) html += `
${lesson.room}
`; + if (lesson.group) html += `
${lesson.group}
`; + html += `
`; + }); + } + + html += '
'; + scheduleDiv.innerHTML = html; + + const prevBtn = document.getElementById('prevDayBtn'); + const nextBtn = document.getElementById('nextDayBtn'); + + if (prevBtn) { + prevBtn.addEventListener('click', () => { + if (currentDayIndex > 0) { + currentDayIndex--; + renderMobileSchedule(currentScheduleData); + } + }); + } + + if (nextBtn) { + nextBtn.addEventListener('click', () => { + if (currentDayIndex < currentScheduleData.days.length - 1) { + currentDayIndex++; + renderMobileSchedule(currentScheduleData); + } + }); + } +} + +function showLoader(show) { + if (show) { + loader.classList.remove('hidden'); + } else { + loader.classList.add('hidden'); + } +} + +function showError(msg) { + errorDiv.textContent = msg; + errorDiv.classList.remove('hidden'); + setTimeout(() => { + errorDiv.classList.add('hidden'); + }, 3000); +} + +function hideError() { + errorDiv.classList.add('hidden'); +} \ No newline at end of file