Files
magistr/frontend/react/admin/university-structure-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

151 lines
5.3 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';
export const DEPARTMENT_TABLE_COLUMNS = 4;
export const SPECIALTY_TABLE_COLUMNS = 4;
export const PROFILE_TABLE_COLUMNS = 5;
export const LIST_PAGE_SIZES = [25, 50, 100];
// --- Загрузка ---
export function fetchDepartments(client = apiClient) {
return client.get('/api/departments', { errorMessage: 'Не удалось загрузить кафедры' });
}
export function fetchSpecialties(client = apiClient) {
return client.get('/api/specialties', { errorMessage: 'Не удалось загрузить специальности' });
}
export function fetchAllProfiles(client = apiClient) {
return client.get('/api/specialties/profiles', { errorMessage: 'Не удалось загрузить профили обучения' });
}
export function fetchProfiles(specId, client = apiClient) {
return client.get(`/api/specialties/${specId}/profiles`, { errorMessage: 'Не удалось загрузить профили обучения' });
}
// --- Кафедры ---
export function createDepartment({ name, code }, client = apiClient) {
return client.post(
'/api/departments',
{ departmentName: name, departmentCode: Number(code) },
{ errorMessage: 'Ошибка создания кафедры' }
);
}
export function updateDepartment(id, { name, code }, client = apiClient) {
return client.put(
`/api/departments/${id}`,
{ departmentName: name, departmentCode: Number(code) },
{ errorMessage: 'Ошибка обновления кафедры' }
);
}
export function deleteDepartment(id, client = apiClient) {
return client.delete(`/api/departments/${id}`, { errorMessage: 'Ошибка удаления кафедры' });
}
// --- Специальности ---
export function createSpecialty({ name, code }, client = apiClient) {
return client.post(
'/api/specialties',
{ specialityName: name, specialityCode: code },
{ errorMessage: 'Ошибка создания специальности' }
);
}
export function updateSpecialty(id, { name, code }, client = apiClient) {
return client.put(
`/api/specialties/${id}`,
{ specialityName: name, specialityCode: code },
{ errorMessage: 'Ошибка обновления специальности' }
);
}
export function deleteSpecialty(id, client = apiClient) {
return client.delete(`/api/specialties/${id}`, { errorMessage: 'Ошибка удаления специальности' });
}
// --- Профили ---
export function createProfile(specId, { name, description }, client = apiClient) {
return client.post(
`/api/specialties/${specId}/profiles`,
{ name, description },
{ errorMessage: 'Ошибка сохранения профиля' }
);
}
export function updateProfile(specId, profileId, { name, description }, client = apiClient) {
return client.put(
`/api/specialties/${specId}/profiles/${profileId}`,
{ id: Number(profileId), name, description },
{ errorMessage: 'Ошибка сохранения профиля' }
);
}
export function deleteProfile(specId, profileId, client = apiClient) {
return client.delete(`/api/specialties/${specId}/profiles/${profileId}`, { errorMessage: 'Ошибка удаления профиля' });
}
// --- Клиентская фильтрация и пагинация списков (как в legacy listPage) ---
export function filterListItems(items, query, fields) {
const normalized = String(query || '').trim().toLocaleLowerCase('ru');
if (!normalized) return [...(items || [])];
return (items || []).filter(item =>
fields(item).join(' ').toLocaleLowerCase('ru').includes(normalized));
}
export function clampPage(page, totalPages) {
return Math.min(Math.max(Number(page) || 0, 0), Math.max(0, totalPages - 1));
}
export function pageSlice(items, page, size) {
const pageSize = Number(size) || 25;
return (items || []).slice(page * pageSize, (page + 1) * pageSize);
}
export function departmentFields(department) {
return [department.id, department.departmentName || department.name, department.departmentCode ?? department.code];
}
export function specialtyFields(speciality) {
return [
speciality.id,
speciality.specialityName || speciality.name,
speciality.specialityCode || speciality.specialtyCode || speciality.specialty_code
];
}
export function profileFields(profile) {
return [profile.id, profile.name, profile.description, profile.specialityCode, profile.specialityName];
}
// --- Метки ---
export function departmentName(department) {
return department.departmentName || department.name;
}
export function departmentCode(department) {
return department.departmentCode ?? department.code;
}
export function specialtyName(speciality) {
return speciality.specialityName || speciality.name;
}
export function specialtyCode(speciality) {
return speciality.specialityCode || speciality.specialtyCode || speciality.specialty_code;
}
export function specialtyOptionLabel(speciality) {
return `${specialtyCode(speciality)} · ${specialtyName(speciality)}`;
}
export function profileSpecialtyLabel(profile) {
return `${profile.specialityCode} · ${profile.specialityName}`;
}