diff --git a/frontend/admin/css/department.css b/frontend/admin/css/department.css index 691860e..d39a250 100644 --- a/frontend/admin/css/department.css +++ b/frontend/admin/css/department.css @@ -1,235 +1,236 @@ -.wrap{ - max-width: 900px; - margin: 0 auto; - background: var(--bg-card); - border: 1px solid var(--bg-card-border); - border-radius: 12px; - overflow: hidden; - box-shadow: 0 6px 20px rgba(0,0,0,.06); -} - -.header{ - padding: 14px 16px; - border-bottom: 1px solid var(--bg-card-border); - font-weight: 700; - color: var(--text-primary); -} - -details.table-item{ - border-top: 1px solid var(--bg-card-border); -} -details.table-item:first-of-type{ border-top:none; } - -summary{ - list-style: none; - cursor: pointer; - user-select: none; - padding: 12px 16px; - display: flex; - align-items: center; - gap: 10px; -} -summary::-webkit-details-marker{ display:none; } - -.chev{ - width: 28px; - height: 28px; - border: 1px solid var(--bg-card-border); - border-radius: 10px; - display: grid; - place-items: center; - flex: 0 0 auto; - - color: var(--text-secondary); - background: var(--bg-input); - - transition: transform .18s ease, color .18s ease, border-color .18s ease, background .18s ease; -} - -.chev-icon{ - width: 16px; - height: 16px; - display: block; -} - -summary:hover .chev{ - background: var(--bg-hover); - border-color: color-mix(in srgb, var(--accent) 22%, var(--bg-card-border)); - color: var(--text-primary); -} - -details[open] .chev{ - transform: rotate(180deg); - color: var(--accent); - border-color: color-mix(in srgb, var(--accent) 35%, var(--bg-card-border)); - background: color-mix(in srgb, var(--accent) 10%, var(--bg-input)); -} - -.meta{ color: var(--text-secondary); font-size: 12px; } - -.content{ padding: 0 16px 16px 16px; } - -.wrap table{ - width: 100%; - border-collapse: collapse; - border: 1px solid var(--bg-card-border); - border-radius: 10px; - overflow: hidden; - background: var(--bg-card); -} - -.wrap thead th{ - text-align: left; - font-size: 13px; - color: var(--text-secondary); - background: var(--bg-input); - border-bottom: 1px solid var(--bg-card-border); - padding: 10px 12px; -} - -.wrap tbody td{ - padding: 10px 12px; - border-bottom: 1px solid var(--bg-card-border); - font-size: 14px; - color: var(--text-primary); -} - -.wrap tbody tr:hover{ background: var(--bg-hover); } - -.title-multiline{ - display: flex; - flex-direction: column; - gap: 2px; - line-height: 1.2; -} - -.title-multiline .title-main{ - font-weight: 700; - color: var(--text-primary); -} - -.title-multiline .title-sub{ - font-weight: 500; - font-size: 12px; - color: var(--text-secondary); -} - -.title-multiline b{ - font-weight: 700; - color: var(--text-primary); -} - -/* summary = 3 колонки: [chev] [title] [meta] */ -details.table-item > summary{ - display: grid; - grid-template-columns: 28px 1fr auto; - gap: 12px; - align-items: start; /* важно: всё прижимаем к верху */ - padding: 12px 16px; -} - -/* чтобы текст нормально переносился и не растягивал мету */ -details.table-item > summary .title{ - min-width: 0; /* важно для grid, иначе может распирать */ -} - -/* "2 записи" всегда справа и сверху, аккуратно */ -details.table-item > summary .meta{ - justify-self: end; - align-self: start; - white-space: nowrap; - padding-top: 4px; /* чуть опустить относительно первой строки */ - font-size: 12px; - color: var(--text-secondary); -} - -/* стрелка тоже сверху */ -details.table-item > summary .chev{ - align-self: start; - margin-top: 2px; -} - -.records-search{ - width: min(360px, 60vw); - padding: 0.45rem 0.7rem; - background: var(--bg-input); - border: 1px solid var(--bg-card-border); - border-radius: var(--radius-sm); - color: var(--text-primary); - font-size: 0.9rem; - outline: none; - transition: border-color .2s ease, box-shadow .2s ease, background .2s ease; -} - -.records-search::placeholder{ color: var(--text-placeholder); } - -.records-search:focus{ - background: var(--bg-input-focus); - border-color: var(--accent); - box-shadow: 0 0 0 3px var(--accent-glow); -} -/* Таблица внутри раскрывающегося блока */ -details.table-item .content table{ - width: 100%; - border-collapse: separate; /* нужно для красивых линий */ - border-spacing: 0; - border: 1px solid var(--bg-card-border); - border-radius: 12px; - overflow: hidden; - background: var(--bg-card); -} - -/* Шапка */ -details.table-item .content thead th{ - position: sticky; /* опционально: шапка прилипает при скролле */ - top: 0; - z-index: 1; - - background: var(--bg-input); - color: var(--text-secondary); - border-bottom: 1px solid var(--bg-card-border); -} - -/* Ячейки: одинаковые отступы */ -details.table-item .content th, -details.table-item .content td{ - padding: 0.75rem 0.85rem; - vertical-align: top; -} - -/* Вертикальные разделители между колонками */ -details.table-item .content th:not(:last-child), -details.table-item .content td:not(:last-child){ - border-right: 1px solid var(--bg-card-border); -} - -/* Горизонтальные разделители между строками */ -details.table-item .content tbody td{ - border-bottom: 1px solid var(--bg-card-border); - color: var(--text-primary); -} - -/* У последней строки нет нижней линии */ -details.table-item .content tbody tr:last-child td{ - border-bottom: none; -} - -/* "Зебра" для читабельности */ -details.table-item .content tbody tr:nth-child(even){ - background: color-mix(in srgb, var(--bg-card) 70%, var(--bg-hover)); -} - -/* Ховер по строке */ -details.table-item .content tbody tr:hover{ - background: var(--bg-hover); -} - -/* (Опционально) Чтобы длинный текст не ломал ширину */ -details.table-item .content td{ - word-break: break-word; -} - -/* (Опционально) если таблица широкая — пусть скроллится горизонтально */ -details.table-item .content{ - overflow-x: auto; -} \ No newline at end of file +.wrap{ + max-width: 900px; + margin: 0 auto; + background: var(--bg-card); + border: 1px solid var(--bg-card-border); + border-radius: 12px; + overflow: hidden; + box-shadow: 0 6px 20px rgba(0,0,0,.06); +} + +.header{ + padding: 14px 16px; + border-bottom: 1px solid var(--bg-card-border); + font-weight: 700; + color: var(--text-primary); +} + +details.table-item{ + border-top: 1px solid var(--bg-card-border); +} +details.table-item:first-of-type{ border-top:none; } + +summary{ + list-style: none; + cursor: pointer; + user-select: none; + padding: 12px 16px; + display: flex; + align-items: center; + gap: 10px; +} +summary::-webkit-details-marker{ display:none; } + +.chev{ + width: 28px; + height: 28px; + border: 1px solid var(--bg-card-border); + border-radius: 10px; + display: grid; + place-items: center; + flex: 0 0 auto; + + color: var(--text-secondary); + background: var(--bg-input); + + transition: transform .18s ease, color .18s ease, border-color .18s ease, background .18s ease; +} + +.chev-icon{ + width: 16px; + height: 16px; + display: block; +} + +summary:hover .chev{ + background: var(--bg-hover); + border-color: color-mix(in srgb, var(--accent) 22%, var(--bg-card-border)); + color: var(--text-primary); +} + +details[open] .chev{ + transform: rotate(180deg); + color: var(--accent); + border-color: color-mix(in srgb, var(--accent) 35%, var(--bg-card-border)); + background: color-mix(in srgb, var(--accent) 10%, var(--bg-input)); +} + +.meta{ color: var(--text-secondary); font-size: 12px; } + +.content{ padding: 0 16px 16px 16px; } + +.wrap table{ + width: 100%; + border-collapse: collapse; + border: 1px solid var(--bg-card-border); + border-radius: 10px; + overflow: hidden; + background: var(--bg-card); +} + +.wrap thead th{ + text-align: left; + font-size: 13px; + color: var(--text-secondary); + background: var(--bg-input); + border-bottom: 1px solid var(--bg-card-border); + padding: 10px 12px; +} + +.wrap tbody td{ + padding: 10px 12px; + border-bottom: 1px solid var(--bg-card-border); + font-size: 14px; + color: var(--text-primary); +} + +.wrap tbody tr:hover{ background: var(--bg-hover); } + +.title-multiline{ + display: flex; + flex-direction: column; + gap: 2px; + line-height: 1.2; +} + +.title-multiline .title-main{ + font-weight: 700; + color: var(--text-primary); +} + +.title-multiline .title-sub{ + font-weight: 500; + font-size: 12px; + color: var(--text-secondary); +} + +.title-multiline b{ + font-weight: 700; + color: var(--text-primary); +} + +/* summary = 3 колонки: [chev] [title] [meta] */ +details.table-item > summary{ + display: grid; + grid-template-columns: 28px 1fr auto; + gap: 12px; + align-items: start; /* важно: всё прижимаем к верху */ + padding: 12px 16px; +} + +/* чтобы текст нормально переносился и не растягивал мету */ +details.table-item > summary .title{ + min-width: 0; /* важно для grid, иначе может распирать */ +} + +/* "2 записи" всегда справа и сверху, аккуратно */ +details.table-item > summary .meta{ + justify-self: end; + align-self: start; + white-space: nowrap; + padding-top: 4px; /* чуть опустить относительно первой строки */ + font-size: 12px; + color: var(--text-secondary); +} + +/* стрелка тоже сверху */ +details.table-item > summary .chev{ + align-self: start; + margin-top: 2px; +} + +.records-search{ + width: min(360px, 60vw); + padding: 0.45rem 0.7rem; + background: var(--bg-input); + border: 1px solid var(--bg-card-border); + border-radius: var(--radius-sm); + color: var(--text-primary); + font-size: 0.9rem; + outline: none; + transition: border-color .2s ease, box-shadow .2s ease, background .2s ease; +} + +.records-search::placeholder{ color: var(--text-placeholder); } + +.records-search:focus{ + background: var(--bg-input-focus); + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-glow); +} +/* Таблица внутри раскрывающегося блока */ +details.table-item .content table{ + width: 100%; + border-collapse: separate; /* нужно для красивых линий */ + border-spacing: 0; + border: 1px solid var(--bg-card-border); + border-radius: 12px; + overflow: hidden; + background: var(--bg-card); +} + +/* Шапка */ +details.table-item .content thead th{ + position: sticky; /* опционально: шапка прилипает при скролле */ + top: 0; + z-index: 1; + + background: var(--bg-input); + color: var(--text-secondary); + border-bottom: 1px solid var(--bg-card-border); +} + +/* Ячейки: одинаковые отступы */ +details.table-item .content th, +details.table-item .content td{ + padding: 0.75rem 0.85rem; + vertical-align: top; +} + +/* Вертикальные разделители между колонками */ +details.table-item .content th:not(:last-child), +details.table-item .content td:not(:last-child){ + border-right: 1px solid var(--bg-card-border); +} + +/* Горизонтальные разделители между строками */ +details.table-item .content tbody td{ + border-bottom: 1px solid var(--bg-card-border); + color: var(--text-primary); +} + +/* У последней строки нет нижней линии */ +details.table-item .content tbody tr:last-child td{ + border-bottom: none; +} + +/* "Зебра" для читабельности */ +details.table-item .content tbody tr:nth-child(even){ + background: color-mix(in srgb, var(--bg-card) 70%, var(--bg-hover)); +} + +/* Ховер по строке */ +details.table-item .content tbody tr:hover{ + background: var(--bg-hover); +} + +/* (Опционально) Чтобы длинный текст не ломал ширину */ +details.table-item .content td{ + word-break: break-word; +} + +/* (Опционально) если таблица широкая — пусть скроллится горизонтально */ +details.table-item .content{ + overflow-x: auto; +} + \ No newline at end of file diff --git a/frontend/admin/js/views/department.js b/frontend/admin/js/views/department.js index b7c3711..9b0a1c0 100644 --- a/frontend/admin/js/views/department.js +++ b/frontend/admin/js/views/department.js @@ -1,4 +1,121 @@ import { api } from '../api.js'; -import { escapeHtml } from '../utils.js'; +import { escapeHtml, showAlert, hideAlert } from '../utils.js'; -export async function initDepartment() { } \ No newline at end of file +export async function initDepartment() { + const form = document.getElementById('department-schedule-form'); + const departmentSelect = document.getElementById('filter-department'); + const container = document.getElementById('schedule-blocks-container'); + + let departments = []; + + // Загрузка кафедр + try { + departments = await api.get('/api/departments'); + departmentSelect.innerHTML = '' + + departments.map(d => ``).join(''); + } catch (e) { + departmentSelect.innerHTML = ''; + } + + form.addEventListener('submit', async (e) => { + e.preventDefault(); + hideAlert('schedule-form-alert'); + + const departmentId = departmentSelect.value; + const period = document.getElementById('filter-period').value; + const semesterType = document.querySelector('input[name="semesterType"]:checked')?.value; + + if (!departmentId || !period || !semesterType) { + showAlert('schedule-form-alert', 'Заполните все поля', 'error'); + return; + } + + const deptName = departmentSelect.options[departmentSelect.selectedIndex].text; + + try { + const params = new URLSearchParams({ + departmentId, + semesterType, + period + }); + + // Запрос на бэк + const data = await api.get(`/api/department/schedule?${params.toString()}`); + + const semesterName = semesterType === 'spring' ? 'весенний' : (semesterType === 'autumn' ? 'осенний' : semesterType); + const periodName = period.replace('-', '/'); // Display 2024-2025 as 2024/2025 + + renderScheduleBlock(deptName, semesterName, periodName, data); + + form.reset(); + + } catch (err) { + showAlert('schedule-form-alert', err.message || 'Ошибка загрузки данных', 'error'); + } + }); + + function renderScheduleBlock(deptName, semester, period, schedule) { + const details = document.createElement('details'); + details.className = 'table-item'; + details.open = true; // Сразу открываем новый блок + + details.innerHTML = ` + + +
+ Данные к составлению расписания + Кафедра: ${escapeHtml(deptName)} + Семестр: ${escapeHtml(semester)} + Уч. год: ${escapeHtml(period)} +
+
${schedule ? schedule.length : 0} записей
+
+ +
+ + + + + + + + + + + + + + + ${renderRows(schedule)} + +
СпециальностьКурс/семестрГруппаДисциплинаВид занятийЧасов в неделюАудиторияФамилия преподавателя
+
+ `; + + container.prepend(details); + } + + function renderRows(schedule) { + if (!schedule || schedule.length === 0) { + return 'Нет данных'; + } + + return schedule.map(r => ` + + ${escapeHtml(r.specialty || '-')} + ${escapeHtml(r.courseSemester || '-')} + ${escapeHtml(r.groupName || '-')} + ${escapeHtml(r.subjectName || '-')} + ${escapeHtml(r.lessonType || '-')} + ${escapeHtml(r.hours || '-')} + ${escapeHtml(r.classroom || '-')} + ${escapeHtml(r.teacherName || '-')} + + `).join(''); + } +} \ No newline at end of file diff --git a/frontend/admin/views/department.html b/frontend/admin/views/department.html index 74dc1bf..c86cb66 100644 --- a/frontend/admin/views/department.html +++ b/frontend/admin/views/department.html @@ -1,193 +1,46 @@ -
-

Кафедра

+
+

Запрос расписания кафедры

+
+
+
+ + +
-
- - - -
+
+ +
+ + +
+
+ +
+ + +
+ + +
+ +
- -
- - -
- - -
- Данные к составлению расписания - Кафедра: Информационная безопасность - Факультет: ФиПИ - Семестр: весенний - Уч. год: 2024/2025 -
-
3 записи
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
СпециальностьКурс и семестрГруппаДисциплинаВид занятийЧасов в неделюДеление на подгруппыФамилия преподавателя
09.02.072 курс, 4 семестрИС-21Базы данныхЛабораторная2ДаИванов
09.02.072 курс, 4 семестрИС-22Операционные системыПрактика1НетСмирнов
09.02.071 курс, 2 семестрИС-12АлгоритмыЛекция2НетКузнецов
-
-
- - -
- - -
orders
-
1 запись
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
СпециальностьКурс и семестрГруппаДисциплинаВид занятийЧасов в неделюДеление на подгруппыФамилия преподавателя
38.02.011 курс, 1 семестрЭК-11ЭкономикаЛекция1НетПетров
-
-
- - -
- - -
products
-
2 записи
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
СпециальностьКурс и семестрГруппаДисциплинаВид занятийЧасов в неделюДеление на подгруппыФамилия преподавателя
15.02.083 курс, 6 семестрМС-31МатериаловедениеПрактика3ДаСидоров
15.02.083 курс, 6 семестрМС-32Технология металловЛабораторная2ДаОрлов
-
-
- -
+
+
\ No newline at end of file