@@ -70,7 +69,7 @@ export function BrokerAccountLayout({ children }: { children: ReactNode }) {
...baseLinkStyle,
color: 'var(--color-primary)',
fontWeight: 700,
- background: 'rgba(25, 118, 210, 0.08)',
+ background: 'rgba(25, 118, 210, 0.1)',
},
}}
>
diff --git a/apps/frontend/src/widgets/broker-dashboard/lib/dashboardFilters.ts b/apps/frontend/src/widgets/broker-dashboard/lib/dashboardFilters.ts
index 39abe1e..06ad5d6 100644
--- a/apps/frontend/src/widgets/broker-dashboard/lib/dashboardFilters.ts
+++ b/apps/frontend/src/widgets/broker-dashboard/lib/dashboardFilters.ts
@@ -18,7 +18,7 @@ export function defaultEventsFilters(): DashboardFilterState
const now = dayjs()
return {
from: now.subtract(7, 'day').format('YYYY-MM-DD'),
- to: now.format('YYYY-MM-DD'),
+ to: now.add(7, 'day').format('YYYY-MM-DD'),
types: [...DASHBOARD_EVENT_TYPES],
preset: '7d',
}
@@ -40,7 +40,9 @@ export function applyDatePreset(
): DashboardFilterState {
const now = dayjs()
const next = { ...filters, preset }
- if (preset === 'all') return { ...next, from: '', to: '' }
+ if (preset === 'all') {
+ return { ...next, from: '2000-01-01', to: '2099-12-31' }
+ }
const amount = preset === '1y' ? 1 : Number.parseInt(preset, 10)
const unit = preset === '1y' ? 'year' : 'day'
return {
@@ -50,6 +52,25 @@ export function applyDatePreset(
}
}
+export function applyEventDatePreset(
+ filters: DashboardFilterState,
+ preset: DashboardDatePreset,
+): DashboardFilterState {
+ const now = dayjs()
+ const next = { ...filters, preset }
+ if (preset === 'all') {
+ return { ...next, from: '2000-01-01', to: '2099-12-31' }
+ }
+
+ const amount = preset === '1y' ? 1 : Number.parseInt(preset, 10)
+ const unit = preset === '1y' ? 'year' : 'day'
+ return {
+ ...next,
+ from: now.subtract(amount, unit).format('YYYY-MM-DD'),
+ to: now.add(amount, unit).format('YYYY-MM-DD'),
+ }
+}
+
export function validateDashboardFilters(
filters: DashboardFilterState,
): string {
diff --git a/apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.test.ts b/apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.test.ts
index 4082f8d..17e7de4 100644
--- a/apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.test.ts
+++ b/apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.test.ts
@@ -96,10 +96,10 @@ describe('moneyToneToColor', () => {
})
describe('incomeTypeTone', () => {
- it('maps each income label to a stable tone', () => {
+ it('maps each income label to a stable tone matching HTML parity (coupon=info)', () => {
expect(incomeTypeTone('Дивиденд')).toBe('success')
- expect(incomeTypeTone('Дивиденд (внешний)')).toBe('info')
- expect(incomeTypeTone('Купон')).toBe('warning')
+ expect(incomeTypeTone('Дивиденд (внешний)')).toBe('success')
+ expect(incomeTypeTone('Купон')).toBe('info')
})
})
diff --git a/apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts b/apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts
index 04e31db..d1fdd00 100644
--- a/apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts
+++ b/apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts
@@ -65,11 +65,10 @@ export function eventTypeTone(type: BrokerEventItem['type']): TypeTone {
export function incomeTypeTone(label: DashboardIncomeTypeLabel): TypeTone {
switch (label) {
case 'Дивиденд':
- return 'success'
case 'Дивиденд (внешний)':
- return 'info'
+ return 'success'
case 'Купон':
- return 'warning'
+ return 'info'
}
}
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 06c4755..3624b50 100644
--- a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx
+++ b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx
@@ -210,11 +210,31 @@ describe('BrokerDashboard', () => {
})
})
- it('shows date filter toggle button with apply action and accessible name', async () => {
+ it('requests future events in the default dashboard range', () => {
+ vi.useFakeTimers()
+ try {
+ vi.setSystemTime(new Date('2026-06-27T12:00:00.000Z'))
+
+ renderWithProviders()
+
+ expect(hookMocks.useBrokerEvents).toHaveBeenCalledWith(
+ 'acc-1',
+ expect.objectContaining({
+ from: '2026-06-20',
+ to: '2026-07-04',
+ }),
+ { enabled: true },
+ )
+ } finally {
+ vi.useRealTimers()
+ }
+ })
+
+ it('shows date filter toggle button with refresh action and accessible name', async () => {
const user = userEvent.setup()
renderWithProviders()
- const applyButtons = screen.getAllByRole('button', { name: /Применить период/ })
+ const applyButtons = screen.getAllByRole('button', { name: 'Обновить' })
expect(applyButtons).toHaveLength(2)
const toggleButtons = screen.getAllByRole('button', { name: /^Период/ })
@@ -228,6 +248,7 @@ describe('BrokerDashboard', () => {
expect(screen.getByText('1г')).toBeInTheDocument()
expect(screen.getByText('Всё')).toBeInTheDocument()
expect(screen.getByText('Сбросить')).toBeInTheDocument()
+ expect(screen.getByRole('button', { name: 'Применить период' })).toBeInTheDocument()
})
it('does not render a text chevron glyph inside the period toggle button', () => {
@@ -386,6 +407,46 @@ describe('BrokerDashboard', () => {
expect(screen.getByText('Ожидается')).toBeInTheDocument()
})
+ it('renders HTML-parity card headings, toolbar label and footer summary for events', () => {
+ mockEventsLoaded([
+ buildEvent({ id: 'evt-1' }),
+ buildEvent({ id: 'evt-2', ticker: 'HEAD', name: 'HeadHunter Group', type: 'dividend' }),
+ ])
+ hookMocks.useBrokerEvents.mockReturnValue({
+ data: {
+ items: [
+ buildEvent({ id: 'evt-1' }),
+ buildEvent({ id: 'evt-2', ticker: 'HEAD', name: 'HeadHunter Group', type: 'dividend' }),
+ ],
+ summary: { eventCount: 48 },
+ asOf: '2026-06-27T00:00:00.000Z',
+ },
+ isLoading: false,
+ isError: false,
+ })
+
+ renderWithProviders()
+
+ const eventsSection = screen.getByLabelText('События')
+ expect(screen.getByText('2 из 48')).toBeInTheDocument()
+ expect(within(eventsSection).getAllByText('Тип').length).toBeGreaterThan(0)
+ expect(
+ within(eventsSection).getByText('Показано 2 событий за выбранный период'),
+ ).toBeInTheDocument()
+ })
+
+ it('renders HTML-parity count badge and footer summary for income', () => {
+ mockOperationsLoaded([
+ buildOperation({ id: 'op-1' }),
+ buildOperation({ id: 'op-2', ticker: 'IRAO', name: 'Интер РАО' }),
+ ])
+
+ renderWithProviders()
+
+ expect(screen.getByText('2 операции')).toBeInTheDocument()
+ expect(screen.getByText(/Показано 2 · Итого:/)).toBeInTheDocument()
+ })
+
it('uses negative tone when event actual amount is negative', () => {
mockEventsLoaded([
buildEvent({
diff --git a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx
index ffcee25..6069b93 100644
--- a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx
+++ b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx
@@ -8,6 +8,7 @@ import type { BrokerPortfolio } from '@/shared/api'
import { useCursorPagination } from '@/shared/lib/useCursorPagination'
import {
applyDatePreset,
+ applyEventDatePreset,
type DashboardDatePreset,
type DashboardEventType,
type DashboardIncomeType,
@@ -151,7 +152,7 @@ export function BrokerDashboard({
}, [incomePagination])
function handleDraftEventPresetChange(preset: DashboardDatePreset) {
- setDraftEventFilters((filters) => applyDatePreset(filters, preset))
+ setDraftEventFilters((filters) => applyEventDatePreset(filters, preset))
}
function handleDraftEventFromChange(value: string) {
@@ -194,6 +195,7 @@ export function BrokerDashboard({
onApplyFilters={applyEventFilters}
onResetFilters={resetEventFilters}
hasDraftTypes={hasDraftEventTypes}
+ totalCount={events.data?.summary?.eventCount}
page={eventPage}
canGoBack={eventPage > 1}
canGoForward={eventPage * eventPageSize < eventItems.length}
@@ -217,6 +219,7 @@ export function BrokerDashboard({
onApplyFilters={applyIncomeFilters}
onResetFilters={resetIncomeFilters}
hasDraftTypes={hasDraftIncomeTypes}
+ visibleCount={operations.data?.items?.length ?? 0}
pageNumber={incomePagination.pageNumber}
canGoBack={incomePagination.pageNumber > 1}
canGoForward={operations.data?.hasNext ?? false}
diff --git a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAllocationCard.tsx b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAllocationCard.tsx
index 679e78f..d7ed14e 100644
--- a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAllocationCard.tsx
+++ b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAllocationCard.tsx
@@ -31,6 +31,14 @@ export function BrokerDashboardAllocationCard({ portfolio }: { portfolio: Broker
Нет данных для распределения
) : (
+
+
+ Структура портфеля
+
+
+ {formatBrokerMoney(portfolio.totals.portfolio)}
+
+
{sectors.map((sector) => (
diff --git a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx
index 5695160..40e9c5e 100644
--- a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx
+++ b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx
@@ -72,20 +72,35 @@ export function BrokerDashboardAnalyticsCard({
const value = data[field]
const tone = analyticsTone(field, value)
return (
-
- {analyticsDisplay(field, value, data.currency)}
-
- }
- />
+ sx={{
+ borderRadius: 2,
+ border: '1px solid',
+ borderColor: tone === 'negative' ? 'error.light' : 'divider',
+ bgcolor:
+ tone === 'positive'
+ ? 'rgba(46, 125, 50, 0.06)'
+ : tone === 'negative'
+ ? 'rgba(211, 47, 47, 0.06)'
+ : 'grey.50',
+ p: 1.5,
+ }}
+ >
+
+ {analyticsDisplay(field, value, data.currency)}
+
+ }
+ />
+
)
})}
diff --git a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardCard.tsx b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardCard.tsx
index 3981c36..0cbe672 100644
--- a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardCard.tsx
+++ b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardCard.tsx
@@ -4,6 +4,7 @@ import type { ReactNode } from 'react'
type BrokerDashboardCardProps = {
title: string
+ badge?: ReactNode
action?: ReactNode
filters?: ReactNode
children: ReactNode
@@ -13,6 +14,7 @@ type BrokerDashboardCardProps = {
export function BrokerDashboardCard({
title,
+ badge,
action,
filters,
children,
@@ -38,15 +40,36 @@ export function BrokerDashboardCard({
-
- {title}
-
+
+
+ {title}
+
+ {badge ? (
+
+ {badge}
+
+ ) : null}
+
{action}
{filters && {filters}}
diff --git a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardDateFilter.tsx b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardDateFilter.tsx
index 6384840..16ea1d8 100644
--- a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardDateFilter.tsx
+++ b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardDateFilter.tsx
@@ -88,9 +88,16 @@ export function BrokerDashboardDateFilter({
setSelectingStage('from')
}
+ function handleApply() {
+ onApply()
+ handleClose()
+ }
+
const selectingLabel =
selectingStage === 'from' ? 'Выберите начало периода' : 'Выберите конец периода'
const periodAriaLabel = appliedLabel ? `Период: ${appliedLabel}` : 'Период'
+ const rangeInverted = Boolean(draftFrom && draftTo) && dayjs(draftTo).isBefore(dayjs(draftFrom))
+ const canApply = hasDraftTypes && !rangeInverted
return (
@@ -151,29 +158,6 @@ export function BrokerDashboardDateFilter({
-
- Применить период
-
-
+
Сбросить
+
+ Применить период
+
+
+ Обновить
+
)
diff --git a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardEventsCard.tsx b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardEventsCard.tsx
index beb46c6..ab9d494 100644
--- a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardEventsCard.tsx
+++ b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardEventsCard.tsx
@@ -79,6 +79,7 @@ type BrokerDashboardEventsCardProps = {
onApplyFilters: () => void
onResetFilters: () => void
hasDraftTypes: boolean
+ totalCount?: number
page: number
onPreviousPage: () => void
onNextPage: () => void
@@ -124,6 +125,7 @@ export function BrokerDashboardEventsCard({
onApplyFilters,
onResetFilters,
hasDraftTypes,
+ totalCount,
page,
onPreviousPage,
onNextPage,
@@ -135,6 +137,7 @@ export function BrokerDashboardEventsCard({
return (
0 ? `${events.length} из ${totalCount ?? events.length}` : undefined}
action={Все события}
filters={
-
-
-
- {page}
+
+
+ Показано {events.length} событий за выбранный период
-
+
+
+
+ {page}
+
+
+
)}
diff --git a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardIncomeCard.tsx b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardIncomeCard.tsx
index b91902d..8101cc9 100644
--- a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardIncomeCard.tsx
+++ b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardIncomeCard.tsx
@@ -73,6 +73,7 @@ type BrokerDashboardIncomeCardProps = {
onApplyFilters: () => void
onResetFilters: () => void
hasDraftTypes: boolean
+ visibleCount: number
pageNumber: number
canGoBack: boolean
canGoForward: boolean
@@ -97,6 +98,7 @@ export function BrokerDashboardIncomeCard({
onApplyFilters,
onResetFilters,
hasDraftTypes,
+ visibleCount,
pageNumber,
canGoBack,
canGoForward,
@@ -109,6 +111,7 @@ export function BrokerDashboardIncomeCard({
return (
0 ? `${visibleCount} операции` : undefined}
action={Все операции}
filters={
-
- Показано: {rows.length} · Итого:{' '}
- {total
- ? `${total.value >= 0 ? '+' : '−'}${formatDashboardCurrency({
- currency: total.currency,
- value: Math.abs(total.value),
- })}`
- : '—'}
-
-
-
-
- {pageNumber}
+
+
+ Показано {rows.length} · Итого:{' '}
+ {total
+ ? `${total.value >= 0 ? '+' : '−'}${formatDashboardCurrency({
+ currency: total.currency,
+ value: Math.abs(total.value),
+ })}`
+ : '—'}
-
+
+
+
+ {pageNumber}
+
+
+
)}
diff --git a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardTableToolbar.tsx b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardTableToolbar.tsx
index 02b5e36..5493ac4 100644
--- a/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardTableToolbar.tsx
+++ b/apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardTableToolbar.tsx
@@ -11,7 +11,7 @@ export function BrokerDashboardTableToolbar({ chips, children }: BrokerDashboard
- {chips}
+
+
+ Тип
+
+ {chips}
+
{children}
)
diff --git a/docs/features/broker-dashboard-redesign/plan.md b/docs/features/broker-dashboard-redesign/plan.md
index 87edd2b..fd78c1d 100644
--- a/docs/features/broker-dashboard-redesign/plan.md
+++ b/docs/features/broker-dashboard-redesign/plan.md
@@ -65,7 +65,8 @@
### 3. События
- Типы событий переключаются chip-фильтрами с немедленным применением.
-- Диапазон дат по умолчанию: `сегодня - 7 дней` / `сегодня`.
+- Диапазон дат по умолчанию: `сегодня - 7 дней` / `сегодня + 7 дней`, чтобы обзор сразу показывал
+ ближайшие будущие события.
- Диапазон дат редактируется отдельно от применённого состояния: draft state меняется локально, запрос уходит только по действию применения периода.
- В шапке управления периодом не используется слово `Фильтр`; роль управления считывается через иконку календаря, применённый диапазон, раскрытие панели и пресеты.
- Native `` не используется; период выбирается через одно визуальное поле и popover с community `DateCalendar` из `@mui/x-date-pickers`.
@@ -73,6 +74,7 @@
- При пустом выборе типов запрос отключается, карточка показывает валидационное сообщение.
- Пагинация локальная, по 10 событий на страницу, сбрасывается при смене применённых фильтров.
- При загрузке карточка показывает skeleton таблицы событий с колонками дата, инструмент, тип, сумма, статус.
+- В toolbar карточки используются count badge, label `Тип`, кнопка `Обновить` и footer-summary по паттерну HTML-эталона.
### 4. Доходы
@@ -118,6 +120,7 @@
используя крупный `Heading size="title"`.
- `BrokerDashboardDateFilter` должен использовать иконку раскрытия вместо текстового символа и сохранять
единый toolbar-паттерн для `События` и `Доходы`.
+- Для `События` period presets должны поддерживать будущую часть диапазона, а не обрезаться текущим днём.
- Таблицы `События` и `Доходы` должны иметь `thead`, type badges, двухстрочный инструмент при наличии
названия и semantic amount colors.
- `BrokerDashboardAnalyticsCard` должен окрашивать KPI-карточки по смыслу и показывать RUB через `₽`.
@@ -168,7 +171,7 @@
- [ ] Обновить `BrokerDashboardDateFilter`: убрать слово `Фильтр` из пользовательского текста, заменить `Показать` на понятное действие применения периода и визуально собрать chips, диапазон, сброс и применение в аккуратную шапку.
- [ ] Заменить native date inputs на одно визуальное поле периода, которое открывает MUI `Popover` с community `DateCalendar`.
- [ ] Реализовать локальную логику выбора диапазона: первый клик задаёт начало, второй — конец; если конец раньше начала, диапазон пересобирается от выбранной даты.
-- [ ] Настроить default range событий на `сегодня - 7 дней` / `сегодня`.
+- [ ] Настроить default range событий на `сегодня - 7 дней` / `сегодня + 7 дней`.
- [ ] Подключить для событий draft/applied state: типы применяются сразу, даты только по действию применения периода.
- [ ] Сохранять локальную пагинацию по 10 событий и сбрасывать её при смене применённых фильтров.
- [ ] Заменить текстовую загрузку событий на skeleton таблицы.
diff --git a/docs/features/broker-dashboard-redesign/spec.md b/docs/features/broker-dashboard-redesign/spec.md
index d1d882f..ac37cc3 100644
--- a/docs/features/broker-dashboard-redesign/spec.md
+++ b/docs/features/broker-dashboard-redesign/spec.md
@@ -105,7 +105,7 @@ Hero показывает:
### 4. Блок `События`
- Блок использует существующий источник `useBrokerEvents(accountId, query)`.
-- По умолчанию применяется период `сегодня - 7 дней` / `сегодня` и типы
+- По умолчанию применяется период `сегодня - 7 дней` / `сегодня + 7 дней` и типы
`dividend,coupon,maturity,offer`, как в существующей вкладке событий.
- Блок содержит кликабельные chip-фильтры типов событий: `Дивиденды`, `Купоны`, `Погашения`, `Оферты`.
- Пользователь может выбрать несколько типов событий.
@@ -114,7 +114,8 @@ Hero показывает:
- Изменение chip-фильтров типов событий применяется сразу и возвращает локальную пагинацию на первую
страницу.
- Блок содержит фильтр периода `from` / `to`.
-- По умолчанию применяется недельный период `сегодня - 7 дней` / `сегодня`.
+- По умолчанию применяется недельный период `сегодня - 7 дней` / `сегодня + 7 дней`, чтобы обзор
+ сразу показывал ближайшие будущие события.
- Изменение черновых фильтров не запускает запрос до применения периода пользователем.
- В пользовательском тексте шапки периода не используется слово `Фильтр`; UI должен считываться как
управление периодом за счёт иконки календаря, применённого диапазона, пресетов и affordance раскрытия.
@@ -257,7 +258,7 @@ Hero показывает:
- В таблице `События` инструмент отображается двумя строками при наличии названия, тип отображается
бейджем, сумма и статус имеют семантические цвета.
- Блок `События` поддерживает multi-select chip-фильтр типов и локальную пагинацию по 10 событий.
-- Блок `События` по умолчанию запрашивает период `сегодня - 7 дней` / `сегодня` и использует одно поле
+- Блок `События` по умолчанию запрашивает период `сегодня - 7 дней` / `сегодня + 7 дней` и использует одно поле
периода с popover-календарём на базе community `DateCalendar`.
- Блок `Доходы` показывает доходные операции дивидендов и купонов и итог по отображаемым строкам.
- В таблице `Доходы` инструмент отображается двумя строками при наличии названия, тип отображается
diff --git a/docs/features/broker-dashboard-redesign/tasks.md b/docs/features/broker-dashboard-redesign/tasks.md
index 1ebdd33..30661d9 100644
--- a/docs/features/broker-dashboard-redesign/tasks.md
+++ b/docs/features/broker-dashboard-redesign/tasks.md
@@ -44,7 +44,8 @@
- [x] Переименовать действие `Показать` в управлении периодом и визуально улучшить шапку фильтров.
- [x] Заменить native date inputs на одно поле периода с popover и community `DateCalendar` из `@mui/x-date-pickers`.
- [x] Не использовать `@mui/x-date-pickers-pro` и `DateRangePicker`.
-- [x] Настроить default range событий и доходов на `сегодня - 7 дней` / `сегодня`.
+- [x] Настроить default range событий на `сегодня - 7 дней` / `сегодня + 7 дней`, доходов — на
+ `сегодня - 7 дней` / `сегодня`.
- [x] Заменить текстовую загрузку `События` на skeleton таблицы.
- [x] Заменить текстовую загрузку `Доходы` на skeleton таблицы.
- [x] Обновить component tests под новые тексты, единое поле периода, popover-календарь и skeleton loading states.
@@ -56,18 +57,27 @@
- [x] Согласовать статический визуальный эталон `docs/research/2026-06-27-broker-account-redesign.html`.
- [x] Обновить `spec.md` под HTML parity: компактные заголовки, единый toolbar, бейджи, цвета, подписи инструментов, `₽`.
- [x] Обновить `plan.md` под перенос HTML parity в React-компоненты.
-- [ ] Добавить `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts` с helpers для money tone, type tone, currency symbol и instrument display.
-- [ ] Добавить `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.test.ts`.
+- [x] Добавить `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts` с helpers для money tone, type tone, currency symbol и instrument display.
+- [x] Добавить `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.test.ts`.
- [ ] Расширить income helpers так, чтобы строки доходов могли отдавать main/subtitle инструмента без дублирования текста.
-- [ ] Обновить `BrokerDashboardHero`: semantic colors для доходности, дневного изменения и всего полученных доходов.
-- [ ] Обновить `BrokerDashboardCard`: компактный card heading вместо крупного page-level heading.
-- [ ] Обновить `BrokerDashboardDateFilter`: единый toolbar-паттерн и chevron-иконка вместо текстового `v`.
-- [ ] Обновить `BrokerDashboardEventsCard`: `thead`, двухстрочный инструмент, type badges, semantic amount colors, status badges.
-- [ ] Обновить `BrokerDashboardIncomeCard`: `thead`, двухстрочный инструмент, type badges, semantic amount colors.
-- [ ] Обновить `BrokerDashboardTableSkeleton`: общий skeleton-паттерн для событий и доходов с корректной геометрией колонок.
+- [x] Обновить `BrokerDashboardHero`: semantic colors для доходности, дневного изменения и всего полученных доходов.
+- [x] Обновить `BrokerDashboardCard`: компактный card heading вместо крупного page-level heading.
+- [x] Обновить `BrokerDashboardDateFilter`: единый toolbar-паттерн и chevron-иконка вместо текстового `v`.
+- [x] Обновить `BrokerDashboardEventsCard`: `thead`, двухстрочный инструмент, type badges, semantic amount colors, status badges.
+- [x] Обновить `BrokerDashboardIncomeCard`: `thead`, двухстрочный инструмент, type badges, semantic amount colors.
+ Бейдж `Купон` приведён к `info` (синий) согласно HTML-эталону `.type-badge.coupon` (`incomeTypeTone` в `dashboardVisual.ts`),
+ `Дивиденд (внешний)` отнесён к `success` (семейство дивидендов).
+- [x] Обновить `BrokerDashboardTableSkeleton`: общий skeleton-паттерн для событий и доходов с корректной геометрией колонок.
- [x] Обновить `BrokerDashboardAnalyticsCard`: `₽` для RUB и positive/negative tone карточек.
- [x] Обновить component tests dashboard под HTML parity (analytics tones).
-- [ ] Проверить, что блок `Доходы` не расширяет backend/API и остаётся в рамках текущих income-типов.
+- [x] Проверить, что блок `Доходы` не расширяет backend/API и остаётся в рамках текущих income-типов.
+ Исправлен пресет «Всё»: `applyDatePreset('all')` теперь отдаёт широкий диапазон `2000-01-01`–`2099-12-31`
+ вместо пустых `from`/`to`, которые backend (`BrokerEventsQueryDto @Matches`) отвергал с 400. В DateFilter
+ кнопка «Применить период» блокируется при инвертированном диапазоне (`to < from`), чтобы избежать
+ молчаливо пустого ответа (например, будущий `from` при дефолтном `to=сегодня`).
+- [x] Исправить default-range `События`, чтобы dashboard по умолчанию и через пресеты мог загружать будущие события.
+- [x] Подтянуть card headers, toolbar label `Тип`, count badges и footer summaries ближе к
+ `docs/research/2026-06-27-broker-account-redesign.html`.
- [ ] Проверить desktop layout `/broker/2084014113` против `docs/research/2026-06-27-broker-account-redesign.html`.
- [ ] Проверить mobile layout `/broker/2084014113` на viewport `390x844` против `docs/research/2026-06-27-broker-account-redesign.html`.
@@ -127,4 +137,3 @@ HTML-эталона (`docs/research/2026-06-27-broker-account-redesign.html:1145
- Реальный viewport на `390x844` для подтверждения отсутствия общего горизонтального overflow.
Геометрия таблиц уже переключена на внутренний `overflowX: 'auto'` (`BrokerDashboardEventsCard.tsx:176`,
`BrokerDashboardIncomeCard.tsx`), но фактическая вёрстка в браузере не сверялась с эталоном.
-