feat: migrate BrokerSummary, BrokerAssetCards and BrokerAccountOverviewPage
This commit is contained in:
parent
3682f43aa6
commit
ef97fd7136
@ -1,4 +1,6 @@
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Box } from '@mui/material';
|
||||||
|
import { Text } from '@moex-vibe/design-system';
|
||||||
import { useBrokerOperations } from '@/entities/broker-operation';
|
import { useBrokerOperations } from '@/entities/broker-operation';
|
||||||
import { useBrokerAccountContext } from '@/widgets/broker-account-layout';
|
import { useBrokerAccountContext } from '@/widgets/broker-account-layout';
|
||||||
import { BrokerAllocationChart } from '@/widgets/broker-allocation-chart';
|
import { BrokerAllocationChart } from '@/widgets/broker-allocation-chart';
|
||||||
@ -11,16 +13,22 @@ export function BrokerAccountOverviewPage() {
|
|||||||
|
|
||||||
if (portfolio.isLoading) return <BrokerOverviewSkeleton />;
|
if (portfolio.isLoading) return <BrokerOverviewSkeleton />;
|
||||||
if (portfolio.error || !portfolio.data) {
|
if (portfolio.error || !portfolio.data) {
|
||||||
return <p role="alert">Не удалось загрузить сводку счёта</p>;
|
return (
|
||||||
|
<Text component="p" role="alert" tone="negative">
|
||||||
|
Не удалось загрузить сводку счёта
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="broker-overview">
|
<Box component="div" sx={{ display: 'grid', gap: 3 }}>
|
||||||
<BrokerSummary portfolio={portfolio.data} />
|
<BrokerSummary portfolio={portfolio.data} />
|
||||||
<BrokerAllocationChart portfolio={portfolio.data} />
|
<BrokerAllocationChart portfolio={portfolio.data} />
|
||||||
<BrokerAssetCards accountId={accountId} portfolio={portfolio.data} />
|
<BrokerAssetCards accountId={accountId} portfolio={portfolio.data} />
|
||||||
{operations.error ? (
|
{operations.error ? (
|
||||||
<p role="alert">Не удалось загрузить последние операции</p>
|
<Text component="p" role="alert" tone="negative">
|
||||||
|
Не удалось загрузить последние операции
|
||||||
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<BrokerOperationsTable
|
<BrokerOperationsTable
|
||||||
title="Последние операции"
|
title="Последние операции"
|
||||||
@ -33,6 +41,6 @@ export function BrokerAccountOverviewPage() {
|
|||||||
page={operations.data}
|
page={operations.data}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Box } from '@mui/material';
|
||||||
|
import { Text } from '@moex-vibe/design-system';
|
||||||
import type { BrokerMoney, BrokerPortfolio } from '@/shared/api/responses';
|
import type { BrokerMoney, BrokerPortfolio } from '@/shared/api/responses';
|
||||||
import { formatBrokerMoney, pluralize } from '@/shared/lib/formatters';
|
import { formatBrokerMoney, pluralize } from '@/shared/lib/formatters';
|
||||||
|
|
||||||
@ -11,6 +13,16 @@ function formatAllocationPercent(value: number | null) {
|
|||||||
return value === null ? '\u2014' : `${value.toFixed(1)}%`;
|
return value === null ? '\u2014' : `${value.toFixed(1)}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cardSx = {
|
||||||
|
p: 2,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: 'divider',
|
||||||
|
borderRadius: 2,
|
||||||
|
bgcolor: 'surface.default',
|
||||||
|
display: 'grid',
|
||||||
|
gap: 1,
|
||||||
|
};
|
||||||
|
|
||||||
export function BrokerAssetCards({
|
export function BrokerAssetCards({
|
||||||
accountId,
|
accountId,
|
||||||
portfolio,
|
portfolio,
|
||||||
@ -37,23 +49,23 @@ export function BrokerAssetCards({
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="broker-overview__assets" aria-label="Основные классы активов">
|
<Box component="section" sx={{ display: 'grid', gap: 2 }} aria-label="Основные классы активов">
|
||||||
{cards.map((card) => (
|
{cards.map((card) => (
|
||||||
<Link
|
<Link key={card.label} to={card.path}>
|
||||||
className="broker-overview__card broker-overview__asset-link"
|
<Box sx={{ ...cardSx, color: 'inherit', textDecoration: 'none' }}>
|
||||||
key={card.label}
|
<Box component="span" sx={{ color: 'primary.main', fontSize: 18, fontWeight: 700 }}>
|
||||||
to={card.path}
|
{card.label}
|
||||||
>
|
</Box>
|
||||||
<strong className="broker-overview__asset-title">{card.label}</strong>
|
<Text>
|
||||||
<span>
|
{card.count} {card.countLabel}
|
||||||
{card.count} {card.countLabel}
|
</Text>
|
||||||
</span>
|
<Text>{formatBrokerMoney(card.value)}</Text>
|
||||||
<span>{formatBrokerMoney(card.value)}</span>
|
<Text>
|
||||||
<span>
|
{formatAllocationPercent(allocationPercent(card.value, portfolio.totals.portfolio))}
|
||||||
{formatAllocationPercent(allocationPercent(card.value, portfolio.totals.portfolio))}
|
</Text>
|
||||||
</span>
|
</Box>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</section>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { Box } from '@mui/material';
|
||||||
|
import { Text } from '@moex-vibe/design-system';
|
||||||
import type { BrokerPortfolio } from '@/shared/api/responses';
|
import type { BrokerPortfolio } from '@/shared/api/responses';
|
||||||
import {
|
import {
|
||||||
formatBrokerMoney as formatMoney,
|
formatBrokerMoney as formatMoney,
|
||||||
@ -6,31 +8,61 @@ import {
|
|||||||
|
|
||||||
export function BrokerSummary({ portfolio }: { portfolio: BrokerPortfolio }) {
|
export function BrokerSummary({ portfolio }: { portfolio: BrokerPortfolio }) {
|
||||||
return (
|
return (
|
||||||
<section className="broker-overview__summary" aria-label="Сводка счёта">
|
<Box component="section" sx={{ display: 'grid', gap: 2 }} aria-label="Сводка счёта">
|
||||||
<div className="broker-overview__card">
|
<Box
|
||||||
<span className="broker-overview__label">Стоимость портфеля</span>
|
sx={{
|
||||||
<strong className="broker-overview__total">
|
p: 2,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: 'divider',
|
||||||
|
borderRadius: 2,
|
||||||
|
bgcolor: 'surface.default',
|
||||||
|
display: 'grid',
|
||||||
|
gap: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text variant="label" tone="secondary">
|
||||||
|
Стоимость портфеля
|
||||||
|
</Text>
|
||||||
|
<Box component="span" sx={{ fontWeight: 700, fontSize: 24 }}>
|
||||||
{formatMoney(portfolio.totals.portfolio)}
|
{formatMoney(portfolio.totals.portfolio)}
|
||||||
</strong>
|
</Box>
|
||||||
<span>За день: {formatMoney(portfolio.yields.daily)}</span>
|
<Text>За день: {formatMoney(portfolio.yields.daily)}</Text>
|
||||||
<span>Дневная доходность: {formatPercent(portfolio.yields.dailyPercent)}</span>
|
<Text>Дневная доходность: {formatPercent(portfolio.yields.dailyPercent)}</Text>
|
||||||
<span>Ожидаемая доходность: {formatPercent(portfolio.yields.expectedPercent)}</span>
|
<Text>Ожидаемая доходность: {formatPercent(portfolio.yields.expectedPercent)}</Text>
|
||||||
</div>
|
</Box>
|
||||||
<div className="broker-overview__card">
|
<Box
|
||||||
<span className="broker-overview__label">Денежный остаток</span>
|
sx={{
|
||||||
|
p: 2,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: 'divider',
|
||||||
|
borderRadius: 2,
|
||||||
|
bgcolor: 'surface.default',
|
||||||
|
display: 'grid',
|
||||||
|
gap: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text variant="label" tone="secondary">
|
||||||
|
Денежный остаток
|
||||||
|
</Text>
|
||||||
{portfolio.cash.length === 0 ? (
|
{portfolio.cash.length === 0 ? (
|
||||||
<span>Нет денежных остатков</span>
|
<Text>Нет денежных остатков</Text>
|
||||||
) : (
|
) : (
|
||||||
<ul className="broker-overview__cash">
|
<Box component="ul" sx={{ listStyle: 'none', display: 'grid', gap: 1 }}>
|
||||||
{portfolio.cash.map((money, index) => (
|
{portfolio.cash.map((money, index) => (
|
||||||
<li key={`${money.currency}-${index}`}>
|
<Box
|
||||||
<span>{money.currency}</span>
|
component="li"
|
||||||
<strong>{formatMoney(money)}</strong>
|
key={`${money.currency}-${index}`}
|
||||||
</li>
|
sx={{ display: 'flex', justifyContent: 'space-between', gap: 1.5 }}
|
||||||
|
>
|
||||||
|
<Text>{money.currency}</Text>
|
||||||
|
<Box component="span" sx={{ fontWeight: 700 }}>
|
||||||
|
{formatMoney(money)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Box>
|
||||||
</section>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user