Files
OpenLesson/public/main.js

246 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 = `<option value="">${defaultLabel}</option>`;
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 = '<p>Ошибка загрузки данных</p>';
}
}
// Обновление зависимых выпадающих списков на основе текущих фильтров
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 = '<p style="text-align:center; grid-column:1/-1;">Нет доступных уроков</p>';
if (counterSpan) counterSpan.textContent = '0';
return;
}
container.innerHTML = lessons.map(lesson => `
<div class="lesson-card" data-id="${lesson.id}">
<h3>${escapeHtml(lesson.class_name)} | ${escapeHtml(lesson.subject)}</h3>
<p><strong>Учитель:</strong> ${escapeHtml(lesson.teacher)}</p>
<p><strong>Тема:</strong> ${escapeHtml(lesson.topic || '—')}</p>
<div class="slots">
Свободных мест: ${lesson.max_slots - lesson.current_slots} из ${lesson.max_slots}
</div>
</div>
`).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 = '<span style="color:red">Заполните все поля</span>';
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 = '<span style="color:green">✅ Вы успешно записаны!</span>';
setTimeout(() => {
modal.style.display = 'none';
loadLessons();
}, 1500);
} else {
messageDiv.innerHTML = `<span style="color:red">${data.error || 'Ошибка'}</span>`;
}
} catch (err) {
messageDiv.innerHTML = '<span style="color:red">Ошибка сервера</span>';
}
});
}
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 = `
<strong>${escapeHtml(lesson.class_name)}</strong><br>
${escapeHtml(lesson.subject)}${escapeHtml(lesson.teacher)}<br>
<small>${lesson.date} ${lesson.time}</small>
`;
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 '&amp;';
if (m === '<') return '&lt;';
if (m === '>') return '&gt;';
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();
});
});