# Индикация загрузки при переключении страниц в таблицах брокера Дата: 2026-06-18 Статус: реализовано ## Контекст Страница детального просмотра брокерского счёта (`BrokerAccountDetailPage.tsx`) содержит несколько таблиц с пагинацией: - **PositionGroupTable** — Акции, Облигации, ETF, Фонды (4 независимые таблицы с курсорной пагинацией) - **BrokerOperationsTable** — Операции (курсорная пагинация, управляемая из родительского компонента) Текущее поведение при переключении страниц: `isLoading === true` → таблица скрывается, показывается `TableSkeleton` (shimmer-строки). Это создаёт визуальный flash: контент исчезает → скелетон → новые данные. При этом `placeholderData: keepPreviousData` уже настроен в хуках, но компоненты его не используют — они проверяют `isLoading`, а не `data`. ## Цель Добавить плавную индикацию загрузки при переключении страниц, чтобы пользователь видел, что данные обновляются, но не терял визуальный контекст. ## Дизайн (выбран C3) ### Визуальное поведение 1. При нажатии «→» (вперед) или «←» (назад): - Текущее содержимое таблицы **остаётся видимым** (предыдущая страница) - Поверх таблицы появляется **полупрозрачный overlay** с центрированным спиннером - Кнопка пагинации показывает спиннер и блокируется 2. Когда новые данные загружены: - Overlay исчезает с fade-out - Таблица обновляется новыми данными 3. При первой загрузке (initial load): - Overlay не используется (нет старых данных для показа) - Показывается `TableSkeleton` (как сейчас) ### Как это работает технически TanStack Query v5 предоставляет два флага: - `isLoading` — true, когда данных **нет** и идёт первый запрос (initial load) - `isFetching` — true при любом запросе (включая фоновые refetch'и при смене cursor) Логика рендеринга для таблиц: ``` if isLoading → TableSkeleton (первая загрузка, данных нет) if isFetching && data → TableLoadingOverlay + старые данные (переключение страниц) иначе → рендер таблицы с данными ``` ### Компонент TableLoadingOverlay ```tsx interface TableLoadingOverlayProps { pageNumber?: number; } function TableLoadingOverlay({ pageNumber }: TableLoadingOverlayProps) { return (
{pageNumber !== undefined && ( Загрузка страницы {pageNumber}… )}
); } ``` ### Пагинация: спиннер в кнопке При `isFetching` кнопка «→» или «←» показывает спиннер вместо стрелки и становится disabled. ```css @keyframes loading-spin { to { transform: rotate(360deg); } } .loading-spinner { width: 20px; height: 20px; border: 2px solid var(--color-border); border-top-color: var(--color-accent); border-radius: 50%; animation: loading-spin 0.8s linear infinite; } ``` ## Где применяется | Компонент | Что меняется | |---|---| | `BrokerPositionsSection.tsx` (PositionGroupTable) | Overlay вместо TableSkeleton при isFetching. Спиннер в кнопках пагинации | | `BrokerOperationsTable.tsx` | Overlay вместо TableSkeleton при isFetching. Спиннер в кнопках пагинации | ## Файлы для изменения | Файл | Изменение | |---|---| | `apps/frontend/src/styles.css` | Добавить `@keyframes loading-spin`, `.loading-spinner`, `.table-loading-overlay` | | `apps/frontend/src/pages/broker/BrokerPositionsSection.tsx` | Overlay + спиннер в пагинации. Использовать `isFetching` из хука | | `apps/frontend/src/pages/broker/BrokerOperationsTable.tsx` | Overlay + спиннер в пагинации. Использовать `isFetching` из хука | | `apps/frontend/src/pages/broker/BrokerAccountDetailPage.tsx` | Прокинуть `isFetching` для операций (из useBrokerOperations) | | `apps/frontend/src/pages/broker/BrokerPages.test.tsx` | Обновить тесты для overlay-логики | ## Тестирование - `npm run test:frontend` — существующие тесты проходят с учётом изменений - Ручная проверка: переключение страниц в Акциях, Облигациях, Операциях — overlay появляется/исчезает - Ручная проверка: при первой загрузке — skeleton (не overlay) - Ручная проверка: при быстром переключении (быстрее, чем загрузка) — overlay остаётся, данные не мигают