Админка, настройки, логин, кабинеты преподавателя и студента полностью переведены на 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
88 lines
3.1 KiB
JavaScript
88 lines
3.1 KiB
JavaScript
import { apiClient } from '../shared/api-client.js';
|
||
|
||
export async function fetchEquipments(client = apiClient) {
|
||
try {
|
||
return await client.get('/api/equipments');
|
||
} catch {
|
||
console.error('Не удалось загрузить список оборудования');
|
||
return [];
|
||
}
|
||
}
|
||
|
||
export function fetchClassrooms(client = apiClient) {
|
||
return client.get(
|
||
'/api/classrooms?includeArchived=true',
|
||
{ errorMessage: 'Не удалось загрузить список аудиторий' }
|
||
);
|
||
}
|
||
|
||
export function createClassroom({ name, capacity, equipmentIds }, client = apiClient) {
|
||
return client.post(
|
||
'/api/classrooms',
|
||
{ name, capacity, equipmentIds, isAvailable: true },
|
||
{ errorMessage: 'Ошибка создания' }
|
||
);
|
||
}
|
||
|
||
export function updateClassroom(id, payload, client = apiClient) {
|
||
return client.put(`/api/classrooms/${id}`, payload, { errorMessage: 'Ошибка обновления' });
|
||
}
|
||
|
||
export function toggleClassroomAvailability(id, isAvailable, client = apiClient) {
|
||
return updateClassroom(id, { isAvailable }, client);
|
||
}
|
||
|
||
export function archiveClassroom(id, client = apiClient) {
|
||
return client.delete(`/api/classrooms/${id}`, { errorMessage: 'Не удалось архивировать аудиторию' });
|
||
}
|
||
|
||
export function restoreClassroom(id, client = apiClient) {
|
||
return client.post(
|
||
`/api/classrooms/${id}/restore`,
|
||
{},
|
||
{ errorMessage: 'Не удалось восстановить аудиторию' }
|
||
);
|
||
}
|
||
|
||
export function createEquipment(name, client = apiClient) {
|
||
return client.post('/api/equipments', { name }, { errorMessage: 'Ошибка добавления' });
|
||
}
|
||
|
||
export function deleteEquipment(id, client = apiClient) {
|
||
return client.delete(`/api/equipments/${id}`, { errorMessage: 'Не удалось удалить оборудование' });
|
||
}
|
||
|
||
export function parseCapacity(value) {
|
||
return parseInt(value, 10);
|
||
}
|
||
|
||
export function activeClassrooms(classrooms) {
|
||
return (classrooms || []).filter(classroom => classroom.isAvailable && classroom.status !== 'ARCHIVED');
|
||
}
|
||
|
||
export function classroomStatusLabel(classroom) {
|
||
if (classroom.status === 'ARCHIVED') return 'Архив';
|
||
return classroom.isAvailable ? 'Доступна' : 'Не доступна';
|
||
}
|
||
|
||
export function equipmentNamesText(classroom) {
|
||
const names = (classroom.equipments || []).map(equipment => equipment.name);
|
||
return names.length ? names.join(', ') : null;
|
||
}
|
||
|
||
export function equipmentSelectLabel(selectedNames) {
|
||
const names = selectedNames || [];
|
||
if (names.length === 0) return 'Выберите оборудование...';
|
||
if (names.length === 1) return names[0];
|
||
return `Выбрано: ${names.length}`;
|
||
}
|
||
|
||
export function equipmentIdSelection(equipments, selectedIds) {
|
||
const selected = new Set((selectedIds || []).map(String));
|
||
return (equipments || []).map(equipment => ({
|
||
id: equipment.id,
|
||
name: equipment.name,
|
||
checked: selected.has(String(equipment.id))
|
||
}));
|
||
}
|