// public/main.js – страница записи родителей let allLessons = []; // Загрузка опций для выпадающих списков async function loadFilterOptions() { try { const [classes, teachers, topics] = await Promise.all([ fetch('/api/filter-options/class-names').then(r => r.json()), fetch('/api/filter-options/teachers').then(r => r.json()), fetch('/api/filter-options/topics').then(r => r.json()) ]); // Сохраняем полные списки для дальнейшей фильтрации window.allClassNames = classes; window.allTeachers = teachers; window.allTopics = topics; populateSelect('filterClass', window.allClassNames, 'Все классы'); populateSelect('filterTeacher', window.allTeachers, 'Все учителя'); populateSelect('filterTopic', window.allTopics, 'Все темы'); } catch (err) { console.error('Ошибка загрузки опций фильтров', err); } } function populateSelect(selectId, options, defaultLabel) { const select = document.getElementById(selectId); if (!select) return; const currentValue = select.value; select.innerHTML = ``; options.forEach(opt => { const option = document.createElement('option'); option.value = opt; option.textContent = opt; select.appendChild(option); }); // Восстанавливаем выбранное значение, если оно ещё допустимо if (currentValue && options.includes(currentValue)) { select.value = currentValue; } else { select.value = ''; } } // Загрузка всех уроков с сервера async function loadLessons() { try { const res = await fetch('/api/lessons'); allLessons = await res.json(); updateDependentFilters(); // первоначальное построение зависимых списков applyFilters(); } catch (err) { console.error('Ошибка загрузки уроков', err); document.getElementById('lessonsContainer').innerHTML = '

Ошибка загрузки данных

'; } } // Обновление зависимых выпадающих списков на основе текущих фильтров function updateDependentFilters() { const selectedClass = document.getElementById('filterClass').value; const selectedTeacher = document.getElementById('filterTeacher').value; const selectedTopic = document.getElementById('filterTopic').value; // Фильтруем уроки по выбранным значениям (если они не пустые) let filteredLessons = allLessons; if (selectedClass) { filteredLessons = filteredLessons.filter(l => l.class_name === selectedClass); } if (selectedTeacher) { filteredLessons = filteredLessons.filter(l => l.teacher === selectedTeacher); } if (selectedTopic) { filteredLessons = filteredLessons.filter(l => l.topic === selectedTopic); } // Извлекаем уникальные значения для каждого поля const availableClasses = [...new Set(filteredLessons.map(l => l.class_name))].sort(); const availableTeachers = [...new Set(filteredLessons.map(l => l.teacher))].sort(); const availableTopics = [...new Set(filteredLessons.map(l => l.topic).filter(t => t))].sort(); // Обновляем select, сохраняя текущие значения, если они допустимы const classSelect = document.getElementById('filterClass'); const teacherSelect = document.getElementById('filterTeacher'); const topicSelect = document.getElementById('filterTopic'); const oldClass = classSelect.value; const oldTeacher = teacherSelect.value; const oldTopic = topicSelect.value; populateSelect('filterClass', availableClasses, 'Все классы'); populateSelect('filterTeacher', availableTeachers, 'Все учителя'); populateSelect('filterTopic', availableTopics, 'Все темы'); // Если старое значение не было сброшено populateSelect, восстанавливаем if (oldClass && availableClasses.includes(oldClass)) classSelect.value = oldClass; else classSelect.value = ''; if (oldTeacher && availableTeachers.includes(oldTeacher)) teacherSelect.value = oldTeacher; else teacherSelect.value = ''; if (oldTopic && availableTopics.includes(oldTopic)) topicSelect.value = oldTopic; else topicSelect.value = ''; } // Применение фильтров + скрытие уроков без свободных мест function applyFilters() { const classFilter = document.getElementById('filterClass').value; const teacherFilter = document.getElementById('filterTeacher').value; const topicFilter = document.getElementById('filterTopic').value; // Сначала отбираем только доступные уроки (есть свободные места) let filtered = allLessons.filter(lesson => lesson.available === true); // Точное совпадение для select (не частичное) if (classFilter) filtered = filtered.filter(lesson => lesson.class_name === classFilter); if (teacherFilter) filtered = filtered.filter(lesson => lesson.teacher === teacherFilter); if (topicFilter) filtered = filtered.filter(lesson => lesson.topic === topicFilter); renderLessons(filtered); } // Отрисовка карточек уроков + обновление счётчика function renderLessons(lessons) { const container = document.getElementById('lessonsContainer'); const counterSpan = document.getElementById('availableCount'); if (!container) return; if (lessons.length === 0) { container.innerHTML = '

