From 28579321a92e2216d8d91776c76fad551be6af7f Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Wed, 17 Jun 2026 14:46:48 +0300 Subject: [PATCH] feat(frontend): add positions hook and skeleton loading to account detail page --- .../pages/broker/BrokerAccountDetailPage.tsx | 149 +++++++++++++++++- 1 file changed, 144 insertions(+), 5 deletions(-) diff --git a/apps/frontend/src/pages/broker/BrokerAccountDetailPage.tsx b/apps/frontend/src/pages/broker/BrokerAccountDetailPage.tsx index ae2e562..f98b871 100644 --- a/apps/frontend/src/pages/broker/BrokerAccountDetailPage.tsx +++ b/apps/frontend/src/pages/broker/BrokerAccountDetailPage.tsx @@ -3,12 +3,13 @@ import { useParams } from 'react-router-dom'; import type { BrokerMoney } from '../../api/responses'; import { useBrokerOperations } from '../../hooks/useBrokerOperations'; import { useBrokerPortfolio } from '../../hooks/useBrokerPortfolio'; +import { useBrokerPositions } from '../../hooks/useBrokerPositions'; import { BrokerOperationsTable } from './BrokerOperationsTable'; import { BrokerPositionsSection } from './BrokerPositionsSection'; +import { SkeletonBlock } from '../../components/SkeletonBlock'; function formatMoney(value: BrokerMoney | null | undefined) { if (!value) return '-'; - return new Intl.NumberFormat('ru-RU', { style: 'currency', currency: value.currency || 'RUB', @@ -20,10 +21,127 @@ export function BrokerAccountDetailPage() { const { accountId } = useParams(); const [operationCursor, setOperationCursor] = useState(undefined); const [operationCursorStack, setOperationCursorStack] = useState>([]); + const [positionCursor, setPositionCursor] = useState(undefined); + const [positionCursorStack, setPositionCursorStack] = useState>([]); const portfolio = useBrokerPortfolio(accountId); const operations = useBrokerOperations(accountId, { limit: 10, cursor: operationCursor }); + const positions = useBrokerPositions(accountId, { limit: 10, cursor: positionCursor }); + + if (portfolio.isLoading) { + return ( +
+
+ + +
+
+ {[1, 2, 3].map((i) => ( +
+ +
+ +
+ ))} +
+
+ + + + + + + + + + + + {Array.from({ length: 4 }).map((_, i) => ( + + {Array.from({ length: 5 }).map((_, j) => ( + + ))} + + ))} + +
+ Тикер + + Название + + Количество + + Цена + + Стоимость +
+ +
+
+
+ ); + } - if (portfolio.isLoading) return

Загрузка портфеля...

; if (portfolio.error || !portfolio.data) { return

Не удалось загрузить портфель

; } @@ -31,20 +149,33 @@ export function BrokerAccountDetailPage() { function handleNextOperationsPage() { const nextCursor = operations.data?.nextCursor; if (!nextCursor || !operations.data?.hasNext) return; - setOperationCursorStack((previous) => [...previous, operationCursor]); setOperationCursor(nextCursor); } function handlePreviousOperationsPage() { if (operationCursorStack.length === 0) return; - const nextStack = operationCursorStack.slice(0, -1); const previousCursor = operationCursorStack[operationCursorStack.length - 1]; setOperationCursorStack(nextStack); setOperationCursor(previousCursor); } + function handleNextPositionsPage() { + const nextCursor = positions.data?.nextCursor; + if (!nextCursor || !positions.data?.hasNext) return; + setPositionCursorStack((previous) => [...previous, positionCursor]); + setPositionCursor(nextCursor); + } + + function handlePreviousPositionsPage() { + if (positionCursorStack.length === 0) return; + const nextStack = positionCursorStack.slice(0, -1); + const previousCursor = positionCursorStack[positionCursorStack.length - 1]; + setPositionCursorStack(nextStack); + setPositionCursor(previousCursor); + } + return (
@@ -87,7 +218,15 @@ export function BrokerAccountDetailPage() { ))} - + 0} + canGoForward={Boolean(positions.data?.hasNext && positions.data.nextCursor)} + onPrevious={handlePreviousPositionsPage} + onNext={handleNextPositionsPage} + />