Много разных изменений
This commit is contained in:
@@ -3,6 +3,9 @@ import { bindDateInputMask, readDateInputIso, setDateInputIso } from '../date-in
|
||||
import { escapeHtml, formatLocalDate, showAlert, hideAlert } from '../utils.js';
|
||||
import { academicCalendarWeekNumber } from './academic-calendar-grid.js';
|
||||
import { buildAcademicCalendarTitle } from './academic-calendar-title.js';
|
||||
import { confirmAction } from '../dialog.js';
|
||||
import { confirmAbandonDirtyChanges, createDirtyScope } from '../dirty-state.js';
|
||||
import { trackUxEvent } from '../../../telemetry.js';
|
||||
|
||||
const DAY_OPTIONS = [
|
||||
{ value: 1, label: 'Понедельник', shortLabel: 'Пн' },
|
||||
@@ -219,6 +222,10 @@ export async function initAcademicCalendar() {
|
||||
let isSyncingCalendarDayDialog = false;
|
||||
let calendarDayTooltip = null;
|
||||
let calendarDayTooltipPositionAnimation = null;
|
||||
let loadedGridCalendarId = '';
|
||||
let loadedSubjectCalendarId = '';
|
||||
const gridDirtyScope = createDirtyScope('academic-calendar-grid', 'В календарной сетке есть несохранённые изменения. Загрузить другой график и потерять их?');
|
||||
const subjectDirtyScope = createDirtyScope('academic-calendar-subjects', 'В привязках дисциплин есть несохранённые изменения. Продолжить без сохранения?');
|
||||
|
||||
bindEvents();
|
||||
|
||||
@@ -248,7 +255,10 @@ export async function initAcademicCalendar() {
|
||||
].forEach(bindDateInputMask);
|
||||
|
||||
calendarTabButtons.forEach(button => {
|
||||
button.addEventListener('click', () => activateCalendarTab(button.dataset.calendarTab));
|
||||
button.addEventListener('click', async () => {
|
||||
if (!await confirmAbandonDirtyChanges()) return;
|
||||
activateCalendarTab(button.dataset.calendarTab);
|
||||
});
|
||||
});
|
||||
academicYearsRefreshButton?.addEventListener('click', loadYears);
|
||||
calendarsRefreshButton?.addEventListener('click', loadCalendars);
|
||||
@@ -269,6 +279,7 @@ export async function initAcademicCalendar() {
|
||||
if (event.target.matches('.calendar-day-activity')) {
|
||||
updateCalendarDayVisual(event.target.closest('.calendar-day'));
|
||||
renderCalendarTotals();
|
||||
markGridDirty();
|
||||
}
|
||||
});
|
||||
calendarGrid?.addEventListener('mousedown', (event) => {
|
||||
@@ -320,6 +331,7 @@ export async function initAcademicCalendar() {
|
||||
});
|
||||
updateCalendarDayDialogSummaryForSelection(days);
|
||||
renderCalendarTotals();
|
||||
markGridDirty();
|
||||
});
|
||||
calendarDayDialogTimeSaveButton?.addEventListener('click', closeCalendarDayDialog);
|
||||
calendarDayDialogTimeClearButton?.addEventListener('click', clearTimeSlotDateAssignment);
|
||||
@@ -441,7 +453,13 @@ export async function initAcademicCalendar() {
|
||||
}
|
||||
|
||||
if (deleteYearButton) {
|
||||
if (!confirm('Удалить учебный год вместе с семестрами и календарными графиками?')) return;
|
||||
const confirmed = await confirmAction({
|
||||
title: 'Удаление учебного года',
|
||||
message: 'Удалить учебный год вместе с семестрами и календарными графиками?',
|
||||
confirmLabel: 'Удалить',
|
||||
danger: true
|
||||
});
|
||||
if (!confirmed) return;
|
||||
try {
|
||||
await api.delete('/api/admin/calendar/years/' + deleteYearButton.dataset.id);
|
||||
showAlert('academic-year-alert', 'Учебный год удалён', 'success');
|
||||
@@ -653,7 +671,13 @@ export async function initAcademicCalendar() {
|
||||
}
|
||||
|
||||
if (deleteButton) {
|
||||
if (!confirm('Удалить календарный график?')) return;
|
||||
const confirmed = await confirmAction({
|
||||
title: 'Удаление календарного графика',
|
||||
message: 'Удалить календарный график? Связанные настройки больше не будут доступны.',
|
||||
confirmLabel: 'Удалить',
|
||||
danger: true
|
||||
});
|
||||
if (!confirmed) return;
|
||||
try {
|
||||
await api.delete('/api/admin/academic-calendars/' + deleteButton.dataset.id);
|
||||
showAlert('academic-calendar-alert', 'Календарный график удалён', 'success');
|
||||
@@ -691,9 +715,16 @@ export async function initAcademicCalendar() {
|
||||
async function loadCalendarSubjects() {
|
||||
hideAlert('calendar-subject-alert');
|
||||
const calendar = selectedSubjectCalendar();
|
||||
if (subjectDirtyScope.isDirty() && !await confirmAbandonDirtyChanges()) {
|
||||
subjectCalendarSelect.value = loadedSubjectCalendarId;
|
||||
refreshSelectVisuals(subjectCalendarSelect);
|
||||
return;
|
||||
}
|
||||
populateSubjectSemesterSelect();
|
||||
calendarSubjectRows = [];
|
||||
if (!calendar) {
|
||||
loadedSubjectCalendarId = '';
|
||||
clearSubjectDirty();
|
||||
renderCalendarSubjects();
|
||||
return;
|
||||
}
|
||||
@@ -701,6 +732,8 @@ export async function initAcademicCalendar() {
|
||||
try {
|
||||
const rows = await api.get(`/api/admin/academic-calendars/${calendar.id}/subjects`);
|
||||
calendarSubjectRows = sortCalendarSubjectRows(rows || []);
|
||||
loadedSubjectCalendarId = String(calendar.id);
|
||||
clearSubjectDirty();
|
||||
renderCalendarSubjects();
|
||||
} catch (error) {
|
||||
subjectSummary.innerHTML = `<div class="loading-row">Ошибка загрузки: ${escapeHtml(error.message)}</div>`;
|
||||
@@ -741,6 +774,7 @@ export async function initAcademicCalendar() {
|
||||
}
|
||||
]);
|
||||
renderCalendarSubjects();
|
||||
markSubjectDirty();
|
||||
showAlert('calendar-subject-alert', 'Дисциплина добавлена. Сохраните привязки.', 'success');
|
||||
}
|
||||
|
||||
@@ -759,6 +793,8 @@ export async function initAcademicCalendar() {
|
||||
const saved = await api.put(`/api/admin/academic-calendars/${calendar.id}/subjects`, payload);
|
||||
calendarSubjectRows = sortCalendarSubjectRows(saved || []);
|
||||
renderCalendarSubjects();
|
||||
clearSubjectDirty();
|
||||
trackUxEvent('calendar_subjects_saved', { calendarId: calendar.id, count: calendarSubjectRows.length });
|
||||
showAlert('calendar-subject-alert', 'Привязки дисциплин сохранены', 'success');
|
||||
} catch (error) {
|
||||
showAlert('calendar-subject-alert', error.message || 'Ошибка сохранения дисциплин графика', 'error');
|
||||
@@ -774,6 +810,7 @@ export async function initAcademicCalendar() {
|
||||
Number(row.semesterNumber) !== semesterNumber || Number(row.subjectId) !== subjectId
|
||||
);
|
||||
renderCalendarSubjects();
|
||||
markSubjectDirty();
|
||||
showAlert('calendar-subject-alert', 'Дисциплина убрана. Сохраните привязки.', 'success');
|
||||
}
|
||||
|
||||
@@ -820,6 +857,11 @@ export async function initAcademicCalendar() {
|
||||
async function loadCalendarGrid() {
|
||||
hideAlert('calendar-editor-alert');
|
||||
const calendar = selectedEditorCalendar();
|
||||
if (gridDirtyScope.isDirty() && !await confirmAbandonDirtyChanges()) {
|
||||
editorCalendarSelect.value = loadedGridCalendarId;
|
||||
refreshSelectVisuals(editorCalendarSelect);
|
||||
return;
|
||||
}
|
||||
if (!calendar) {
|
||||
showAlert('calendar-editor-alert', 'Выберите календарный график', 'error');
|
||||
return;
|
||||
@@ -829,6 +871,9 @@ export async function initAcademicCalendar() {
|
||||
const rows = await api.get(`/api/admin/academic-calendars/${calendar.id}/grid`);
|
||||
const gridRows = rows.length ? rows : buildDefaultGrid(calendar);
|
||||
renderCalendarGrid(gridRows);
|
||||
loadedGridCalendarId = String(calendar.id);
|
||||
clearGridDirty();
|
||||
trackUxEvent('calendar_grid_loaded', { calendarId: calendar.id, hasSavedRows: rows.length > 0 });
|
||||
showAlert('calendar-editor-alert', rows.length ? 'Сетка загружена' : 'Подготовлена новая сетка по датам учебного года', 'success');
|
||||
} catch (error) {
|
||||
showAlert('calendar-editor-alert', error.message || 'Ошибка загрузки сетки', 'error');
|
||||
@@ -856,6 +901,8 @@ export async function initAcademicCalendar() {
|
||||
}
|
||||
try {
|
||||
await api.put(`/api/admin/academic-calendars/${calendar.id}/grid`, rows);
|
||||
clearGridDirty();
|
||||
trackUxEvent('calendar_grid_saved', { calendarId: calendar.id, cells: rows.length });
|
||||
showAlert('calendar-editor-alert', 'Календарный учебный график сохранён', 'success');
|
||||
await loadCalendarGrid();
|
||||
} catch (error) {
|
||||
@@ -895,9 +942,30 @@ export async function initAcademicCalendar() {
|
||||
}
|
||||
});
|
||||
renderCalendarTotals();
|
||||
if (changed) markGridDirty();
|
||||
showAlert('calendar-editor-alert', `Обновлено ячеек: ${changed}`, 'success');
|
||||
}
|
||||
|
||||
function markGridDirty() {
|
||||
gridDirtyScope.mark();
|
||||
if (editorSaveButton) editorSaveButton.textContent = 'Сохранить сетку •';
|
||||
}
|
||||
|
||||
function clearGridDirty() {
|
||||
gridDirtyScope.clear();
|
||||
if (editorSaveButton) editorSaveButton.textContent = 'Сохранить сетку';
|
||||
}
|
||||
|
||||
function markSubjectDirty() {
|
||||
subjectDirtyScope.mark();
|
||||
if (subjectSaveButton) subjectSaveButton.textContent = 'Сохранить привязки •';
|
||||
}
|
||||
|
||||
function clearSubjectDirty() {
|
||||
subjectDirtyScope.clear();
|
||||
if (subjectSaveButton) subjectSaveButton.textContent = 'Сохранить привязки';
|
||||
}
|
||||
|
||||
function renderCalendarGrid(rows) {
|
||||
activeCalendarDay = null;
|
||||
selectedCalendarDayKey = null;
|
||||
|
||||
Reference in New Issue
Block a user