Изменил страницу "Кафедра", добавлены изменения из задачи #54 в Vikunja
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
import { api } from '../api.js';
|
import { api } from '../api.js';
|
||||||
import { escapeHtml, showAlert, hideAlert } from '../utils.js';
|
import { escapeHtml, showAlert, hideAlert } from '../utils.js';
|
||||||
|
|
||||||
|
// Ключ для хранения данных в sessionStorage
|
||||||
|
const STORAGE_KEY = 'department_schedule_blocks';
|
||||||
|
|
||||||
export async function initDepartment() {
|
export async function initDepartment() {
|
||||||
const form = document.getElementById('department-schedule-form');
|
const form = document.getElementById('department-schedule-form');
|
||||||
const departmentSelect = document.getElementById('filter-department');
|
const departmentSelect = document.getElementById('filter-department');
|
||||||
@@ -17,6 +20,9 @@ export async function initDepartment() {
|
|||||||
departmentSelect.innerHTML = '<option value="">Ошибка загрузки</option>';
|
departmentSelect.innerHTML = '<option value="">Ошибка загрузки</option>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Восстанавливаем ранее загруженные таблицы из sessionStorage =====
|
||||||
|
restoreScheduleBlocks();
|
||||||
|
|
||||||
form.addEventListener('submit', async (e) => {
|
form.addEventListener('submit', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
hideAlert('schedule-form-alert');
|
hideAlert('schedule-form-alert');
|
||||||
@@ -39,17 +45,32 @@ export async function initDepartment() {
|
|||||||
const semesterName = semesterType === 'spring' ? 'весенний' : (semesterType === 'autumn' ? 'осенний' : semesterType);
|
const semesterName = semesterType === 'spring' ? 'весенний' : (semesterType === 'autumn' ? 'осенний' : semesterType);
|
||||||
const periodName = period.replace('-', '/');
|
const periodName = period.replace('-', '/');
|
||||||
|
|
||||||
renderScheduleBlock(deptName, semesterName, periodName, data);
|
renderScheduleBlock(deptName, semesterName, periodName, data, departmentId, semesterType, period);
|
||||||
form.reset();
|
|
||||||
|
// НЕ сбрасываем форму — фильтры остаются заполненными (fix #3)
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showAlert('schedule-form-alert', err.message || 'Ошибка загрузки данных', 'error');
|
showAlert('schedule-form-alert', err.message || 'Ошибка загрузки данных', 'error');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function renderScheduleBlock(deptName, groupSemester, period, schedule) {
|
// ===== Уникальный ключ для таблицы по параметрам =====
|
||||||
|
function blockKey(departmentId, semesterType, period) {
|
||||||
|
return `${departmentId}_${semesterType}_${period}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Рендер блока таблицы (с дедупликацией — fix #6) =====
|
||||||
|
function renderScheduleBlock(deptName, semester, period, schedule, departmentId, semesterType, rawPeriod) {
|
||||||
|
const key = blockKey(departmentId, semesterType, rawPeriod);
|
||||||
|
|
||||||
|
// Удаляем ранее загруженный блок с тем же ключом
|
||||||
|
const existing = container.querySelector(`[data-block-key="${key}"]`);
|
||||||
|
if (existing) existing.remove();
|
||||||
|
|
||||||
const details = document.createElement('details');
|
const details = document.createElement('details');
|
||||||
details.className = 'table-item';
|
details.className = 'table-item';
|
||||||
details.open = true;
|
details.open = true;
|
||||||
|
details.setAttribute('data-block-key', key);
|
||||||
details.innerHTML = `
|
details.innerHTML = `
|
||||||
<summary>
|
<summary>
|
||||||
<div class="chev" aria-hidden="true">
|
<div class="chev" aria-hidden="true">
|
||||||
@@ -61,10 +82,10 @@ export async function initDepartment() {
|
|||||||
<div class="title title-multiline">
|
<div class="title title-multiline">
|
||||||
<span class="title-main">Данные к составлению расписания</span>
|
<span class="title-main">Данные к составлению расписания</span>
|
||||||
<span class="title-sub">Кафедра: <b>${escapeHtml(deptName)}</b></span>
|
<span class="title-sub">Кафедра: <b>${escapeHtml(deptName)}</b></span>
|
||||||
<span class="title-sub">Семестр: <b>${escapeHtml(groupSemester)}</b></span>
|
<span class="title-sub">Семестр: <b>${escapeHtml(semester)}</b></span>
|
||||||
<span class="title-sub">Уч. год: <b>${escapeHtml(period)}</b></span>
|
<span class="title-sub">Уч. год: <b>${escapeHtml(period)}</b></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">${schedule ? schedule.length : 0} записей</div>
|
<div class="meta">${Array.isArray(schedule) ? schedule.length : 0} записей</div>
|
||||||
</summary>
|
</summary>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<table>
|
<table>
|
||||||
@@ -86,11 +107,15 @@ export async function initDepartment() {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
container.prepend(details);
|
container.prepend(details);
|
||||||
|
|
||||||
|
// Сохраняем в sessionStorage
|
||||||
|
saveScheduleBlock(key, { deptName, semester, period, schedule, departmentId, semesterType, rawPeriod });
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderRows(schedule) {
|
function renderRows(schedule) {
|
||||||
if (!schedule || schedule.length === 0) {
|
if (!Array.isArray(schedule) || schedule.length === 0) {
|
||||||
return '<tr><td colspan="8" class="loading-row">Нет данных</td></tr>';
|
return '<tr><td colspan="8" class="loading-row">Нет данных</td></tr>';
|
||||||
}
|
}
|
||||||
return schedule.map(r => `
|
return schedule.map(r => `
|
||||||
@@ -98,9 +123,9 @@ export async function initDepartment() {
|
|||||||
<td>${escapeHtml(r.specialityCode || '-')}</td>
|
<td>${escapeHtml(r.specialityCode || '-')}</td>
|
||||||
<td>${(() => {
|
<td>${(() => {
|
||||||
const course = r.groupCourse || '-';
|
const course = r.groupCourse || '-';
|
||||||
const groupSemester = r.groupSemester || '-';
|
const semester = r.semester || '-';
|
||||||
if (course === '-' && groupSemester === '-') return '-';
|
if (course === '-' && semester === '-') return '-';
|
||||||
return `${course} | ${groupSemester}`;
|
return `${course} | ${semester}`;
|
||||||
})()}</td>
|
})()}</td>
|
||||||
<td>${escapeHtml(r.groupName || '-')}</td>
|
<td>${escapeHtml(r.groupName || '-')}</td>
|
||||||
<td>${escapeHtml(r.subjectName || '-')}</td>
|
<td>${escapeHtml(r.subjectName || '-')}</td>
|
||||||
@@ -117,6 +142,32 @@ export async function initDepartment() {
|
|||||||
`).join('');
|
`).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 = '<option value="">Выберите дисциплину</option>' +
|
csSubjectSelect.innerHTML = '<option value="">Выберите дисциплину</option>' +
|
||||||
csSubjects.map(s => `<option value="${s.id}">${escapeHtml(s.name)}</option>`).join('');
|
csSubjects.map(s => `<option value="${s.id}">${escapeHtml(s.name)}</option>`).join('');
|
||||||
|
|
||||||
|
// Загрузка преподавателей: сначала по кафедре, при ошибке — все преподаватели
|
||||||
|
csTeachers = [];
|
||||||
if (localDepartmentId) {
|
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 = '<option value="">Выберите преподавателя</option>' +
|
csTeacherSelect.innerHTML = '<option value="">Выберите преподавателя</option>' +
|
||||||
csTeachers.map(t => `<option value="${t.id}">${escapeHtml(t.fullName || t.username)}</option>`).join('');
|
csTeachers.map(t => `<option value="${t.id}">${escapeHtml(t.fullName || t.username)}</option>`).join('');
|
||||||
} else {
|
} else {
|
||||||
csTeacherSelect.innerHTML = '<option value="">Ошибка: Не найден ID кафедры</option>';
|
csTeacherSelect.innerHTML = '<option value="">Нет преподавателей</option>';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Ошибка загрузки справочников:', e);
|
console.error('Ошибка загрузки справочников:', e);
|
||||||
@@ -175,7 +242,7 @@ export async function initDepartment() {
|
|||||||
// ===== Открытие / Закрытие оверлея =====
|
// ===== Открытие / Закрытие оверлея =====
|
||||||
function openOverlay() {
|
function openOverlay() {
|
||||||
csOverlay.classList.add('open');
|
csOverlay.classList.add('open');
|
||||||
document.body.style.overflow = 'hidden'; // Предотвращаем скролл страницы
|
document.body.style.overflow = 'hidden';
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeOverlay() {
|
function closeOverlay() {
|
||||||
@@ -204,7 +271,6 @@ export async function initDepartment() {
|
|||||||
modalCreateScheduleClose.addEventListener('click', closeOverlay);
|
modalCreateScheduleClose.addEventListener('click', closeOverlay);
|
||||||
|
|
||||||
csOverlay.addEventListener('click', (e) => {
|
csOverlay.addEventListener('click', (e) => {
|
||||||
// Закрыть по клику на затемнённый фон (но не по клику на содержимое модалок)
|
|
||||||
if (e.target === csOverlay || e.target.classList.contains('cs-overlay-scroll')) {
|
if (e.target === csOverlay || e.target.classList.contains('cs-overlay-scroll')) {
|
||||||
closeOverlay();
|
closeOverlay();
|
||||||
}
|
}
|
||||||
@@ -216,10 +282,10 @@ export async function initDepartment() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== Рендер таблицы =====
|
// ===== Рендер таблицы подготовленных записей =====
|
||||||
function renderPreparedSchedules() {
|
function renderPreparedSchedules() {
|
||||||
if (preparedSchedules.length === 0) {
|
if (preparedSchedules.length === 0) {
|
||||||
preparedSchedulesTbody.innerHTML = '<tr><td colspan="10" class="loading-row">Нет записей</td></tr>';
|
preparedSchedulesTbody.innerHTML = '<tr><td colspan="9" class="loading-row">Нет записей</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
preparedSchedulesTbody.innerHTML = preparedSchedules.map((s, index) => {
|
preparedSchedulesTbody.innerHTML = preparedSchedules.map((s, index) => {
|
||||||
@@ -230,14 +296,13 @@ export async function initDepartment() {
|
|||||||
const lessonTypeName = LESSON_TYPE_LABELS[s.lessonTypeId] || 'Неизвестно';
|
const lessonTypeName = LESSON_TYPE_LABELS[s.lessonTypeId] || 'Неизвестно';
|
||||||
const semLabel = SEMESTER_LABELS[s.semesterType] || s.semesterType;
|
const semLabel = SEMESTER_LABELS[s.semesterType] || s.semesterType;
|
||||||
const periodDisplay = s.period.replace('-', '/');
|
const periodDisplay = s.period.replace('-', '/');
|
||||||
const divText = s.division ? '✓' : '';
|
const divText = s.isDivision ? '✓' : '';
|
||||||
const hasError = !!s._errorMsg;
|
const hasError = !!s._errorMsg;
|
||||||
const rowStyle = hasError ? ' style="background: rgba(239, 68, 68, 0.08);"' : '';
|
const rowStyle = hasError ? ' style="background: rgba(239, 68, 68, 0.08);"' : '';
|
||||||
let row = `
|
let row = `
|
||||||
<tr${rowStyle}>
|
<tr${rowStyle}>
|
||||||
<td>${escapeHtml(periodDisplay)}</td>
|
<td>${escapeHtml(periodDisplay)}</td>
|
||||||
<td>${escapeHtml(semLabel)}</td>
|
<td>${escapeHtml(semLabel)}</td>
|
||||||
<td>${s.groupSemester}</td>
|
|
||||||
<td>${escapeHtml(String(groupName))}</td>
|
<td>${escapeHtml(String(groupName))}</td>
|
||||||
<td>${escapeHtml(String(subjectName))}</td>
|
<td>${escapeHtml(String(subjectName))}</td>
|
||||||
<td>${escapeHtml(lessonTypeName)}</td>
|
<td>${escapeHtml(lessonTypeName)}</td>
|
||||||
@@ -248,7 +313,7 @@ export async function initDepartment() {
|
|||||||
</tr>`;
|
</tr>`;
|
||||||
if (hasError) {
|
if (hasError) {
|
||||||
row += `<tr style="background: rgba(239, 68, 68, 0.05);">
|
row += `<tr style="background: rgba(239, 68, 68, 0.05);">
|
||||||
<td colspan="10" style="color: var(--error); font-size: 0.85rem; padding: 0.4rem 0.85rem;">
|
<td colspan="9" style="color: var(--error); font-size: 0.85rem; padding: 0.4rem 0.85rem;">
|
||||||
⚠ ${escapeHtml(s._errorMsg)}
|
⚠ ${escapeHtml(s._errorMsg)}
|
||||||
</td>
|
</td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
@@ -268,8 +333,6 @@ export async function initDepartment() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ===== Очистка полей формы (частичная) =====
|
// ===== Очистка полей формы (частичная) =====
|
||||||
// НЕ очищаем select'ы — они остаются заполненными для удобства.
|
|
||||||
// Пользователь сам изменит нужные поля для следующей записи.
|
|
||||||
function clearFormFields() {
|
function clearFormFields() {
|
||||||
document.getElementById('cs-hours').value = '';
|
document.getElementById('cs-hours').value = '';
|
||||||
document.getElementById('cs-division').checked = false;
|
document.getElementById('cs-division').checked = false;
|
||||||
@@ -283,12 +346,11 @@ export async function initDepartment() {
|
|||||||
const depId = csDepartmentIdInput.value;
|
const depId = csDepartmentIdInput.value;
|
||||||
const period = document.getElementById('cs-period').value;
|
const period = document.getElementById('cs-period').value;
|
||||||
const semesterType = document.querySelector('input[name="csSemesterType"]:checked')?.value;
|
const semesterType = document.querySelector('input[name="csSemesterType"]:checked')?.value;
|
||||||
const groupSemester = document.getElementById('cs-semester').value;
|
|
||||||
const groupId = csGroupSelect.value;
|
const groupId = csGroupSelect.value;
|
||||||
const subjectId = csSubjectSelect.value;
|
const subjectId = csSubjectSelect.value;
|
||||||
const lessonTypeId = document.getElementById('cs-lesson-type').value;
|
const lessonTypeId = document.getElementById('cs-lesson-type').value;
|
||||||
const hours = document.getElementById('cs-hours').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;
|
const teacherId = csTeacherSelect.value;
|
||||||
|
|
||||||
if (!period || !semesterType || !groupId || !subjectId || !lessonTypeId || !hours || !teacherId) {
|
if (!period || !semesterType || !groupId || !subjectId || !lessonTypeId || !hours || !teacherId) {
|
||||||
@@ -298,27 +360,25 @@ export async function initDepartment() {
|
|||||||
|
|
||||||
const newRecord = {
|
const newRecord = {
|
||||||
departmentId: Number(depId),
|
departmentId: Number(depId),
|
||||||
groupSemester: Number(groupSemester),
|
|
||||||
groupId: Number(groupId),
|
groupId: Number(groupId),
|
||||||
subjectsId: Number(subjectId),
|
subjectsId: Number(subjectId),
|
||||||
lessonTypeId: Number(lessonTypeId),
|
lessonTypeId: Number(lessonTypeId),
|
||||||
numberOfHours: Number(hours),
|
numberOfHours: Number(hours),
|
||||||
division: division,
|
isDivision: isDivision,
|
||||||
teacherId: Number(teacherId),
|
teacherId: Number(teacherId),
|
||||||
semesterType: semesterType,
|
semesterType: semesterType,
|
||||||
period: period
|
period: period
|
||||||
};
|
};
|
||||||
|
|
||||||
// Проверка на дубликат в уже добавленных записях
|
// Проверка на дубликат
|
||||||
const isDuplicate = preparedSchedules.some(s =>
|
const isDuplicate = preparedSchedules.some(s =>
|
||||||
s.period === newRecord.period &&
|
s.period === newRecord.period &&
|
||||||
s.semesterType === newRecord.semesterType &&
|
s.semesterType === newRecord.semesterType &&
|
||||||
s.groupSemester === newRecord.groupSemester &&
|
|
||||||
s.groupId === newRecord.groupId &&
|
s.groupId === newRecord.groupId &&
|
||||||
s.subjectsId === newRecord.subjectsId &&
|
s.subjectsId === newRecord.subjectsId &&
|
||||||
s.lessonTypeId === newRecord.lessonTypeId &&
|
s.lessonTypeId === newRecord.lessonTypeId &&
|
||||||
s.numberOfHours === newRecord.numberOfHours &&
|
s.numberOfHours === newRecord.numberOfHours &&
|
||||||
s.division === newRecord.division &&
|
s.isDivision === newRecord.isDivision &&
|
||||||
s.teacherId === newRecord.teacherId
|
s.teacherId === newRecord.teacherId
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -332,7 +392,7 @@ export async function initDepartment() {
|
|||||||
clearFormFields();
|
clearFormFields();
|
||||||
|
|
||||||
showAlert('create-schedule-alert', 'Запись добавлена ✓', 'success');
|
showAlert('create-schedule-alert', 'Запись добавлена ✓', 'success');
|
||||||
setTimeout(() => hideAlert('create-schedule-alert'), 2000);
|
setTimeout(() => hideAlert('create-schedule-alert'), 4000); // fix #1: 4 секунды
|
||||||
|
|
||||||
renderPreparedSchedules();
|
renderPreparedSchedules();
|
||||||
updateTableVisibility();
|
updateTableVisibility();
|
||||||
@@ -360,7 +420,6 @@ export async function initDepartment() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Ошибка сохранения записи:', err);
|
console.error('Ошибка сохранения записи:', err);
|
||||||
errors++;
|
errors++;
|
||||||
// Помечаем запись как дубликат, если бэк вернул соответствующую ошибку
|
|
||||||
const isDuplicate = err.status === 409 ||
|
const isDuplicate = err.status === 409 ||
|
||||||
(err.message && err.message.toLowerCase().includes('уже существует'));
|
(err.message && err.message.toLowerCase().includes('уже существует'));
|
||||||
failedRecords.push({
|
failedRecords.push({
|
||||||
@@ -382,7 +441,6 @@ export async function initDepartment() {
|
|||||||
updateTableVisibility();
|
updateTableVisibility();
|
||||||
setTimeout(closeOverlay, 2000);
|
setTimeout(closeOverlay, 2000);
|
||||||
} else {
|
} else {
|
||||||
// Оставляем неудачные записи для повторной попытки / удаления
|
|
||||||
preparedSchedules = failedRecords;
|
preparedSchedules = failedRecords;
|
||||||
renderPreparedSchedules();
|
renderPreparedSchedules();
|
||||||
if (saved > 0) {
|
if (saved > 0) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export async function initGroups() {
|
|||||||
populateEfSelects(educationForms);
|
populateEfSelects(educationForms);
|
||||||
await loadGroups();
|
await loadGroups();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
groupsTbody.innerHTML = '<tr><td colspan="7" class="loading-row">Ошибка загрузки данных</td></tr>';
|
groupsTbody.innerHTML = '<tr><td colspan="8" class="loading-row">Ошибка загрузки данных</td></tr>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ export async function initGroups() {
|
|||||||
allGroups = await api.get('/api/groups');
|
allGroups = await api.get('/api/groups');
|
||||||
applyGroupFilter();
|
applyGroupFilter();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
groupsTbody.innerHTML = '<tr><td colspan="7" class="loading-row">Ошибка загрузки</td></tr>';
|
groupsTbody.innerHTML = '<tr><td colspan="8" class="loading-row">Ошибка загрузки</td></tr>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ export async function initGroups() {
|
|||||||
|
|
||||||
function renderGroups(groups) {
|
function renderGroups(groups) {
|
||||||
if (!groups || !groups.length) {
|
if (!groups || !groups.length) {
|
||||||
groupsTbody.innerHTML = '<tr><td colspan="7" class="loading-row">Нет групп</td></tr>';
|
groupsTbody.innerHTML = '<tr><td colspan="8" class="loading-row">Нет групп</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
groupsTbody.innerHTML = groups.map(g => `
|
groupsTbody.innerHTML = groups.map(g => `
|
||||||
@@ -72,6 +72,7 @@ export async function initGroups() {
|
|||||||
<td><span class="badge badge-ef">${escapeHtml(g.educationFormName)}</span></td>
|
<td><span class="badge badge-ef">${escapeHtml(g.educationFormName)}</span></td>
|
||||||
<td>${g.departmentId || '-'}</td>
|
<td>${g.departmentId || '-'}</td>
|
||||||
<td>${g.course || '-'}</td>
|
<td>${g.course || '-'}</td>
|
||||||
|
<td>${escapeHtml(g.specialityCode || '-')}</td>
|
||||||
<td><button class="btn-delete" data-id="${g.id}">Удалить</button></td>
|
<td><button class="btn-delete" data-id="${g.id}">Удалить</button></td>
|
||||||
</tr>`).join('');
|
</tr>`).join('');
|
||||||
}
|
}
|
||||||
@@ -83,13 +84,15 @@ export async function initGroups() {
|
|||||||
const groupSize = document.getElementById('new-group-size').value;
|
const groupSize = document.getElementById('new-group-size').value;
|
||||||
const educationFormId = newGroupEfSelect.value;
|
const educationFormId = newGroupEfSelect.value;
|
||||||
const departmentId = document.getElementById('new-group-department').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 (!name) { showAlert('create-group-alert', 'Введите название группы', 'error'); return; }
|
||||||
if (!groupSize) { showAlert('create-group-alert', 'Введите размер группы', 'error'); return; }
|
if (!groupSize) { showAlert('create-group-alert', 'Введите размер группы', 'error'); return; }
|
||||||
if (!educationFormId) { showAlert('create-group-alert', 'Выберите форму обучения', 'error'); return; }
|
if (!educationFormId) { showAlert('create-group-alert', 'Выберите форму обучения', 'error'); return; }
|
||||||
if (!departmentId) { showAlert('create-group-alert', 'Введите ID кафедры', '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 {
|
try {
|
||||||
const data = await api.post('/api/groups', {
|
const data = await api.post('/api/groups', {
|
||||||
@@ -97,7 +100,8 @@ export async function initGroups() {
|
|||||||
groupSize: Number(groupSize),
|
groupSize: Number(groupSize),
|
||||||
educationFormId: Number(educationFormId),
|
educationFormId: Number(educationFormId),
|
||||||
departmentId: Number(departmentId),
|
departmentId: Number(departmentId),
|
||||||
yearStartStudy: Number(yearStartStudy)
|
course: Number(course),
|
||||||
|
specialityCode: specialityCode
|
||||||
});
|
});
|
||||||
showAlert('create-group-alert', `Группа "${escapeHtml(data.name || name)}" создана`, 'success');
|
showAlert('create-group-alert', `Группа "${escapeHtml(data.name || name)}" создана`, 'success');
|
||||||
createGroupForm.reset();
|
createGroupForm.reset();
|
||||||
|
|||||||
@@ -30,11 +30,11 @@
|
|||||||
<label for="filter-period">Учебный год</label>
|
<label for="filter-period">Учебный год</label>
|
||||||
<select id="filter-period" required>
|
<select id="filter-period" required>
|
||||||
<option value="">Выберите...</option>
|
<option value="">Выберите...</option>
|
||||||
<option value="2022-2023">2022/2023</option>
|
|
||||||
<option value="2023-2024">2023/2024</option>
|
|
||||||
<option value="2024-2025">2024/2025</option>
|
|
||||||
<option value="2025-2026">2025/2026</option>
|
|
||||||
<option value="2026-2027">2026/2027</option>
|
<option value="2026-2027">2026/2027</option>
|
||||||
|
<option value="2025-2026">2025/2026</option>
|
||||||
|
<option value="2024-2025">2024/2025</option>
|
||||||
|
<option value="2023-2024">2023/2024</option>
|
||||||
|
<option value="2022-2023">2022/2023</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -63,9 +63,9 @@
|
|||||||
<label for="cs-period">Учебный год</label>
|
<label for="cs-period">Учебный год</label>
|
||||||
<select id="cs-period" required>
|
<select id="cs-period" required>
|
||||||
<option value="">Выберите...</option>
|
<option value="">Выберите...</option>
|
||||||
<option value="2024-2025">2024/2025</option>
|
|
||||||
<option value="2025-2026">2025/2026</option>
|
|
||||||
<option value="2026-2027">2026/2027</option>
|
<option value="2026-2027">2026/2027</option>
|
||||||
|
<option value="2025-2026">2025/2026</option>
|
||||||
|
<option value="2024-2025">2024/2025</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -83,11 +83,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" style="flex: 1 1 150px;">
|
|
||||||
<label for="cs-semester">Курс/Семестр (номер)</label>
|
|
||||||
<input type="number" id="cs-semester" required min="1" max="12" placeholder="Например: 1">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group" style="flex: 1 1 180px;">
|
<div class="form-group" style="flex: 1 1 180px;">
|
||||||
<label for="cs-group">Группа</label>
|
<label for="cs-group">Группа</label>
|
||||||
<select id="cs-group" required>
|
<select id="cs-group" required>
|
||||||
@@ -159,7 +154,6 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Уч. год</th>
|
<th>Уч. год</th>
|
||||||
<th>Семестр</th>
|
<th>Семестр</th>
|
||||||
<th>№</th>
|
|
||||||
<th>Группа</th>
|
<th>Группа</th>
|
||||||
<th>Дисциплина</th>
|
<th>Дисциплина</th>
|
||||||
<th>Вид</th>
|
<th>Вид</th>
|
||||||
@@ -171,7 +165,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody id="prepared-schedules-tbody">
|
<tbody id="prepared-schedules-tbody">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="10" class="loading-row">Нет записей</td>
|
<td colspan="9" class="loading-row">Нет записей</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -25,6 +25,10 @@
|
|||||||
<label for="new-group-yearStartStudy">Год начала обучения</label>
|
<label for="new-group-yearStartStudy">Год начала обучения</label>
|
||||||
<input type="number" id="new-group-yearStartStudy" required pattern="^20\d{2}$" maxlength="3" placeholder="2026">
|
<input type="number" id="new-group-yearStartStudy" required pattern="^20\d{2}$" maxlength="3" placeholder="2026">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="new-group-speciality-code">Код специальности</label>
|
||||||
|
<input type="text" id="new-group-speciality-code" placeholder="09.03.01" required>
|
||||||
|
</div>
|
||||||
<button type="submit" class="btn-primary">Создать</button>
|
<button type="submit" class="btn-primary">Создать</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-alert" id="create-group-alert" role="alert"></div>
|
<div class="form-alert" id="create-group-alert" role="alert"></div>
|
||||||
@@ -51,12 +55,13 @@
|
|||||||
<th>Форма обучения</th>
|
<th>Форма обучения</th>
|
||||||
<th>ID кафедры</th>
|
<th>ID кафедры</th>
|
||||||
<th>Курс</th>
|
<th>Курс</th>
|
||||||
|
<th>Код специальности</th>
|
||||||
<th>Действия</th>
|
<th>Действия</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="groups-tbody">
|
<tbody id="groups-tbody">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="7" class="loading-row">Загрузка...</td>
|
<td colspan="8" class="loading-row">Загрузка...</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Reference in New Issue
Block a user