This commit is contained in:
Калугин Олег Александрович
2026-04-13 12:06:31 +00:00
committed by GitVerse
parent ae3227f127
commit 2d007d2359
8 changed files with 318 additions and 99 deletions

View File

@@ -14,7 +14,7 @@
<main> <main>
<div class="admin-controls"> <div class="admin-controls">
<button id="addLessonBtn">+ Добавить урок</button> <button id="addLessonBtn">+ Добавить урок</button>
<div class="filters"> <div class="filters">
<div class="filter-group"> <div class="filter-group">
<label>Класс</label> <label>Класс</label>
<select id="filterClass"> <select id="filterClass">
@@ -36,13 +36,13 @@
<input type="text" id="filterParallel" placeholder="Параллель (цифра)"> <input type="text" id="filterParallel" placeholder="Параллель (цифра)">
<button id="applyFilters">Применить</button> <button id="applyFilters">Применить</button>
<button id="resetFilters" class="reset-btn">Сбросить</button> <button id="resetFilters" class="reset-btn">Сбросить</button>
</div> </div>
</div> </div>
<div id="lessonsList" class="lessons-list"></div> <div id="lessonsList" class="lessons-list"></div>
<!-- Импорт JSON (без даты/времени) --> <!-- Импорт JSON / Excel -->
<div class="import-section"> <div class="import-section">
<h3>Импорт уроков из JSON</h3> <h3>Импорт уроков из JSON / Excel</h3>
<input type="file" id="jsonFileInput" accept=".json, .xlsx, .xls"> <input type="file" id="jsonFileInput" accept=".json, .xlsx, .xls">
<div class="import-params"> <div class="import-params">
<label>Макс. мест (сколько родителей может записаться): <label>Макс. мест (сколько родителей может записаться):
@@ -54,7 +54,9 @@
<h4>Предпросмотр (первые 20 записей)</h4> <h4>Предпросмотр (первые 20 записей)</h4>
<div style="overflow-x:auto;"> <div style="overflow-x:auto;">
<table id="previewTable" border="1" cellpadding="5"> <table id="previewTable" border="1" cellpadding="5">
<thead><tr><th>Класс</th><th>Предмет</th><th>Учитель</th><th>Тема</th></tr></thead> <thead>
<tr><th>Класс</th><th>Предмет</th><th>Учитель</th><th>Тема</th></tr>
</thead>
<tbody></tbody> <tbody></tbody>
</table> </table>
</div> </div>
@@ -76,8 +78,8 @@
<label>Учитель: <input type="text" id="teacher" required></label> <label>Учитель: <input type="text" id="teacher" required></label>
<label>Тема урока: <input type="text" id="topic"></label> <label>Тема урока: <input type="text" id="topic"></label>
<label>Макс. мест: <input type="number" id="maxSlots" required></label> <label>Макс. мест: <input type="number" id="maxSlots" required></label>
<label>Дата: <input type="date" id="date" required></label> <label>Дата: <input type="date" id="date"></label>
<label>Время: <input type="time" id="time" required></label> <label>Время: <input type="time" id="time"></label>
<button type="submit">Сохранить</button> <button type="submit">Сохранить</button>
</form> </form>
</div> </div>
@@ -92,7 +94,7 @@
</div> </div>
</div> </div>
</main> </main>
<script src="https://cdn.sheetjs.com/xlsx-0.20.2/package/dist/xlsx.full.min.js"></script> <!--<script src="https://cdn.sheetjs.com/xlsx-0.20.2/package/dist/xlsx.full.min.js"></script>-->
<script src="admin.js"></script> <script src="admin.js"></script>
</body> </body>
</html> </html>

View File

