From e0c9d97dedce47a91da3127823ff444ac89555f8 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Wed, 17 Jun 2026 14:45:27 +0300 Subject: [PATCH] feat(frontend): add pagination and skeleton to BrokerPositionsSection --- .../pages/broker/BrokerPositionsSection.tsx | 182 ++++++++++++++++-- 1 file changed, 166 insertions(+), 16 deletions(-) diff --git a/apps/frontend/src/pages/broker/BrokerPositionsSection.tsx b/apps/frontend/src/pages/broker/BrokerPositionsSection.tsx index 5c58ce5..095b364 100644 --- a/apps/frontend/src/pages/broker/BrokerPositionsSection.tsx +++ b/apps/frontend/src/pages/broker/BrokerPositionsSection.tsx @@ -1,6 +1,7 @@ import { Link } from 'react-router-dom'; import type { BrokerMoney, BrokerPosition } from '../../api/responses'; import { getBrokerInstrumentPath, getBrokerPositionGroup } from './brokerDisplay'; +import { TableSkeleton } from '../../components/TableSkeleton'; type BrokerPositionGroupConfig = { key: 'shares' | 'bonds' | 'other'; @@ -32,9 +33,26 @@ const tdStyle = { verticalAlign: 'top', } satisfies React.CSSProperties; +const pagButtonStyle = { + 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, +} satisfies React.CSSProperties; + +const pagButtonDisabledStyle = { + ...pagButtonStyle, + opacity: 0.35, + cursor: 'not-allowed', +} satisfies React.CSSProperties; + function formatMoney(value: BrokerMoney | null | undefined) { if (!value) return '-'; - return new Intl.NumberFormat('ru-RU', { style: 'currency', currency: value.currency || 'RUB', @@ -123,29 +141,161 @@ function PositionTable({ title, positions }: { title: string; positions: BrokerP ); } -export function BrokerPositionsSection({ positions }: { positions: BrokerPosition[] }) { +type BrokerPositionsSectionProps = { + page: { items: BrokerPosition[] } | undefined; + isLoading: boolean; + pageNumber: number; + canGoBack: boolean; + canGoForward: boolean; + onPrevious: () => void; + onNext: () => void; +}; + +export function BrokerPositionsSection({ + page, + isLoading, + pageNumber, + canGoBack, + canGoForward, + onPrevious, + onNext, +}: BrokerPositionsSectionProps) { + const positions = page?.items ?? []; + const grouped = GROUPS.map((group) => ({ ...group, positions: positions.filter((position) => getBrokerPositionGroup(position) === group.key), })).filter((group) => group.positions.length > 0); - if (grouped.length === 0) { - return ( -
-

Позиции

-

В портфеле нет позиций

-
- ); - } - return (
-

Позиции

-
- {grouped.map((group) => ( - - ))} +
+

Позиции

+
+ + + {pageNumber} + + +
+ + {isLoading && grouped.length === 0 ? ( +
+ + + + + + + + + + + +
+ Тикер + + Название + + Количество + + Цена + + Стоимость +
+
+ ) : grouped.length === 0 ? ( +

В портфеле нет позиций

+ ) : isLoading ? ( +
+
+ {grouped.map((group) => ( +
+

{group.title}

+
+ + + + + + + + + + + +
+ Тикер + + Название + + Количество + + Цена + + Стоимость +
+
+
+ ))} +
+
+ ) : ( +
+ {grouped.map((group) => ( + + ))} +
+ )}
); }