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
This commit is contained in:
38
frontend/react/shared/ui/AsyncState.jsx
Normal file
38
frontend/react/shared/ui/AsyncState.jsx
Normal file
@@ -0,0 +1,38 @@
|
||||
const DEFAULT_COPY = Object.freeze({
|
||||
empty: {
|
||||
message: 'Здесь пока нет данных',
|
||||
title: 'Пусто'
|
||||
},
|
||||
error: {
|
||||
message: 'Попробуйте повторить запрос',
|
||||
title: 'Не удалось загрузить данные'
|
||||
},
|
||||
loading: {
|
||||
message: 'Это займёт несколько секунд',
|
||||
title: 'Загружаем данные'
|
||||
}
|
||||
});
|
||||
|
||||
export function AsyncState({ kind = 'loading', message, onRetry, title }) {
|
||||
const copy = DEFAULT_COPY[kind] || DEFAULT_COPY.loading;
|
||||
const isError = kind === 'error';
|
||||
|
||||
return (
|
||||
<section
|
||||
className={`ui-state ui-state--${kind}`}
|
||||
role={isError ? 'alert' : 'status'}
|
||||
aria-live={isError ? 'assertive' : 'polite'}
|
||||
>
|
||||
<span className="ui-state__signal" aria-hidden="true" />
|
||||
<div className="ui-state__content">
|
||||
<h2 className="ui-state__title">{title || copy.title}</h2>
|
||||
<p className="ui-state__message">{message || copy.message}</p>
|
||||
</div>
|
||||
{isError && onRetry && (
|
||||
<button type="button" className="btn btn-md btn-secondary" onClick={onRetry}>
|
||||
Повторить
|
||||
</button>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user