@@ -121,13 +121,19 @@ async function loadLessons(filters = {}) {
section.className = 'class-group'; section.className = 'class-group';
section.innerHTML = `<h2>${className}</h2>`; section.innerHTML = `<h2>${className}</h2>`;
classLessons.forEach(lesson => { classLessons.forEach(lesson => {
let dateTimeStr = '';
if (lesson.topic === 'Консультация' && lesson.date && lesson.time) {
dateTimeStr = `${lesson.date} ${lesson.time}`;
} else {
dateTimeStr = 'Согласно расписания';
}
const div = document.createElement('div'); const div = document.createElement('div');
div.className = 'lesson-item'; div.className = 'lesson-item';
div.innerHTML = ` div.innerHTML = `
<div> <div>
<strong>${lesson.subject}</strong> — ${lesson.teacher}<br> <strong>${lesson.subject}</strong> — ${lesson.teacher}<br>
<em>Тема: ${lesson.topic || '—'}</em><br> <em>Тема: ${lesson.topic || '—'}</em><br>
${lesson.date} ${lesson.time} | Места: ${lesson.current_slots}/${lesson.max_slots} ${dateTimeStr} | Мест свободно: ${lesson.max_slots - lesson.current_slots}
</div> </div>
<div class="lesson-actions"> <div class="lesson-actions">
<button class="viewRegBtn" data-id="${lesson.id}">Записи</button> <button class="viewRegBtn" data-id="${lesson.id}">Записи</button>
@@ -178,8 +184,8 @@ function openLessonModal(id = null) {
document.getElementById('teacher').value = lesson.teacher; document.getElementById('teacher').value = lesson.teacher;
document.getElementById('topic').value = lesson.topic || ''; document.getElementById('topic').value = lesson.topic || '';
document.getElementById('maxSlots').value = lesson.max_slots; document.getElementById('maxSlots').value = lesson.max_slots;
document.getElementById('date').value = lesson.date; document.getElementById('date').value = lesson.date || '';
document.getElementById('time').value = lesson.time; document.getElementById('time').value = lesson.time || '';
} }
}); });
} }
@@ -252,7 +258,7 @@ function setupEventListeners() {
window.location.href = '/'; window.location.href = '/';
}); });
// ========== ИМПОРТ JSON / XLSX ========== // Импорт
function parseExcelToRecords(file) { function parseExcelToRecords(file) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const reader = new FileReader(); const reader = new FileReader();

View File

@@ -66,7 +66,7 @@ async function loadRegistrations() {
document.getElementById('recordsCount').innerText = `Найдено: ${currentRegistrations.length}`; document.getElementById('recordsCount').innerText = `Найдено: ${currentRegistrations.length}`;
} catch (err) { } catch (err) {
console.error(err); console.error(err);
document.getElementById('tableBody').innerHTML = '<tr><td colspan="9">Ошибка загрузки</td></tr>'; document.getElementById('tableBody').innerHTML = '<td><td colspan="9">Ошибка загрузки</td></tr>';
} }
} }
@@ -76,7 +76,16 @@ function renderTable(registrations) {
tbody.innerHTML = '<tr><td colspan="9">Нет записей</td></tr>'; tbody.innerHTML = '<tr><td colspan="9">Нет записей</td></tr>';
return; return;
} }
tbody.innerHTML = registrations.map(reg => ` tbody.innerHTML = registrations.map(reg => {
let dateStr = '', timeStr = '';
if (reg.topic === 'Консультация' && reg.date && reg.time) {
dateStr = escapeHtml(reg.date);
timeStr = escapeHtml(reg.time);
} else {
dateStr = 'Согласно расписания';
timeStr = '';
}
return `
<tr> <tr>
<td>${escapeHtml(reg.parent_name)}</td> <td>${escapeHtml(reg.parent_name)}</td>
<td>${escapeHtml(reg.parent_phone)}</td> <td>${escapeHtml(reg.parent_phone)}</td>
@@ -84,11 +93,12 @@ function renderTable(registrations) {
<td>${escapeHtml(reg.subject)}</td> <td>${escapeHtml(reg.subject)}</td>
<td>${escapeHtml(reg.teacher)}</td> <td>${escapeHtml(reg.teacher)}</td>
<td>${escapeHtml(reg.topic || '—')}</td> <td>${escapeHtml(reg.topic || '—')}</td>
<td>${escapeHtml(reg.date)}</td> <td>${dateStr}</td>
<td>${escapeHtml(reg.time)}</td> <td>${timeStr}</td>
<td>${new Date(reg.created_at).toLocaleString()}</td> <td>${new Date(reg.created_at).toLocaleString()}</td>
</tr> </tr>
`).join(''); `;
}).join('');
} }
function escapeHtml(str) { function escapeHtml(str) {
@@ -106,23 +116,31 @@ function exportToCSV() {
alert('Нет данных для экспорта'); alert('Нет данных для экспорта');
return; return;
} }
// Заголовки
const headers = ['ФИО родителя', 'Телефон', 'Класс', 'Предмет', 'Учитель', 'Тема урока', 'Дата урока', 'Время', 'Дата регистрации']; const headers = ['ФИО родителя', 'Телефон', 'Класс', 'Предмет', 'Учитель', 'Тема урока', 'Дата урока', 'Время', 'Дата регистрации'];
const rows = currentRegistrations.map(reg => [ const rows = currentRegistrations.map(reg => {
let dateVal = '', timeVal = '';
if (reg.topic === 'Консультация' && reg.date && reg.time) {
dateVal = reg.date;
timeVal = reg.time;
} else {
dateVal = 'Согласно расписания';
timeVal = '';
}
return [
reg.parent_name, reg.parent_name,
reg.parent_phone, reg.parent_phone,
reg.class_name, reg.class_name,
reg.subject, reg.subject,
reg.teacher, reg.teacher,
reg.topic || '', reg.topic || '',
reg.date, dateVal,
reg.time, timeVal,
new Date(reg.created_at).toLocaleString() new Date(reg.created_at).toLocaleString()
]); ];
});
const csvContent = [headers, ...rows] const csvContent = [headers, ...rows]
.map(row => row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(';')) .map(row => row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(';'))
.join('\n'); .join('\n');
// Добавляем BOM для корректной поддержки кириллицы в Excel
const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' }); const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a'); const link = document.createElement('a');
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);

