2026-06-25 06:44:31 +03:00

6.1 KiB
Raw Permalink Blame History

Индикация загрузки при переключении страниц в таблицах брокера

Дата: 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

interface TableLoadingOverlayProps {
  pageNumber?: number;
}

function TableLoadingOverlay({ pageNumber }: TableLoadingOverlayProps) {
  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: 'rgba(255,255,255,0.65)',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      flexDirection: 'column', gap: 12,
      transition: 'opacity 0.2s ease',
    }}>
      <div className="loading-spinner" />
      {pageNumber !== undefined && (
        <span style={{ fontSize: 13, color: 'var(--color-text-secondary)' }}>
          Загрузка страницы {pageNumber}
        </span>
      )}
    </div>
  );
}

Пагинация: спиннер в кнопке

При isFetching кнопка «→» или «←» показывает спиннер вместо стрелки и становится disabled.

@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 остаётся, данные не мигают