@@ -61,10 +82,10 @@ export async function initDepartment() {
Данные к составлению расписания
Кафедра: ${escapeHtml(deptName)}
- Семестр: ${escapeHtml(groupSemester)}
+ Семестр: ${escapeHtml(semester)}
Уч. год: ${escapeHtml(period)}
-
${schedule ? schedule.length : 0} записей
+
${Array.isArray(schedule) ? schedule.length : 0} записей
@@ -86,11 +107,15 @@ export async function initDepartment() {
`;
+
container.prepend(details);
+
+ // Сохраняем в sessionStorage
+ saveScheduleBlock(key, { deptName, semester, period, schedule, departmentId, semesterType, rawPeriod });
}
function renderRows(schedule) {
- if (!schedule || schedule.length === 0) {
+ if (!Array.isArray(schedule) || schedule.length === 0) {
return '
| Нет данных |
';
}
return schedule.map(r => `
@@ -98,9 +123,9 @@ export async function initDepartment() {
${escapeHtml(r.specialityCode || '-')} |
${(() => {
const course = r.groupCourse || '-';
- const groupSemester = r.groupSemester || '-';
- if (course === '-' && groupSemester === '-') return '-';
- return `${course} | ${groupSemester}`;
+ const semester = r.semester || '-';
+ if (course === '-' && semester === '-') return '-';
+ return `${course} | ${semester}`;
})()} |
${escapeHtml(r.groupName || '-')} |
${escapeHtml(r.subjectName || '-')} |
@@ -117,6 +142,32 @@ export async function initDepartment() {
`).join('');
}
+ // ===== Persistence: sessionStorage (fix #4) =====
+ function saveScheduleBlock(key, blockData) {
+ try {
+ const stored = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || '{}');
+ stored[key] = blockData;
+ sessionStorage.setItem(STORAGE_KEY, JSON.stringify(stored));
+ } catch (e) {
+ console.warn('Ошибка сохранения в sessionStorage:', e);
+ }
+ }
+
+ function restoreScheduleBlocks() {
+ try {
+ const stored = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || '{}');
+ const keys = Object.keys(stored);
+ if (keys.length === 0) return;
+
+ keys.forEach(key => {
+ const b = stored[key];
+ renderScheduleBlock(b.deptName, b.semester, b.period, b.schedule, b.departmentId, b.semesterType, b.rawPeriod);
+ });
+ } catch (e) {
+ console.warn('Ошибка восстановления из sessionStorage:', e);
+ }
+ }
+
// =========================================================
// ЛОГИКА ДЛЯ ФУНКЦИОНАЛА "СОЗДАТЬ ЗАПИСЬ (К/Ф)"
// Два модальных окна поверх всего контента в одном оверлее
@@ -158,12 +209,28 @@ export async function initDepartment() {
csSubjectSelect.innerHTML = '
' +
csSubjects.map(s => `
`).join('');
+ // Загрузка преподавателей: сначала по кафедре, при ошибке — все преподаватели
+ csTeachers = [];
if (localDepartmentId) {
- csTeachers = await api.get(`/api/users/teachers/${localDepartmentId}`);
+ try {
+ csTeachers = await api.get(`/api/users/teachers/${localDepartmentId}`);
+ } catch (e) {
+ console.warn('Не удалось загрузить преподавателей для кафедры, загружаем всех:', e);
+ }
+ }
+ // Фолбэк: загружаем всех преподавателей
+ if (!Array.isArray(csTeachers) || csTeachers.length === 0) {
+ try {
+ csTeachers = await api.get('/api/users/teachers');
+ } catch (e2) {
+ console.error('Ошибка загрузки всех преподавателей:', e2);
+ }
+ }
+ if (Array.isArray(csTeachers) && csTeachers.length > 0) {
csTeacherSelect.innerHTML = '
' +
csTeachers.map(t => `
`).join('');
} else {
- csTeacherSelect.innerHTML = '
';
+ csTeacherSelect.innerHTML = '
';
}
} catch (e) {
console.error('Ошибка загрузки справочников:', e);
@@ -175,7 +242,7 @@ export async function initDepartment() {
// ===== Открытие / Закрытие оверлея =====
function openOverlay() {
csOverlay.classList.add('open');
- document.body.style.overflow = 'hidden'; // Предотвращаем скролл страницы
+ document.body.style.overflow = 'hidden';
}
function closeOverlay() {
@@ -204,7 +271,6 @@ export async function initDepartment() {
modalCreateScheduleClose.addEventListener('click', closeOverlay);
csOverlay.addEventListener('click', (e) => {
- // Закрыть по клику на затемнённый фон (но не по клику на содержимое модалок)
if (e.target === csOverlay || e.target.classList.contains('cs-overlay-scroll')) {
closeOverlay();
}
@@ -216,10 +282,10 @@ export async function initDepartment() {
}
});
- // ===== Рендер таблицы =====
+ // ===== Рендер таблицы подготовленных записей =====
function renderPreparedSchedules() {
if (preparedSchedules.length === 0) {
- preparedSchedulesTbody.innerHTML = '
| Нет записей |
';
+ preparedSchedulesTbody.innerHTML = '
| Нет записей |
';
return;
}
preparedSchedulesTbody.innerHTML = preparedSchedules.map((s, index) => {
@@ -230,14 +296,13 @@ export async function initDepartment() {
const lessonTypeName = LESSON_TYPE_LABELS[s.lessonTypeId] || 'Неизвестно';
const semLabel = SEMESTER_LABELS[s.semesterType] || s.semesterType;
const periodDisplay = s.period.replace('-', '/');
- const divText = s.division ? '✓' : '';
+ const divText = s.isDivision ? '✓' : '';
const hasError = !!s._errorMsg;
const rowStyle = hasError ? ' style="background: rgba(239, 68, 68, 0.08);"' : '';
let row = `
| ${escapeHtml(periodDisplay)} |
${escapeHtml(semLabel)} |
- ${s.groupSemester} |
${escapeHtml(String(groupName))} |
${escapeHtml(String(subjectName))} |
${escapeHtml(lessonTypeName)} |
@@ -248,7 +313,7 @@ export async function initDepartment() {
`;
if (hasError) {
row += `
- |
+ |
⚠ ${escapeHtml(s._errorMsg)}
|
`;
@@ -268,8 +333,6 @@ export async function initDepartment() {
});
// ===== Очистка полей формы (частичная) =====
- // НЕ очищаем select'ы — они остаются заполненными для удобства.
- // Пользователь сам изменит нужные поля для следующей записи.
function clearFormFields() {
document.getElementById('cs-hours').value = '';
document.getElementById('cs-division').checked = false;
@@ -283,12 +346,11 @@ export async function initDepartment() {
const depId = csDepartmentIdInput.value;
const period = document.getElementById('cs-period').value;
const semesterType = document.querySelector('input[name="csSemesterType"]:checked')?.value;
- const groupSemester = document.getElementById('cs-semester').value;
const groupId = csGroupSelect.value;
const subjectId = csSubjectSelect.value;
const lessonTypeId = document.getElementById('cs-lesson-type').value;
const hours = document.getElementById('cs-hours').value;
- const division = document.getElementById('cs-division').checked;
+ const isDivision = document.getElementById('cs-division').checked;
const teacherId = csTeacherSelect.value;
if (!period || !semesterType || !groupId || !subjectId || !lessonTypeId || !hours || !teacherId) {
@@ -298,27 +360,25 @@ export async function initDepartment() {
const newRecord = {
departmentId: Number(depId),
- groupSemester: Number(groupSemester),
groupId: Number(groupId),
subjectsId: Number(subjectId),
lessonTypeId: Number(lessonTypeId),
numberOfHours: Number(hours),
- division: division,
+ isDivision: isDivision,
teacherId: Number(teacherId),
semesterType: semesterType,
period: period
};
- // Проверка на дубликат в уже добавленных записях
+ // Проверка на дубликат
const isDuplicate = preparedSchedules.some(s =>
s.period === newRecord.period &&
s.semesterType === newRecord.semesterType &&
- s.groupSemester === newRecord.groupSemester &&
s.groupId === newRecord.groupId &&
s.subjectsId === newRecord.subjectsId &&
s.lessonTypeId === newRecord.lessonTypeId &&
s.numberOfHours === newRecord.numberOfHours &&
- s.division === newRecord.division &&
+ s.isDivision === newRecord.isDivision &&
s.teacherId === newRecord.teacherId
);
@@ -332,7 +392,7 @@ export async function initDepartment() {
clearFormFields();
showAlert('create-schedule-alert', 'Запись добавлена ✓', 'success');
- setTimeout(() => hideAlert('create-schedule-alert'), 2000);
+ setTimeout(() => hideAlert('create-schedule-alert'), 4000); // fix #1: 4 секунды
renderPreparedSchedules();
updateTableVisibility();
@@ -360,7 +420,6 @@ export async function initDepartment() {
} catch (err) {
console.error('Ошибка сохранения записи:', err);
errors++;
- // Помечаем запись как дубликат, если бэк вернул соответствующую ошибку
const isDuplicate = err.status === 409 ||
(err.message && err.message.toLowerCase().includes('уже существует'));
failedRecords.push({
@@ -382,7 +441,6 @@ export async function initDepartment() {
updateTableVisibility();
setTimeout(closeOverlay, 2000);
} else {
- // Оставляем неудачные записи для повторной попытки / удаления
preparedSchedules = failedRecords;
renderPreparedSchedules();
if (saved > 0) {
diff --git a/frontend/admin/js/views/groups.js b/frontend/admin/js/views/groups.js
index e702b88..ec57a24 100755
--- a/frontend/admin/js/views/groups.js
+++ b/frontend/admin/js/views/groups.js
@@ -17,7 +17,7 @@ export async function initGroups() {
populateEfSelects(educationForms);
await loadGroups();
} catch (e) {
- groupsTbody.innerHTML = '
| Ошибка загрузки данных |
';
+ groupsTbody.innerHTML = '
| Ошибка загрузки данных |
';
}
}
@@ -26,7 +26,7 @@ export async function initGroups() {
allGroups = await api.get('/api/groups');
applyGroupFilter();
} catch (e) {
- groupsTbody.innerHTML = '
| Ошибка загрузки |
';
+ groupsTbody.innerHTML = '
| Ошибка загрузки |
';
}
}
@@ -61,7 +61,7 @@ export async function initGroups() {
function renderGroups(groups) {
if (!groups || !groups.length) {
- groupsTbody.innerHTML = '
| Нет групп |
';
+ groupsTbody.innerHTML = '
| Нет групп |
';
return;
}
groupsTbody.innerHTML = groups.map(g => `
@@ -72,6 +72,7 @@ export async function initGroups() {
${escapeHtml(g.educationFormName)} |
${g.departmentId || '-'} |
${g.course || '-'} |
+
${escapeHtml(g.specialityCode || '-')} |
|
`).join('');
}
@@ -83,13 +84,15 @@ export async function initGroups() {
const groupSize = document.getElementById('new-group-size').value;
const educationFormId = newGroupEfSelect.value;
const departmentId = document.getElementById('new-group-department').value;
- const yearStartStudy = document.getElementById('new-group-yearStartStudy').value;
+ const course = document.getElementById('new-group-course').value;
+ const specialityCode = document.getElementById('new-group-speciality-code').value.trim();
if (!name) { showAlert('create-group-alert', 'Введите название группы', 'error'); return; }
if (!groupSize) { showAlert('create-group-alert', 'Введите размер группы', 'error'); return; }
if (!educationFormId) { showAlert('create-group-alert', 'Выберите форму обучения', 'error'); return; }
if (!departmentId) { showAlert('create-group-alert', 'Введите ID кафедры', 'error'); return; }
- if (!yearStartStudy) { showAlert('create-group-alert', 'Введите курс', 'error'); return; }
+ if (!course) { showAlert('create-group-alert', 'Введите курс', 'error'); return; }
+ if (!specialityCode) { showAlert('create-group-alert', 'Введите код специальности', 'error'); return; }
try {
const data = await api.post('/api/groups', {
@@ -97,7 +100,8 @@ export async function initGroups() {
groupSize: Number(groupSize),
educationFormId: Number(educationFormId),
departmentId: Number(departmentId),
- yearStartStudy: Number(yearStartStudy)
+ course: Number(course),
+ specialityCode: specialityCode
});
showAlert('create-group-alert', `Группа "${escapeHtml(data.name || name)}" создана`, 'success');
createGroupForm.reset();
diff --git a/frontend/admin/views/department.html b/frontend/admin/views/department.html
index d0b435e..11bf25b 100644
--- a/frontend/admin/views/department.html
+++ b/frontend/admin/views/department.html
@@ -30,11 +30,11 @@
@@ -63,9 +63,9 @@