77
public/k.html Normal file
View File

@@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Управление корпусами учителей</title>
<link rel="stylesheet" href="style.css">
<style>
.teachers-table {
width: 100%;
border-collapse: collapse;
background: white;
border-radius: 1rem;
overflow: hidden;
}
.teachers-table th, .teachers-table td {
border: 1px solid #e2e8f0;
padding: 0.75rem;
text-align: left;
}
.teachers-table th {
background: #f1f5f9;
}
.campus-select {
padding: 0.4rem;
border-radius: 0.5rem;
border: 1px solid #cbd5e1;
}
.save-btn {
background: #2d6a4f;
margin: 1rem 0;
}
.message {
margin-top: 1rem;
padding: 0.5rem;
border-radius: 0.5rem;
}
.success {
background: #d1fae5;
color: #065f46;
}
.error {
background: #fee2e2;
color: #991b1b;
}
</style>
</head>
<body>
<header>
<h1>Управление корпусами учителей</h1>
<div id="userInfo"></div>
<button id="logoutBtn">Выйти</button>
</header>
<main style="padding: 2rem;">
<div class="header-actions">
<h2>Список учителей</h2>
<button id="saveAllBtn" class="save-btn">💾 Сохранить все изменения</button>
</div>
<div style="overflow-x: auto;">
<table class="teachers-table" id="teachersTable">
<thead>
<tr>
<th>Учитель</th>
<th>Предмет</th>
<th>Корпус</th>
</tr>
</thead>
<tbody id="tableBody">
<tr><td colspan="3">Загрузка...</td></tr>
</tbody>
</table>
</div>
<div id="message" class="message" style="display:none;"></div>
</main>
<script src="k.js"></script>
</body>
</html>

115
public/k.js Normal file
View File

