codex/broker-dashboard-redesign #49
@ -69,9 +69,3 @@ export function incomeTypesToOperationTypes(types: DashboardIncomeType[]): strin
|
|||||||
if (types.includes('coupon')) operationTypes.add('OPERATION_TYPE_COUPON')
|
if (types.includes('coupon')) operationTypes.add('OPERATION_TYPE_COUPON')
|
||||||
return [...operationTypes].join(',')
|
return [...operationTypes].join(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetDashboardFilters<T extends string>(
|
|
||||||
factory: () => DashboardFilterState<T>,
|
|
||||||
): DashboardFilterState<T> {
|
|
||||||
return factory()
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import type { BrokerMoney } from '@/shared/api'
|
import type { BrokerMoney } from '@/shared/api'
|
||||||
import {
|
import {
|
||||||
type DashboardMoneyLike,
|
|
||||||
eventTypeTone,
|
eventTypeTone,
|
||||||
formatDashboardCurrency,
|
formatDashboardCurrency,
|
||||||
incomeTypeTone,
|
incomeTypeTone,
|
||||||
@ -14,21 +13,28 @@ function money(currency: string, value: number): BrokerMoney {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('formatDashboardCurrency', () => {
|
describe('formatDashboardCurrency', () => {
|
||||||
it('renders RUB with the ₽ symbol', () => {
|
it('renders RUB with the ₽ symbol via the shared formatter', () => {
|
||||||
const result = formatDashboardCurrency(money('RUB', 1234.56))
|
const result = formatDashboardCurrency(money('RUB', 1234.56))
|
||||||
expect(result).toContain('₽')
|
expect(result).toContain('₽')
|
||||||
expect(result).not.toContain('RUB')
|
expect(result).not.toContain('RUB')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('falls back to currency code for unknown currencies', () => {
|
it('renders known non-RUB currencies via the shared formatter', () => {
|
||||||
const result = formatDashboardCurrency(money('USD', 42))
|
const result = formatDashboardCurrency(money('USD', 42))
|
||||||
expect(result).toContain('USD')
|
expect(result).toContain('$')
|
||||||
|
expect(result).not.toContain('USD')
|
||||||
expect(result).not.toContain('₽')
|
expect(result).not.toContain('₽')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('falls back to currency code for unknown currencies', () => {
|
||||||
|
const result = formatDashboardCurrency(money('XYZ', 100))
|
||||||
|
expect(result).toContain('XYZ')
|
||||||
|
expect(result).not.toContain('¤')
|
||||||
|
})
|
||||||
|
|
||||||
it('falls back to currency code via the value-only shape', () => {
|
it('falls back to currency code via the value-only shape', () => {
|
||||||
const result = formatDashboardCurrency({ currency: 'EUR', value: 9.99 } as DashboardMoneyLike)
|
const result = formatDashboardCurrency({ currency: 'XYZ', value: 9.99 })
|
||||||
expect(result).toContain('EUR')
|
expect(result).toContain('XYZ')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns "—" for null or undefined', () => {
|
it('returns "—" for null or undefined', () => {
|
||||||
@ -72,26 +78,18 @@ describe('moneyTone', () => {
|
|||||||
|
|
||||||
describe('eventTypeTone', () => {
|
describe('eventTypeTone', () => {
|
||||||
it('maps each event type to a stable tone', () => {
|
it('maps each event type to a stable tone', () => {
|
||||||
expect(eventTypeTone('dividend')).toBeTruthy()
|
|
||||||
expect(eventTypeTone('coupon')).toBeTruthy()
|
|
||||||
expect(eventTypeTone('maturity')).toBeTruthy()
|
|
||||||
expect(eventTypeTone('offer')).toBeTruthy()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns success for dividend events', () => {
|
|
||||||
expect(eventTypeTone('dividend')).toBe('success')
|
expect(eventTypeTone('dividend')).toBe('success')
|
||||||
|
expect(eventTypeTone('coupon')).toBe('info')
|
||||||
|
expect(eventTypeTone('maturity')).toBe('warning')
|
||||||
|
expect(eventTypeTone('offer')).toBe('neutral')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('incomeTypeTone', () => {
|
describe('incomeTypeTone', () => {
|
||||||
it('returns a tone for each supported label', () => {
|
it('maps each income label to a stable tone', () => {
|
||||||
expect(incomeTypeTone('Дивиденд')).toBeTruthy()
|
|
||||||
expect(incomeTypeTone('Дивиденд (внешний)')).toBeTruthy()
|
|
||||||
expect(incomeTypeTone('Купон')).toBeTruthy()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('marks plain dividends as success', () => {
|
|
||||||
expect(incomeTypeTone('Дивиденд')).toBe('success')
|
expect(incomeTypeTone('Дивиденд')).toBe('success')
|
||||||
|
expect(incomeTypeTone('Дивиденд (внешний)')).toBe('info')
|
||||||
|
expect(incomeTypeTone('Купон')).toBe('warning')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import type { BrokerEventItem, BrokerMoney } from '@/shared/api'
|
import type { BrokerEventItem, BrokerMoney } from '@/shared/api'
|
||||||
|
import { formatBrokerCurrencyValue } from '@/shared/lib/formatters'
|
||||||
|
import type { DashboardIncomeTypeLabel } from './dashboardIncome'
|
||||||
|
|
||||||
export type MoneyTone = 'positive' | 'negative' | 'planned' | 'neutral'
|
export type MoneyTone = 'positive' | 'negative' | 'planned' | 'neutral'
|
||||||
|
|
||||||
@ -17,11 +19,22 @@ export function moneyTone(value: number | null | undefined, source?: MoneySource
|
|||||||
|
|
||||||
export function formatDashboardCurrency(money: DashboardMoneyLike | null | undefined): string {
|
export function formatDashboardCurrency(money: DashboardMoneyLike | null | undefined): string {
|
||||||
if (!money) return '—'
|
if (!money) return '—'
|
||||||
const value = new Intl.NumberFormat('ru-RU', {
|
try {
|
||||||
|
const formatted = formatBrokerCurrencyValue(money.currency, money.value)
|
||||||
|
if (formatted.includes('¤')) {
|
||||||
|
return formatCurrencyCodeFallback(money.value, money.currency)
|
||||||
|
}
|
||||||
|
return formatted
|
||||||
|
} catch {
|
||||||
|
return formatCurrencyCodeFallback(money.value, money.currency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCurrencyCodeFallback(value: number, currency: string): string {
|
||||||
|
const numberPart = new Intl.NumberFormat('ru-RU', {
|
||||||
maximumFractionDigits: 2,
|
maximumFractionDigits: 2,
|
||||||
}).format(money.value)
|
}).format(value)
|
||||||
if (money.currency === 'RUB') return `${value}\u00a0₽`
|
return `${numberPart}\u00a0${currency}`
|
||||||
return `${value}\u00a0${money.currency}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function eventTypeTone(type: BrokerEventItem['type']): TypeTone {
|
export function eventTypeTone(type: BrokerEventItem['type']): TypeTone {
|
||||||
@ -37,9 +50,7 @@ export function eventTypeTone(type: BrokerEventItem['type']): TypeTone {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type IncomeTypeLabel = 'Дивиденд' | 'Дивиденд (внешний)' | 'Купон'
|
export function incomeTypeTone(label: DashboardIncomeTypeLabel): TypeTone {
|
||||||
|
|
||||||
export function incomeTypeTone(label: IncomeTypeLabel): TypeTone {
|
|
||||||
switch (label) {
|
switch (label) {
|
||||||
case 'Дивиденд':
|
case 'Дивиденд':
|
||||||
return 'success'
|
return 'success'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user