# Pagination Loading Overlay — Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add overlay + spinner to all broker paginated tables when switching pages **Architecture:** Use TanStack Query's `isFetching` (any fetch) vs `isLoading` (initial fetch) to show overlay when data exists and a new page is loading. Keep `keepPreviousData` so old data stays visible under the overlay. Add CSS spinner animation. **Tech Stack:** React, TanStack Query v5, CSS custom properties --- ### Task 1: CSS — spinner animation and overlay styles **Files:** - Modify: `apps/frontend/src/styles.css` - [ ] **Step 1: Add spinner keyframes and loading-spinner class** Add to `apps/frontend/src/styles.css` at the end: ```css @keyframes loading-spin { to { transform: rotate(360deg); } } .loading-spinner { width: 20px; height: 20px; border: 2px solid var(--color-bg); border-top-color: var(--color-primary); border-radius: 50%; animation: loading-spin 0.8s linear infinite; } .table-container { position: relative; } .table-loading-overlay { position: absolute; inset: 0; background: rgba(255, 255, 255, 0.65); display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 12px; transition: opacity 0.2s ease; z-index: 1; } ``` - [ ] **Step 2: Commit** ```bash git add apps/frontend/src/styles.css git commit -m "style: add loading-spinner and overlay CSS classes" ``` --- ### Task 2: PositionGroupTable — overlay on pagination + spinner in buttons **Files:** - Modify: `apps/frontend/src/pages/broker/BrokerPositionsSection.tsx` - [ ] **Step 1: Add `isFetching` to the query destructuring** Line 103 changes from: ```tsx const { data: page, isLoading } = useBrokerPositions(accountId, query); ``` to: ```tsx const { data: page, isLoading, isFetching } = useBrokerPositions(accountId, query); ``` - [ ] **Step 2: Replace the loading rendering section** Current (lines 179-213): ```tsx {isLoading && (
Тикер Название Количество Цена Стоимость
)} {!isLoading && positions.length > 0 && (
...
)} ``` Replace with new rendering logic: ```tsx {isLoading && (
Тикер Название Количество Цена Стоимость
)} {!isLoading && positions.length > 0 && (
{positions.map((position) => ( ))}
Тикер Название Количество Цена Стоимость
{position.name || '-'} {formatQuantity(position.quantity)} {formatMoney(position.currentPrice)} {formatMoney(position.currentValue)}
{isFetching && (
Загрузка страницы {pageNumber}…
)}
)} ``` - [ ] **Step 3: Update pagination buttons to show spinner during fetch** Replace the button content in lines 167-174 (the "→" button): ```tsx ``` Also update the "←" button (lines 148-155): ```tsx ``` - [ ] **Step 4: Commit** ```bash git add apps/frontend/src/pages/broker/BrokerPositionsSection.tsx git commit -m "feat: add loading overlay and spinner to PositionGroupTable" ``` --- ### Task 3: BrokerOperationsTable — new `isFetching` prop + overlay **Files:** - Modify: `apps/frontend/src/pages/broker/BrokerOperationsTable.tsx` - [ ] **Step 1: Add `isFetching` to props interface** Change the component props destructuring (line 96-113): ```tsx export function BrokerOperationsTable({ isLoading, isFetching, page, pageNumber, canGoBack, canGoForward, onPrevious, onNext, }: { isLoading: boolean; isFetching: boolean; page: BrokerOperationsPage | undefined; pageNumber: number; canGoBack: boolean; canGoForward: boolean; onPrevious: () => void; onNext: () => void; }) { ``` - [ ] **Step 2: Replace the loading/empty/data rendering** Current (lines 158-229): ```tsx {isLoading ? (
Дата Тип Инструмент Сумма
) : operations.length === 0 ? (

Операций за выбранный период нет

) : (
{operations.map((operation) => { const impact = getBrokerOperationImpact(operation); return ( ); })}
Дата Тип Инструмент Сумма
{formatDate(operation.date)} {getBrokerOperationTypeLabel(operation)} {formatMoney(operation.payment)}
)} ``` Replace with: ```tsx {isLoading ? (
Дата Тип Инструмент Сумма
) : operations.length === 0 && !isFetching ? (

Операций за выбранный период нет

) : (
{operations.map((operation) => { const impact = getBrokerOperationImpact(operation); return ( ); })}
Дата Тип Инструмент Сумма
{formatDate(operation.date)} {getBrokerOperationTypeLabel(operation)} {formatMoney(operation.payment)}
{isFetching && (
Загрузка страницы {pageNumber}…
)}
)} ``` Note: The empty state check changed from `operations.length === 0` to `operations.length === 0 && !isFetching` — this ensures the overlay shows on top of old data, not the empty message. - [ ] **Step 3: Update pagination buttons** Replace line 132-135 (← button): ```tsx ``` Replace lines 147-154 (→ button): ```tsx ``` - [ ] **Step 4: Commit** ```bash git add apps/frontend/src/pages/broker/BrokerOperationsTable.tsx git commit -m "feat: add loading overlay and spinner to BrokerOperationsTable" ``` --- ### Task 4: BrokerAccountDetailPage — pass `isFetching` to operations table **Files:** - Modify: `apps/frontend/src/pages/broker/BrokerAccountDetailPage.tsx` - [ ] **Step 1: Add `isFetching` to the BrokerOperationsTable props** Change the `` call (line 123-131): ```tsx 0} canGoForward={Boolean(operations.data?.hasNext && operations.data.nextCursor)} onPrevious={handlePreviousOperationsPage} onNext={handleNextOperationsPage} /> ``` - [ ] **Step 2: Commit** ```bash git add apps/frontend/src/pages/broker/BrokerAccountDetailPage.tsx git commit -m "feat: pass isFetching to BrokerOperationsTable" ``` --- ### Task 5: Update tests **Files:** - Modify: `apps/frontend/src/pages/broker/BrokerPages.test.tsx` - [ ] **Step 1: Add `isFetching: false` to all existing position mocks** In `mockUseBrokerPositions` (line 47-61), add `isFetching: false`: ```tsx return { data: { accountId: 'acc-1', items: filtered, nextCursor: null, hasNext: false, asOf: '2026-06-17T00:00:00.000Z', }, isLoading: false, isFetching: false, error: null, } as any; ``` - [ ] **Step 2: Add `isFetching: false` to all operations mocks** Add `isFetching: false` alongside each `isLoading: false` in the operations mocks (lines 58, 85, 112, 147, 190, 201, 266, 324, 363, 433). For example, line 112 area becomes: ```tsx data: { accountId: 'acc-1', items: [], nextCursor: null, hasNext: false, asOf: '2026-06-17T00:00:00.000Z' }, isLoading: false, isFetching: false, ``` - [ ] **Step 3: Verify tests pass** ```bash npx vitest run apps/frontend/src/pages/broker/BrokerPages.test.tsx -w apps/frontend ``` Expected: All tests PASS. - [ ] **Step 4: Commit** ```bash git add apps/frontend/src/pages/broker/BrokerPages.test.tsx git commit -m "test: add isFetching to mock return values" ``` --- ### Task 6: Lint and final verification - [ ] **Step 1: Run lint** ```bash npm run lint ``` Expected: No errors (or only pre-existing ones). - [ ] **Step 2: Run full frontend test suite** ```bash npm run test:frontend ``` Expected: All tests pass. - [ ] **Step 3: Run typecheck** ```bash npx tsc -b apps/frontend ``` Expected: No type errors.