@@ -0,0 +1,115 @@
// public/k.js управление привязкой учителей к корпусам
let currentUser = null;
let teachersList = []; // [{ id, name, subject, campus }]
document.addEventListener('DOMContentLoaded', async () => {
await checkAuth();
await loadTeachers();
setupEventListeners();
});
async function checkAuth() {
try {
const res = await fetch('/api/me');
const data = await res.json();
if (!data.authenticated || data.user.role !== 'admin') {
window.location.href = '/login.html';
return;
}
currentUser = data.user;
document.getElementById('userInfo').innerHTML = `👋 ${currentUser.full_name} (${currentUser.role})`;
} catch (err) {
window.location.href = '/login.html';
}
}
async function loadTeachers() {
try {
const res = await fetch('/api/teachers');
teachersList = await res.json();
renderTable();
} catch (err) {
console.error(err);
document.getElementById('tableBody').innerHTML = '<tr><td colspan="3">Ошибка загрузки</td></tr>';
}
}
function renderTable() {
const tbody = document.getElementById('tableBody');
if (!teachersList.length) {
tbody.innerHTML = '<tr><td colspan="3">Нет учителей</td></tr>';
return;
}
tbody.innerHTML = teachersList.map(teacher => `
<tr data-id="${teacher.id}">
<td>${escapeHtml(teacher.name)}</td>
<td>${escapeHtml(teacher.subject || '—')}</td>
<td>
<select class="campus-select" data-id="${teacher.id}">
<option value="" ${teacher.campus === '' ? 'selected' : ''}>Оба корпуса</option>
<option value="Феофанова 10" ${teacher.campus === 'Феофанова 10' ? 'selected' : ''}>Феофанова 10</option>
<option value="Цветоносная 2" ${teacher.campus === 'Цветоносная 2' ? 'selected' : ''}>Цветоносная 2</option>
</select>
</td>
</tr>
`).join('');
}
async function saveAllChanges() {
const updates = [];
document.querySelectorAll('.campus-select').forEach(select => {
const teacherId = parseInt(select.dataset.id);
const newCampus = select.value;
updates.push({ id: teacherId, campus: newCampus });
});
const messageDiv = document.getElementById('message');
messageDiv.style.display = 'block';
messageDiv.className = 'message';
messageDiv.innerHTML = 'Сохранение...';
try {
const res = await fetch('/api/teachers/campus/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ updates })
});
const data = await res.json();
if (res.ok) {
messageDiv.className = 'message success';
messageDiv.innerHTML = '✅ Изменения сохранены';
setTimeout(() => messageDiv.style.display = 'none', 3000);
// обновляем локальные данные
teachersList = teachersList.map(t => {
const update = updates.find(u => u.id === t.id);
if (update) t.campus = update.campus;
return t;
});
} else {
throw new Error(data.error || 'Ошибка сохранения');
}
} catch (err) {
messageDiv.className = 'message error';
messageDiv.innerHTML = `❌ Ошибка: ${err.message}`;
setTimeout(() => messageDiv.style.display = 'none', 3000);
}
}
function setupEventListeners() {
document.getElementById('saveAllBtn')?.addEventListener('click', saveAllChanges);
document.getElementById('logoutBtn')?.addEventListener('click', async () => {
await fetch('/api/logout', { method: 'POST' });
window.location.href = '/';
});
}
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;
});
}

View File

