import { Link } from 'react-router-dom'; import type { BrokerMoney, BrokerPortfolio } from '../../api/responses'; import { SkeletonBlock } from '../../components/SkeletonBlock'; import { useBrokerOperations } from '../../hooks/useBrokerOperations'; import { useBrokerAccountContext } from './BrokerAccountLayout'; import { BrokerAllocationChart } from './BrokerAllocationChart'; import { BrokerOperationsTable } from './BrokerOperationsTable'; function formatMoney(value: BrokerMoney | null | undefined) { if (!value) return '-'; return new Intl.NumberFormat('ru-RU', { style: 'currency', currency: value.currency || 'RUB', maximumFractionDigits: 2, }).format(value.value); } function formatPercent(value: number | null) { if (value === null) return '-'; return `${new Intl.NumberFormat('ru-RU', { maximumFractionDigits: 2 }).format(value)}%`; } function pluralize(count: number, one: string, few: string, many: string) { const modulo100 = Math.abs(count) % 100; const modulo10 = modulo100 % 10; if (modulo100 > 10 && modulo100 < 20) return many; if (modulo10 === 1) return one; if (modulo10 >= 2 && modulo10 <= 4) return few; return many; } function BrokerSummary({ portfolio }: { portfolio: BrokerPortfolio }) { return (
Стоимость портфеля {formatMoney(portfolio.totals.portfolio)} За день: {formatMoney(portfolio.yields.daily)} Дневная доходность: {formatPercent(portfolio.yields.dailyPercent)} Ожидаемая доходность: {formatPercent(portfolio.yields.expectedPercent)}
Денежный остаток {portfolio.cash.length === 0 ? ( Нет денежных остатков ) : ( )}
); } function allocationPercent(value: BrokerMoney | null, total: BrokerMoney | null) { if (!value || !total || total.value <= 0) return 0; return (value.value / total.value) * 100; } function BrokerAssetCards({ accountId, portfolio, }: { accountId: string; portfolio: BrokerPortfolio; }) { const basePath = `/broker/${encodeURIComponent(accountId)}`; const cards = [ { label: 'Акции', count: portfolio.positionCounts.shares, countLabel: pluralize(portfolio.positionCounts.shares, 'позиция', 'позиции', 'позиций'), value: portfolio.totals.shares, path: `${basePath}/shares`, }, { label: 'Облигации', count: portfolio.positionCounts.bonds, countLabel: pluralize(portfolio.positionCounts.bonds, 'выпуск', 'выпуска', 'выпусков'), value: portfolio.totals.bonds, path: `${basePath}/bonds`, }, ]; return (
{cards.map((card) => ( {card.label} {card.count} {card.countLabel} {formatMoney(card.value)} {allocationPercent(card.value, portfolio.totals.portfolio).toFixed(1)}% ))}
); } function BrokerOverviewSkeleton() { return (
{[1, 2].map((item) => (
))}
{[1, 2].map((item) => (
))}
); } export function BrokerAccountOverviewPage() { const { accountId, portfolio } = useBrokerAccountContext(); const operations = useBrokerOperations(accountId, { limit: 5 }); if (portfolio.isLoading) return ; if (portfolio.error || !portfolio.data) { return

Не удалось загрузить сводку счёта

; } return (
{operations.error ? (

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

) : ( Вся история } emptyMessage="Операций с начала текущего года нет" isLoading={operations.isLoading} isFetching={operations.isFetching} page={operations.data} /> )}
); }