codex/broker-account-analytics #41
1
apps/frontend/src/pages/broker-analytics/index.ts
Normal file
1
apps/frontend/src/pages/broker-analytics/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { BrokerAnalyticsPage } from './ui/BrokerAnalyticsPage'
|
||||
@ -0,0 +1,162 @@
|
||||
import { useBrokerAnalytics } from '@/entities/broker-analytics'
|
||||
import { useBrokerAccountContext } from '@/widgets/broker-account-layout'
|
||||
|
||||
function formatAmount(value: number, currency: string): string {
|
||||
return `${value.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ${currency}`
|
||||
}
|
||||
|
||||
export function BrokerAnalyticsPage() {
|
||||
const { accountId } = useBrokerAccountContext()
|
||||
const { data, isLoading, isError } = useBrokerAnalytics(accountId)
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<section>
|
||||
<div style={{ padding: '24px', color: 'var(--color-text-secondary)' }}>Загрузка...</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<section>
|
||||
<div style={{ padding: '24px', color: 'var(--color-text-negative)' }}>
|
||||
Не удалось загрузить аналитику
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
if (!data || (data.totalDeposits === 0 && data.totalReceived === 0)) {
|
||||
return (
|
||||
<section>
|
||||
<div style={{ padding: '24px', color: 'var(--color-text-secondary)' }}>
|
||||
Нет данных для аналитики
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
gap: '16px',
|
||||
marginBottom: '24px',
|
||||
}}
|
||||
>
|
||||
{/* Вложено */}
|
||||
<div
|
||||
style={{
|
||||
background: 'var(--color-surface-card)',
|
||||
borderRadius: '12px',
|
||||
padding: '20px',
|
||||
}}
|
||||
>
|
||||
<h3 style={{ margin: '0 0 16px', fontSize: '16px', fontWeight: 600 }}>Вложено</h3>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<Row label="Пополнения" value={formatAmount(data.totalDeposits, data.currency)} />
|
||||
<Row
|
||||
label="Выводы"
|
||||
value={`−${formatAmount(data.totalWithdrawn, data.currency)}`}
|
||||
negative
|
||||
/>
|
||||
<Divider />
|
||||
<Row label="Нетто" value={formatAmount(data.netInvested, data.currency)} bold />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Получено */}
|
||||
<div
|
||||
style={{
|
||||
background: 'var(--color-surface-card)',
|
||||
borderRadius: '12px',
|
||||
padding: '20px',
|
||||
}}
|
||||
>
|
||||
<h3 style={{ margin: '0 0 16px', fontSize: '16px', fontWeight: 600 }}>Получено</h3>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<Row label="Дивиденды" value={formatAmount(data.totalDividends, data.currency)} />
|
||||
<Row label="Купоны" value={formatAmount(data.totalCoupons, data.currency)} />
|
||||
<Divider />
|
||||
<Row label="Итого" value={formatAmount(data.totalReceived, data.currency)} bold />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Сводка */}
|
||||
<div
|
||||
style={{
|
||||
background: 'var(--color-surface-card)',
|
||||
borderRadius: '12px',
|
||||
padding: '20px',
|
||||
}}
|
||||
>
|
||||
<h3 style={{ margin: '0 0 16px', fontSize: '16px', fontWeight: 600 }}>Сводка</h3>
|
||||
<div style={{ display: 'flex', gap: '48px' }}>
|
||||
<SummaryItem
|
||||
label="Вложено нетто"
|
||||
value={formatAmount(data.netInvested, data.currency)}
|
||||
/>
|
||||
<SummaryItem label="Получено" value={formatAmount(data.totalReceived, data.currency)} />
|
||||
{data.totalReturnPercent !== null && (
|
||||
<SummaryItem label="Доходность" value={`${data.totalReturnPercent.toFixed(2)}%`} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function Row({
|
||||
label,
|
||||
value,
|
||||
bold,
|
||||
negative,
|
||||
}: {
|
||||
label: string
|
||||
value: string
|
||||
bold?: boolean
|
||||
negative?: boolean
|
||||
}) {
|
||||
const color = negative ? 'var(--color-text-negative, #ef4444)' : 'var(--color-text-primary)'
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<span style={{ color: 'var(--color-text-secondary)', fontSize: '14px' }}>{label}</span>
|
||||
<span style={{ fontWeight: bold ? 600 : 400, color, fontSize: '15px' }}>{value}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Divider() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: '1px',
|
||||
background: 'var(--color-border)',
|
||||
margin: '4px 0',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SummaryItem({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ color: 'var(--color-text-secondary)', fontSize: '13px', marginBottom: '4px' }}>
|
||||
{label}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 600,
|
||||
color: 'var(--color-text-primary)',
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user