@@ -2,7 +2,6 @@
let allLessons = []; let allLessons = [];
// Загрузка опций для выпадающих списков
async function loadFilterOptions() { async function loadFilterOptions() {
try { try {
const [classes, teachers, topics] = await Promise.all([ const [classes, teachers, topics] = await Promise.all([
@@ -10,7 +9,6 @@ async function loadFilterOptions() {
fetch('/api/filter-options/teachers').then(r => r.json()), fetch('/api/filter-options/teachers').then(r => r.json()),
fetch('/api/filter-options/topics').then(r => r.json()) fetch('/api/filter-options/topics').then(r => r.json())
]); ]);
// Сохраняем полные списки для дальнейшей фильтрации
window.allClassNames = classes; window.allClassNames = classes;
window.allTeachers = teachers; window.allTeachers = teachers;
window.allTopics = topics; window.allTopics = topics;
@@ -34,7 +32,6 @@ function populateSelect(selectId, options, defaultLabel) {
option.textContent = opt; option.textContent = opt;
select.appendChild(option); select.appendChild(option);
}); });
// Восстанавливаем выбранное значение, если оно ещё допустимо
if (currentValue && options.includes(currentValue)) { if (currentValue && options.includes(currentValue)) {
select.value = currentValue; select.value = currentValue;
} else { } else {
@@ -42,12 +39,11 @@ function populateSelect(selectId, options, defaultLabel) {
} }
} }
// Загрузка всех уроков с сервера
async function loadLessons() { async function loadLessons() {
try { try {
const res = await fetch('/api/lessons'); const res = await fetch('/api/lessons');
allLessons = await res.json(); allLessons = await res.json();
updateDependentFilters(); // первоначальное построение зависимых списков updateDependentFilters();
applyFilters(); applyFilters();
} catch (err) { } catch (err) {
console.error('Ошибка загрузки уроков', err); console.error('Ошибка загрузки уроков', err);
@@ -55,13 +51,11 @@ async function loadLessons() {
} }
} }
// Обновление зависимых выпадающих списков на основе текущих фильтров
function updateDependentFilters() { function updateDependentFilters() {
const selectedClass = document.getElementById('filterClass').value; const selectedClass = document.getElementById('filterClass').value;
const selectedTeacher = document.getElementById('filterTeacher').value; const selectedTeacher = document.getElementById('filterTeacher').value;
const selectedTopic = document.getElementById('filterTopic').value; const selectedTopic = document.getElementById('filterTopic').value;
// Фильтруем уроки по выбранным значениям (если они не пустые)
let filteredLessons = allLessons; let filteredLessons = allLessons;
if (selectedClass) { if (selectedClass) {
filteredLessons = filteredLessons.filter(l => l.class_name === selectedClass); filteredLessons = filteredLessons.filter(l => l.class_name === selectedClass);
@@ -73,12 +67,10 @@ function updateDependentFilters() {
filteredLessons = filteredLessons.filter(l => l.topic === selectedTopic); filteredLessons = filteredLessons.filter(l => l.topic === selectedTopic);
} }
// Извлекаем уникальные значения для каждого поля
const availableClasses = [...new Set(filteredLessons.map(l => l.class_name))].sort(); const availableClasses = [...new Set(filteredLessons.map(l => l.class_name))].sort();
const availableTeachers = [...new Set(filteredLessons.map(l => l.teacher))].sort(); const availableTeachers = [...new Set(filteredLessons.map(l => l.teacher))].sort();
const availableTopics = [...new Set(filteredLessons.map(l => l.topic).filter(t => t))].sort(); const availableTopics = [...new Set(filteredLessons.map(l => l.topic).filter(t => t))].sort();
// Обновляем select, сохраняя текущие значения, если они допустимы
const classSelect = document.getElementById('filterClass'); const classSelect = document.getElementById('filterClass');
const teacherSelect = document.getElementById('filterTeacher'); const teacherSelect = document.getElementById('filterTeacher');
const topicSelect = document.getElementById('filterTopic'); const topicSelect = document.getElementById('filterTopic');
@@ -91,7 +83,6 @@ function updateDependentFilters() {
populateSelect('filterTeacher', availableTeachers, 'Все учителя'); populateSelect('filterTeacher', availableTeachers, 'Все учителя');
populateSelect('filterTopic', availableTopics, 'Все темы'); populateSelect('filterTopic', availableTopics, 'Все темы');
// Если старое значение не было сброшено populateSelect, восстанавливаем
if (oldClass && availableClasses.includes(oldClass)) classSelect.value = oldClass; if (oldClass && availableClasses.includes(oldClass)) classSelect.value = oldClass;
else classSelect.value = ''; else classSelect.value = '';
if (oldTeacher && availableTeachers.includes(oldTeacher)) teacherSelect.value = oldTeacher; if (oldTeacher && availableTeachers.includes(oldTeacher)) teacherSelect.value = oldTeacher;
@@ -100,16 +91,12 @@ function updateDependentFilters() {
else topicSelect.value = ''; else topicSelect.value = '';
} }
// Применение фильтров + скрытие уроков без свободных мест
function applyFilters() { function applyFilters() {
const classFilter = document.getElementById('filterClass').value; const classFilter = document.getElementById('filterClass').value;
const teacherFilter = document.getElementById('filterTeacher').value; const teacherFilter = document.getElementById('filterTeacher').value;
const topicFilter = document.getElementById('filterTopic').value; const topicFilter = document.getElementById('filterTopic').value;
// Сначала отбираем только доступные уроки (есть свободные места)
let filtered = allLessons.filter(lesson => lesson.available === true); let filtered = allLessons.filter(lesson => lesson.available === true);
// Точное совпадение для select (не частичное)
if (classFilter) filtered = filtered.filter(lesson => lesson.class_name === classFilter); if (classFilter) filtered = filtered.filter(lesson => lesson.class_name === classFilter);
if (teacherFilter) filtered = filtered.filter(lesson => lesson.teacher === teacherFilter); if (teacherFilter) filtered = filtered.filter(lesson => lesson.teacher === teacherFilter);
if (topicFilter) filtered = filtered.filter(lesson => lesson.topic === topicFilter); if (topicFilter) filtered = filtered.filter(lesson => lesson.topic === topicFilter);
@@ -117,7 +104,6 @@ function applyFilters() {
renderLessons(filtered); renderLessons(filtered);
} }
// Отрисовка карточек уроков + обновление счётчика
function renderLessons(lessons) { function renderLessons(lessons) {
const container = document.getElementById('lessonsContainer'); const container = document.getElementById('lessonsContainer');
const counterSpan = document.getElementById('availableCount'); const counterSpan = document.getElementById('availableCount');
@@ -129,16 +115,27 @@ function renderLessons(lessons) {
return; return;
} }
container.innerHTML = lessons.map(lesson => ` container.innerHTML = lessons.map(lesson => {
// Определяем, что показывать в строке времени
let timeHtml = '';
if (lesson.topic === 'Консультация' && lesson.date && lesson.time) {
timeHtml = `<p><strong>Дата/время:</strong> ${escapeHtml(lesson.date)} ${escapeHtml(lesson.time)}</p>`;
} else {
timeHtml = `<p><strong>Время:</strong> Согласно расписания</p>`;
}
return `
<div class="lesson-card" data-id="${lesson.id}"> <div class="lesson-card" data-id="${lesson.id}">
<h3>${escapeHtml(lesson.class_name)} | ${escapeHtml(lesson.subject)}</h3> <h3>${escapeHtml(lesson.class_name)} | ${escapeHtml(lesson.subject)}</h3>
<p><strong>Учитель:</strong> ${escapeHtml(lesson.teacher)}</p> <p><strong>Учитель:</strong> ${escapeHtml(lesson.teacher)}</p>
<p><strong>Тема:</strong> ${escapeHtml(lesson.topic || '—')}</p> <p><strong>Тема:</strong> ${escapeHtml(lesson.topic || '—')}</p>
${timeHtml}
<div class="slots"> <div class="slots">
Свободных мест: ${lesson.max_slots - lesson.current_slots} из ${lesson.max_slots} Свободных мест: ${lesson.max_slots - lesson.current_slots}
</div> </div>
</div> </div>
`).join(''); `;
}).join('');
if (counterSpan) counterSpan.textContent = `${lessons.length}`; if (counterSpan) counterSpan.textContent = `${lessons.length}`;
@@ -147,7 +144,6 @@ function renderLessons(lessons) {
}); });
} }
// Модальное окно записи (без изменений)
function setupModal() { function setupModal() {
const modal = document.getElementById('modal'); const modal = document.getElementById('modal');
const closeSpan = modal.querySelector('.close'); const closeSpan = modal.querySelector('.close');
@@ -195,11 +191,18 @@ function openModal(lessonId) {
const lesson = allLessons.find(l => l.id == lessonId); const lesson = allLessons.find(l => l.id == lessonId);
if (!lesson) return; if (!lesson) return;
let timeInfo = '';
if (lesson.topic === 'Консультация' && lesson.date && lesson.time) {
timeInfo = `${lesson.date} ${lesson.time}`;
} else {
timeInfo = 'Согласно расписания';
}
document.getElementById('lessonId').value = lessonId; document.getElementById('lessonId').value = lessonId;
document.getElementById('modalLessonInfo').innerHTML = ` document.getElementById('modalLessonInfo').innerHTML = `
<strong>${escapeHtml(lesson.class_name)}</strong><br> <strong>${escapeHtml(lesson.class_name)}</strong><br>
${escapeHtml(lesson.subject)}${escapeHtml(lesson.teacher)}<br> ${escapeHtml(lesson.subject)}${escapeHtml(lesson.teacher)}<br>
<small>${lesson.date} ${lesson.time}</small> <small>${timeInfo}</small>
`; `;
document.getElementById('modalMessage').innerHTML = ''; document.getElementById('modalMessage').innerHTML = '';
document.getElementById('registrationForm').reset(); document.getElementById('registrationForm').reset();
@@ -216,13 +219,11 @@ function escapeHtml(str) {
}); });
} }
// Инициализация
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', async () => {
await loadFilterOptions(); await loadFilterOptions();
await loadLessons(); await loadLessons();
setupModal(); setupModal();
// Обработчики изменений фильтров
const classSelect = document.getElementById('filterClass'); const classSelect = document.getElementById('filterClass');
const teacherSelect = document.getElementById('filterTeacher'); const teacherSelect = document.getElementById('filterTeacher');
const topicSelect = document.getElementById('filterTopic'); const topicSelect = document.getElementById('filterTopic');

View File

@@ -174,7 +174,7 @@ form input, form textarea {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.3rem; gap: 0.3rem;
min-width: 150px; min-width: 100px;
max-width: 450px; max-width: 450px;
} }