Нет доступных уроков

'; if (counterSpan) counterSpan.textContent = '0'; return; } container.innerHTML = lessons.map(lesson => `

${escapeHtml(lesson.class_name)} | ${escapeHtml(lesson.subject)}

Учитель: ${escapeHtml(lesson.teacher)}

Тема: ${escapeHtml(lesson.topic || '—')}

Свободных мест: ${lesson.max_slots - lesson.current_slots} из ${lesson.max_slots}
`).join(''); if (counterSpan) counterSpan.textContent = `${lessons.length}`; document.querySelectorAll('.lesson-card').forEach(card => { card.addEventListener('click', () => openModal(card.dataset.id)); }); } // Модальное окно записи (без изменений) function setupModal() { const modal = document.getElementById('modal'); const closeSpan = modal.querySelector('.close'); const form = document.getElementById('registrationForm'); closeSpan.onclick = () => modal.style.display = 'none'; window.onclick = (e) => { if (e.target === modal) modal.style.display = 'none'; }; form.addEventListener('submit', async (e) => { e.preventDefault(); const lessonId = document.getElementById('lessonId').value; const parentName = document.getElementById('parentName').value.trim(); const parentPhone = document.getElementById('parentPhone').value.trim(); const messageDiv = document.getElementById('modalMessage'); if (!parentName || !parentPhone) { messageDiv.innerHTML = 'Заполните все поля'; return; } try { const res = await fetch('/api/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ lesson_id: lessonId, parent_name: parentName, parent_phone: parentPhone }) }); const data = await res.json(); if (res.ok) { messageDiv.innerHTML = '✅ Вы успешно записаны!'; setTimeout(() => { modal.style.display = 'none'; loadLessons(); }, 1500); } else { messageDiv.innerHTML = `${data.error || 'Ошибка'}`; } } catch (err) { messageDiv.innerHTML = 'Ошибка сервера'; } }); } function openModal(lessonId) { const modal = document.getElementById('modal'); const lesson = allLessons.find(l => l.id == lessonId); if (!lesson) return; document.getElementById('lessonId').value = lessonId; document.getElementById('modalLessonInfo').innerHTML = ` ${escapeHtml(lesson.class_name)}
${escapeHtml(lesson.subject)} — ${escapeHtml(lesson.teacher)}
${lesson.date} ${lesson.time} `; document.getElementById('modalMessage').innerHTML = ''; document.getElementById('registrationForm').reset(); modal.style.display = 'flex'; } function escapeHtml(str) { if (!str) return ''; return str.replace(/[&<>]/g, function(m) { if (m === '&') return '&'; if (m === '<') return '<'; if (m === '>') return '>'; return m; }); } // Инициализация document.addEventListener('DOMContentLoaded', async () => { await loadFilterOptions(); await loadLessons(); setupModal(); // Обработчики изменений фильтров const classSelect = document.getElementById('filterClass'); const teacherSelect = document.getElementById('filterTeacher'); const topicSelect = document.getElementById('filterTopic'); function handleFilterChange() { updateDependentFilters(); applyFilters(); } classSelect?.addEventListener('change', handleFilterChange); teacherSelect?.addEventListener('change', handleFilterChange); topicSelect?.addEventListener('change', handleFilterChange); document.getElementById('resetFilter')?.addEventListener('click', () => { classSelect.value = ''; teacherSelect.value = ''; topicSelect.value = ''; updateDependentFilters(); applyFilters(); }); });