- Split single p-queue (5 req/s) into 3 isolated queues: operations (5/s), instruments (20/s), users (5/s) - Removed dead instruments param from mapBrokerPortfolio - portfolio/positions endpoints share raw GetPortfolio cache - Docs: T_BANK_INSTRUMENTS_RATE_LIMIT, CACHE_TBANK_POSITIONS_TTL, rate limiting section in tbank-invest.md
8.6 KiB
Broker Operations UI Improvements — 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: Clean up operations table badges, add "+" for positive amounts, smooth pagination with styled buttons.
Architecture: All changes are frontend-only (React components, TanStack Query hook, display helpers, tests). No backend changes.
Tech Stack: React 18, TanStack Query v5, Vitest + Testing Library
Task 1: Remove getBrokerOperationImpactLabel from brokerDisplay
Files:
-
Modify:
apps/frontend/src/pages/broker/brokerDisplay.ts:165-176 -
Modify:
apps/frontend/src/pages/broker/brokerDisplay.test.ts:195-201 -
Step 1: Remove the test for impact labels
In brokerDisplay.test.ts, delete the test block "provides Russian impact labels" (lines 195-201) and remove getBrokerOperationImpactLabel from the import.
// Import line changes from:
import {
getBrokerInstrumentPath,
getBrokerOperationImpact,
getBrokerOperationImpactLabel,
getBrokerOperationTypeLabel,
getBrokerPositionGroup,
} from './brokerDisplay';
// to:
import {
getBrokerInstrumentPath,
getBrokerOperationImpact,
getBrokerOperationTypeLabel,
getBrokerPositionGroup,
} from './brokerDisplay';
Delete the entire it('provides Russian impact labels', ...) block (lines 195-201).
- Step 2: Run tests to verify the test removal succeeds
Run: npx vitest run apps/frontend/src/pages/broker/brokerDisplay.test.ts
Expected: PASS (1 less test)
- Step 3: Remove
getBrokerOperationImpactLabelfrom source
In brokerDisplay.ts, delete the getBrokerOperationImpactLabel function (lines 165-176) and remove its export.
- Step 4: Run tests to verify
Run: npx vitest run apps/frontend/src/pages/broker/brokerDisplay.test.ts
Expected: PASS
- Step 5: Commit
git add apps/frontend/src/pages/broker/brokerDisplay.ts apps/frontend/src/pages/broker/brokerDisplay.test.ts
git commit -m "refactor: remove unused getBrokerOperationImpactLabel helper"
Task 2: Update BrokerPages.test.tsx for new expectations
Files:
-
Modify:
apps/frontend/src/pages/broker/BrokerPages.test.tsx -
Step 1: Update test to expect no badges and "+" prefix
Remove lines 314-315 (badge checks):
expect(screen.getByText('Пополняет')).toBeInTheDocument();
expect(screen.getByText('Списывает')).toBeInTheDocument();
Update test name at line 225 from:
it('renders broker operations with Russian labels, linked instruments and impact badges', () => {
to:
it('renders broker operations with Russian labels, linked instruments and colored amounts', () => {
Add a check for the "+" prefix on positive amounts after the existing check at line 312-313:
expect(screen.getByText('Выплата купона')).toBeInTheDocument();
expect(screen.getByText('Налог')).toBeInTheDocument();
// Add:
expect(screen.getByText(/\+120,00\s*₽/)).toBeInTheDocument();
Remove the old checks for "Страница 1" and "Страница 2" text (lines 424-437) and instead verify the pagination buttons exist. Update the pagination test block at line 322:
it('requests broker operations by cursor with a page size of 10', async () => {
// ...setup stays the same...
// Replace these:
// expect(screen.getByText('Страница 1')).toBeInTheDocument();
// with check that page buttons exist:
const nextButton = screen.getByRole('button', { name: '→' });
const prevButton = screen.getByRole('button', { name: '←' });
expect(prevButton).toBeDisabled();
expect(nextButton).not.toBeDisabled();
await user.click(nextButton);
expect(operationsSpy).toHaveBeenLastCalledWith('acc-1', {
limit: 10,
cursor: 'cursor-page-2',
});
// expect(screen.getByText('Страница 2')).toBeInTheDocument(); // remove
expect(screen.getByText('2')).toBeInTheDocument(); // page number shown without label
await user.click(screen.getByRole('button', { name: '←' }));
expect(operationsSpy).toHaveBeenLastCalledWith('acc-1', { limit: 10, cursor: undefined });
expect(screen.getByText('1')).toBeInTheDocument();
});
- Step 2: Run tests to verify they fail
Run: npx vitest run apps/frontend/src/pages/broker/BrokerPages.test.tsx
Expected: FAIL (buttons named '→' / '←' not found yet, "+" text not found)
- Step 3: Commit
git add apps/frontend/src/pages/broker/BrokerPages.test.tsx
git commit -m "test: update broker page tests for new UI expectations"
Task 3: Remove badges and add "+" prefix, style pagination buttons
Files:
-
Modify:
apps/frontend/src/pages/broker/BrokerOperationsTable.tsx -
Step 1: Remove badge-related code
In BrokerOperationsTable.tsx:
Delete the impactStyles object (lines 30-47).
Delete the OperationType component (lines 87-108).
Update imports — remove getBrokerOperationImpactLabel and BrokerOperationImpact:
import {
getBrokerInstrumentPath,
getBrokerOperationImpact,
getBrokerOperationTypeLabel,
} from './brokerDisplay';
Update the "Тип" column cell — replace <OperationType operation={operation} /> with just:
<span>{getBrokerOperationTypeLabel(operation)}</span>
- Step 2: Add "+" prefix to positive amounts
Modify formatMoney function:
function formatMoney(value: BrokerMoney | null | undefined) {
if (!value) return '-';
const formatted = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: value.currency || 'RUB',
maximumFractionDigits: 2,
}).format(value.value);
return value.value > 0 ? `+${formatted}` : formatted;
}
- Step 3: Style pagination buttons
Add button style constants before the component:
const pagButtonStyle: React.CSSProperties = {
padding: '6px 14px',
borderRadius: 6,
border: '1px solid #e0e0e0',
background: 'var(--color-surface)',
color: 'var(--color-text)',
fontSize: 14,
fontWeight: 600,
cursor: 'pointer',
lineHeight: 1.4,
};
const pagButtonDisabledStyle: React.CSSProperties = {
...pagButtonStyle,
opacity: 0.35,
cursor: 'not-allowed',
};
Update pagination controls — replace existing "Назад" / "Вперед" buttons and "Страница N" text:
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<button
type="button"
onClick={onPrevious}
disabled={!canGoBack}
style={canGoBack ? pagButtonStyle : pagButtonDisabledStyle}
>
←
</button>
<span
style={{
minWidth: 20,
textAlign: 'center',
color: 'var(--color-text-secondary)',
fontSize: 14,
fontWeight: 600,
}}
>
{pageNumber}
</span>
<button
type="button"
onClick={onNext}
disabled={!canGoForward}
style={canGoForward ? pagButtonStyle : pagButtonDisabledStyle}
>
→
</button>
</div>
- Step 4: Run tests
Run: npx vitest run apps/frontend/src/pages/broker/BrokerPages.test.tsx
Expected: PASS
- Step 5: Run full test suite
Run: npm run test:frontend
Expected: PASS
- Step 6: Commit
git add apps/frontend/src/pages/broker/BrokerOperationsTable.tsx
git commit -m "feat: remove impact badges, add + prefix, style pagination buttons"
Task 4: Add keepPreviousData to operations query
Files:
-
Modify:
apps/frontend/src/hooks/useBrokerOperations.ts -
Step 1: Add keepPreviousData
Update useBrokerOperations.ts:
import { keepPreviousData, useQuery } from '@tanstack/react-query';
export function useBrokerOperations(
accountId: string | undefined,
query: BrokerOperationQuery = {},
) {
return useQuery<BrokerOperationsPage>({
queryKey: ['broker', 'operations', accountId, query],
enabled: Boolean(accountId),
queryFn: async () => (await getBrokerOperations(accountId!, query)).data,
placeholderData: keepPreviousData,
staleTime: 300_000,
retry: 2,
refetchOnWindowFocus: false,
});
}
- Step 2: Run full test suite
Run: npm run test:frontend
Expected: PASS
- Step 3: Commit
git add apps/frontend/src/hooks/useBrokerOperations.ts
git commit -m "feat: add keepPreviousData for smooth pagination"
Task 5: Run lint and verify
- Step 1: Run lint
Run: npm run lint
Expected: PASS (no lint errors)
- Step 2: Run full test suite
Run: npm run test:frontend
Expected: PASS
- Step 3: Verify build
Run: npm run build:frontend
Expected: PASS
- Step 4: Final commit if any fixes
git commit -m "chore: fix lint issues"