Files
magistr/frontend/react/admin/schedule-versions-service.js
dipatrik10 97bf3cc50a feat: завершить миграцию фронтенда на React (этапы 1-5)
Админка, настройки, логин, кабинеты преподавателя и студента
полностью переведены на React 19 (модель strangler, docs/REACT_MIGRATION.md).

- 15 вкладок админки: lazy-загрузка, общий error boundary, сервисный слой
- Удалены legacy-views и переходный слой LegacyTabView (45 файлов)
- Сборка esbuild (frontend/scripts/build-react.mjs) с бюджетами gzip на чанки
- CSP-хэши, кэширование: chunk'и immutable, CSS/HTML no-cache (react-cache.conf)
- Фиксы: lazy-импорт именованных экспортов (краш после авторизации),
  растяжение #admin-root/#settings-root на всю ширину,
  возврат subjects/lessonTypes из справочников (краш просмотра расписаний),
  отказ от заведомо широкого поиска расписания без выбранной цели,
  центрирование карточки логина
- Тесты: 237/237 (npm run check), регрессионные на lazy-вкладки и ширину
- Документация: REACT_MIGRATION.md, REACT_MIGRATION_RESULT.md, FRONTEND.md
2026-09-28 23:15:04 +03:00

125 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { apiClient } from '../shared/api-client.js';
import { formatLocalDate } from '../../admin/js/utils.js';
// Срез «Контур публикации» (этап 5, волна 5): чистая логика и контракты API,
// перенесённые из legacy admin/js/views/schedule-versions.js без изменения поведения.
// --- Ключи localStorage для переходов между вкладками расписания ---
// (общие с конструктором правил и анализом качества)
export const SCHEDULE_YEAR_STORAGE_KEY = 'magistr.schedule.yearId';
export const SCHEDULE_SEMESTER_STORAGE_KEY = 'magistr.schedule.semesterId';
export const SCHEDULE_OPEN_VERSION_STORAGE_KEY = 'magistr.schedule.openVersionId';
export const SCHEDULE_OPEN_RULE_STORAGE_KEY = 'magistr.schedule.openRuleId';
export const SCHEDULE_VERSION_STORAGE_KEY = 'magistr.schedule.versionId';
export const QUALITY_YEAR_STORAGE_KEY = 'magistr.quality.yearId';
export const QUALITY_SEMESTER_STORAGE_KEY = 'magistr.quality.semesterId';
export const QUALITY_VERSION_STORAGE_KEY = 'magistr.quality.versionId';
// --- Подписи ---
export function historyActionLabel(action) {
return {
CREATED: 'Создан черновик',
PUBLISHED: 'Опубликовано',
ARCHIVED: 'Архивировано',
RESTORED: 'Восстановлено'
}[action] || action;
}
export function versionSemesterLabel(semester) {
const type = semester.semesterType === 'autumn' ? 'Осенний' : 'Весенний';
return `${semester.academicYearTitle} · ${type} семестр`;
}
export function formatVersionDateTime(value) {
if (!value) return 'дата не указана';
return new Intl.DateTimeFormat('ru-RU', {
day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit'
}).format(new Date(value));
}
export function ruleChangeTypeLabel(type) {
return { ADDED: 'Добавлено', REMOVED: 'Удалено', CHANGED: 'Изменено' }[type] || type;
}
export function draftBasisText(version) {
return `Основа: ${version.basedOnVersionId ? `версия #${version.basedOnVersionId}` : 'пустое расписание'}`;
}
// --- Группировка версий ---
export function splitScheduleVersions(versions) {
const list = Array.isArray(versions) ? versions : [];
return {
published: list.find(version => version.status === 'PUBLISHED') || null,
drafts: list.filter(version => version.status === 'DRAFT'),
archive: list.filter(version => version.status === 'ARCHIVED')
};
}
// --- Сводка проверки перед публикацией ---
export function publishValidationSummary(validation, diff) {
return validation.valid
? `Проверка пройдена: ${validation.ruleCount} правил, ${validation.slotCount} слотов. `
+ `Занятия: +${diff.addedLessonCount}, ~${diff.changedLessonCount}, −${diff.removedLessonCount}.`
: `Публикация заблокирована: ${(validation.issues || []).join('; ')}`;
}
// --- Контракты API ---
export function scheduleVersionsEndpoint(semesterId) {
return `/api/edu-office/schedule/versions?semesterId=${encodeURIComponent(semesterId)}`;
}
export function scheduleVersionHistoryEndpoint(semesterId) {
return `/api/edu-office/schedule/versions/history?semesterId=${encodeURIComponent(semesterId)}`;
}
export function scheduleVersionDiffEndpoint(versionId) {
return `/api/edu-office/schedule/versions/${encodeURIComponent(versionId)}/diff`;
}
export function scheduleVersionValidateEndpoint(versionId) {
return `/api/edu-office/schedule/versions/${encodeURIComponent(versionId)}/validate`;
}
export function scheduleVersionActionEndpoint(versionId, mode) {
return `/api/edu-office/schedule/versions/${encodeURIComponent(versionId)}/${mode}`;
}
export async function fetchScheduleSemesters(client = apiClient) {
return client.get('/api/schedule/semesters', { errorMessage: 'Не удалось загрузить семестры.' });
}
export async function fetchScheduleVersions(semesterId, client = apiClient) {
return client.get(scheduleVersionsEndpoint(semesterId), { errorMessage: 'Не удалось загрузить версии расписания.' });
}
export async function fetchScheduleVersionHistory(semesterId, client = apiClient) {
return client.get(scheduleVersionHistoryEndpoint(semesterId), { errorMessage: 'Не удалось загрузить журнал действий.' });
}
export async function fetchScheduleVersionDiff(versionId, client = apiClient) {
return client.get(scheduleVersionDiffEndpoint(versionId), { errorMessage: 'Не удалось сравнить версии расписания.' });
}
export async function fetchScheduleVersionValidation(versionId, client = apiClient) {
return client.get(scheduleVersionValidateEndpoint(versionId), { errorMessage: 'Не удалось проверить черновик.' });
}
export async function createScheduleDraftVersion(payload, client = apiClient) {
return client.post('/api/edu-office/schedule/versions', payload, { errorMessage: 'Не удалось создать черновик.' });
}
export async function runScheduleVersionAction(versionId, mode, reason, client = apiClient) {
return client.post(
scheduleVersionActionEndpoint(versionId, mode),
{ reason },
{ errorMessage: 'Операцию не удалось выполнить.' }
);
}
export { formatLocalDate };