Кривая модалка занятий по teacherId добавлена, требует доработки, также код users.js требует унификации+оптимизации(новая модалка вкинута в конец)

This commit is contained in:
2026-03-13 03:12:48 +03:00
parent 24caa148e1
commit 8df736ae36
3 changed files with 370 additions and 3 deletions

View File

@@ -861,3 +861,198 @@ tbody tr:hover {
border-color: var(--success, #10b981); border-color: var(--success, #10b981);
color: white; color: white;
} }
/* ===== View Lessons Modal ===== */
.view-lessons-modal {
width: 50% !important; /* Половина экрана */
max-width: 50% !important;
background: var(--bg-primary);
border: 1px solid var(--bg-card-border);
border-radius: var(--radius-md);
padding: 2rem;
position: relative;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
margin: 0;
transform: none;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding-right: 2rem;
}
.modal-header h2 {
margin: 0;
font-size: 1.3rem;
color: var(--text-primary);
}
/* Контейнер для занятий */
.lessons-container {
max-height: 70vh;
overflow-y: auto;
padding-right: 0.5rem;
}
/* Карточка занятия */
.lesson-card {
background: var(--bg-card);
border: 1px solid var(--bg-card-border);
border-radius: var(--radius-sm);
padding: 1.2rem;
margin-bottom: 1rem;
transition: all 0.2s ease;
}
.lesson-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
border-color: var(--accent);
}
.lesson-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.8rem;
padding-bottom: 0.5rem;
border-bottom: 1px dashed var(--bg-card-border);
}
.lesson-group {
font-weight: 700;
color: var(--accent);
font-size: 1rem;
background: rgba(99, 102, 241, 0.1);
padding: 0.3rem 0.8rem;
border-radius: 20px;
}
.lesson-time {
color: var(--text-secondary);
font-size: 0.9rem;
display: flex;
align-items: center;
gap: 0.3rem;
}
.lesson-time::before {
content: "🕒";
font-size: 0.9rem;
opacity: 0.7;
}
.lesson-card-body {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.lesson-subject {
font-weight: 600;
color: var(--text-primary);
font-size: 1.1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.lesson-subject::before {
content: "📚";
font-size: 1rem;
opacity: 0.7;
}
.lesson-details {
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
margin-top: 0.5rem;
}
.lesson-detail-item {
background: var(--bg-input);
padding: 0.3rem 0.8rem;
border-radius: 15px;
font-size: 0.85rem;
color: var(--text-secondary);
border: 1px solid var(--bg-card-border);
}
/* День недели как разделитель */
.lesson-day-divider {
margin: 1.5rem 0 1rem 0;
font-weight: 700;
color: var(--accent);
font-size: 1.1rem;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 2px solid var(--accent-glow);
padding-bottom: 0.3rem;
}
.lesson-day-divider:first-of-type {
margin-top: 0;
}
/* Загрузка и пустые состояния */
.loading-lessons, .no-lessons {
text-align: center;
color: var(--text-secondary);
padding: 3rem;
font-size: 1rem;
background: var(--bg-card);
border-radius: var(--radius-sm);
}
/* Светлая тема */
[data-theme="light"] .lesson-card {
background: white;
border-color: rgba(0, 0, 0, 0.1);
}
[data-theme="light"] .lesson-group {
background: rgba(99, 102, 241, 0.05);
}
/* Адаптивность */
@media (max-width: 1200px) {
.view-lessons-modal {
width: 70% !important;
max-width: 70% !important;
}
}
@media (max-width: 768px) {
.view-lessons-modal {
width: 90% !important;
max-width: 90% !important;
}
.lesson-card-header {
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
}
.btn-view-lessons {
padding: 0.35rem 0.7rem;
background: rgba(99, 102, 241, 0.1);
border: 1px solid rgba(99, 102, 241, 0.2);
border-radius: var(--radius-sm);
color: var(--accent);
font-family: inherit;
font-size: 0.8rem;
cursor: pointer;
transition: all var(--transition);
white-space: nowrap;
}
.btn-view-lessons:hover {
background: rgba(99, 102, 241, 0.2);
transform: translateY(-1px);
}

View File

@@ -172,8 +172,13 @@ export async function initUsers() {
<td>${u.id}</td> <td>${u.id}</td>
<td>${escapeHtml(u.username)}</td> <td>${escapeHtml(u.username)}</td>
<td><span class="badge ${ROLE_BADGE[u.role] || ''}">${ROLE_LABELS[u.role] || escapeHtml(u.role)}</span></td> <td><span class="badge ${ROLE_BADGE[u.role] || ''}">${ROLE_LABELS[u.role] || escapeHtml(u.role)}</span></td>
<td><button class="btn-delete" data-id="${u.id}">Удалить</button></td> <td>
<td><button class="btn-add-lesson" data-id="${u.id}">Добавить занятие</button></td> <button class="btn-delete" data-id="${u.id}">Удалить</button>
</td>
<td>
<button class="btn-add-lesson" data-id="${u.id}">Добавить занятие</button>
<button class="btn-view-lessons" data-id="${u.id}" data-name="${escapeHtml(u.username)}" style="margin-left: 0.5rem;">👁️ Занятия</button>
</td>
</tr>`).join(''); </tr>`).join('');
} }
@@ -351,4 +356,158 @@ export async function initUsers() {
// Загружаем все данные при инициализации // Загружаем все данные при инициализации
await Promise.all([loadUsers(), loadGroups(), loadSubjects(), loadClassrooms()]); await Promise.all([loadUsers(), loadGroups(), loadSubjects(), loadClassrooms()]);
// Элементы второго модального окна
const modalViewLessons = document.getElementById('modal-view-lessons');
const modalViewLessonsClose = document.getElementById('modal-view-lessons-close');
const lessonsContainer = document.getElementById('lessons-container');
const modalTeacherName = document.getElementById('modal-teacher-name');
// Функция для загрузки и отображения занятий преподавателя
async function loadTeacherLessons(teacherId, teacherName) {
try {
lessonsContainer.innerHTML = '<div class="loading-lessons">Загрузка занятий...</div>';
// Устанавливаем имя преподавателя в заголовок
if (teacherName) {
modalTeacherName.textContent = `Занятия преподавателя: ${teacherName}`;
} else {
modalTeacherName.textContent = 'Занятия преподавателя';
}
// Загружаем занятия
const lessons = await api.get(`/api/users/lessons/${teacherId}`);
if (!lessons || lessons.length === 0) {
lessonsContainer.innerHTML = '<div class="no-lessons">У преподавателя пока нет занятий</div>';
return;
}
// Группируем занятия по дням недели
const daysOrder = ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'];
const lessonsByDay = {};
lessons.forEach(lesson => {
if (!lessonsByDay[lesson.day]) {
lessonsByDay[lesson.day] = [];
}
lessonsByDay[lesson.day].push(lesson);
});
// Сортируем занятия в каждом дне по времени
Object.keys(lessonsByDay).forEach(day => {
lessonsByDay[day].sort((a, b) => {
// Простая сортировка по времени (можно улучшить)
return a.time.localeCompare(b.time);
});
});
// Формируем HTML
let html = '';
daysOrder.forEach(day => {
if (lessonsByDay[day]) {
html += `<div class="lesson-day-divider">${day}</div>`;
lessonsByDay[day].forEach(lesson => {
html += `
<div class="lesson-card">
<div class="lesson-card-header">
<span class="lesson-group">${escapeHtml(lesson.groupName)}</span>
<span class="lesson-time">${escapeHtml(lesson.time)}</span>
</div>
<div class="lesson-card-body">
<div class="lesson-subject">${escapeHtml(lesson.subjectName)}</div>
<div class="lesson-details">
<span class="lesson-detail-item">${escapeHtml(lesson.typeLesson)}</span>
<span class="lesson-detail-item">${escapeHtml(lesson.lessonFormat)}</span>
<span class="lesson-detail-item">${escapeHtml(lesson.week)}</span>
<span class="lesson-detail-item">${escapeHtml(lesson.classroomName)}</span>
</div>
</div>
</div>
`;
});
}
});
lessonsContainer.innerHTML = html;
} catch (e) {
lessonsContainer.innerHTML = `<div class="no-lessons">Ошибка загрузки: ${escapeHtml(e.message)}</div>`;
console.error('Ошибка загрузки занятий:', e);
}
}
// Функция открытия модального окна
function openViewLessonsModal(teacherId, teacherName) {
loadTeacherLessons(teacherId, teacherName);
modalViewLessons.classList.add('open');
// Блокируем скролл страницы
document.body.style.overflow = 'hidden';
}
// Функция закрытия модального окна
function closeViewLessonsModal() {
modalViewLessons.classList.remove('open');
document.body.style.overflow = '';
}
// Обработчик кликов по таблице (добавляем в существующий)
// Найдите в коде usersTbody.addEventListener('click', ...) и добавьте в него:
// ДОЛЖНО ПОЛУЧИТЬСЯ ТАК:
usersTbody.addEventListener('click', async (e) => {
const deleteBtn = e.target.closest('.btn-delete');
if (deleteBtn) {
if (!confirm('Удалить пользователя?')) return;
try {
await api.delete('/api/users/' + deleteBtn.dataset.id);
loadUsers();
} catch (e) {
alert(e.message || 'Ошибка удаления');
}
return;
}
const addLessonBtn = e.target.closest('.btn-add-lesson');
if (addLessonBtn) {
e.preventDefault();
if (modalAddLesson) {
openAddLessonModal(addLessonBtn.dataset.id);
}
return;
}
// НОВЫЙ ОБРАБОТЧИК для кнопки просмотра занятий
const viewLessonsBtn = e.target.closest('.btn-view-lessons');
if (viewLessonsBtn) {
e.preventDefault();
const teacherId = viewLessonsBtn.dataset.id;
const teacherName = viewLessonsBtn.dataset.name;
openViewLessonsModal(teacherId, teacherName);
}
});
// Обработчики закрытия модального окна
if (modalViewLessonsClose) {
modalViewLessonsClose.addEventListener('click', closeViewLessonsModal);
}
// Закрытие по клику на overlay
if (modalViewLessons) {
modalViewLessons.addEventListener('click', (e) => {
if (e.target === modalViewLessons) {
closeViewLessonsModal();
}
});
}
// Закрытие по Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modalViewLessons?.classList.contains('open')) {
closeViewLessonsModal();
}
});
} }

View File

@@ -154,5 +154,18 @@
<div class="form-alert" id="add-lesson-alert" role="alert" style="margin-top: 1rem;"></div> <div class="form-alert" id="add-lesson-alert" role="alert" style="margin-top: 1rem;"></div>
</form> </form>
</div> </div>
<!-- View Teacher Lessons Modal -->
<div class="modal-overlay" id="modal-view-lessons">
<div class="modal-content view-lessons-modal">
<div class="modal-header">
<h2 id="modal-teacher-name">Занятия преподавателя</h2>
<button class="modal-close" id="modal-view-lessons-close">&times;</button>
</div>
<div class="lessons-container" id="lessons-container">
<!-- Фильтры по дням (добавим позже) -->
<div class="loading-lessons">Загрузка занятий...</div>
</div>
</div>
</div>
</div> </div>