diff --git a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx
index 37d5cf0..06c4755 100644
--- a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx
+++ b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx
@@ -22,11 +22,11 @@ vi.mock('@/entities/broker-analytics', () => ({
useBrokerAnalytics: () => ({
data: {
totalDeposits: 1000,
- totalWithdrawn: 100,
- netInvested: 900,
- totalDividends: 25,
- totalCoupons: 15,
- totalReceived: 40,
+ totalWithdrawn: 250,
+ netInvested: -150,
+ totalDividends: 75,
+ totalCoupons: 0,
+ totalReceived: 90,
totalReturnPercent: 4.44,
currency: 'RUB',
},
@@ -252,6 +252,44 @@ describe('BrokerDashboard', () => {
expect(heroText).not.toContain('RUB')
})
+ it('renders analytics card with the ₽ symbol and no "RUB" code', () => {
+ renderWithProviders()
+
+ const analytics = screen.getByLabelText('Аналитика доходности')
+ const analyticsText = analytics.textContent ?? ''
+ expect(analyticsText).toContain('₽')
+ expect(analyticsText).not.toContain('RUB')
+ })
+
+ it('applies positive, negative and neutral tones to analytics values', () => {
+ renderWithProviders()
+
+ const deposits = screen.getByTestId('dashboard-analytics-totalDeposits')
+ expect(deposits.getAttribute('data-tone')).toBe('positive')
+ expect(deposits.textContent).toContain('1\u00a0000,00')
+ expect(deposits.textContent).toContain('₽')
+
+ const withdrawn = screen.getByTestId('dashboard-analytics-totalWithdrawn')
+ expect(withdrawn.getAttribute('data-tone')).toBe('negative')
+ expect(withdrawn.textContent).toMatch(/^[−-]250,00/)
+
+ const net = screen.getByTestId('dashboard-analytics-netInvested')
+ expect(net.getAttribute('data-tone')).toBe('negative')
+ expect(net.textContent).toMatch(/^[−-]150,00/)
+
+ const dividends = screen.getByTestId('dashboard-analytics-totalDividends')
+ expect(dividends.getAttribute('data-tone')).toBe('positive')
+ expect(dividends.textContent).toContain('75,00')
+
+ const coupons = screen.getByTestId('dashboard-analytics-totalCoupons')
+ expect(coupons.getAttribute('data-tone')).toBe('neutral')
+ expect(coupons.textContent).toContain('0,00')
+
+ const received = screen.getByTestId('dashboard-analytics-totalReceived')
+ expect(received.getAttribute('data-tone')).toBe('positive')
+ expect(received.textContent).toContain('90,00')
+ })
+
it('shows skeleton table while events are loading', () => {
hookMocks.useBrokerEvents.mockReturnValue({
data: undefined,
diff --git a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx
index 3ace3ad..28dd785 100644
--- a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx
+++ b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx
@@ -1,10 +1,33 @@
-import { Metric, Text } from '@moex-vibe/design-system'
+import { Metric } from '@moex-vibe/design-system'
import { Box } from '@mui/material'
import type { BrokerAnalytics } from '@/shared/api'
+import {
+ formatDashboardCurrency,
+ type MoneyTone,
+ moneyTone,
+ moneyToneToColor,
+} from '../lib/dashboardVisual'
import { BrokerDashboardCard } from './BrokerDashboardCard'
-function amount(value: number, currency: string): string {
- return `${value.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ${currency}`
+type AnalyticsField =
+ | 'totalDeposits'
+ | 'totalWithdrawn'
+ | 'netInvested'
+ | 'totalDividends'
+ | 'totalCoupons'
+ | 'totalReceived'
+
+function analyticsTone(field: AnalyticsField, value: number): MoneyTone {
+ if (field === 'totalWithdrawn') {
+ return value > 0 ? 'negative' : moneyTone(value)
+ }
+ return moneyTone(value)
+}
+
+function analyticsDisplay(field: AnalyticsField, value: number, currency: string): string {
+ const formatted = formatDashboardCurrency({ currency, value })
+ if (field === 'totalWithdrawn' && value > 0) return `−${formatted}`
+ return formatted
}
export function BrokerDashboardAnalyticsCard({
@@ -17,13 +40,13 @@ export function BrokerDashboardAnalyticsCard({
isError: boolean
}) {
return (
-
+
{isError ? (
- Не удалось загрузить аналитику
+
) : isLoading ? (
- Загрузка аналитики…
+
) : !data ? (
- Нет данных для аналитики
+
) : (
-
-
-
-
-
-
+
+ {analyticsDisplay('totalDeposits', data.totalDeposits, data.currency)}
+
+ }
+ />
+
+ {analyticsDisplay('totalWithdrawn', data.totalWithdrawn, data.currency)}
+
+ }
+ />
+
+ {analyticsDisplay('netInvested', data.netInvested, data.currency)}
+
+ }
+ />
+
+ {analyticsDisplay('totalDividends', data.totalDividends, data.currency)}
+
+ }
+ />
+
+ {analyticsDisplay('totalCoupons', data.totalCoupons, data.currency)}
+
+ }
+ />
+
+ {analyticsDisplay('totalReceived', data.totalReceived, data.currency)}
+
+ }
+ />
)}