docs: add broker overview html parity spec
This commit is contained in:
parent
3fdceb9438
commit
c4b93f9f0c
329
docs/features/broker-account-overview-html-parity/plan.md
Normal file
329
docs/features/broker-account-overview-html-parity/plan.md
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
# Финальный HTML Parity брокерского overview — план реализации
|
||||||
|
|
||||||
|
> **Для агентных исполнителей:** ОБЯЗАТЕЛЬНЫЙ SUB-SKILL: использовать
|
||||||
|
> `superpowers:subagent-driven-development` (предпочтительно) или `superpowers:executing-plans` для
|
||||||
|
> пошагового выполнения. Шаги ведутся чекбоксами `- [ ]`.
|
||||||
|
|
||||||
|
**Цель:** привести `/broker/:accountId` к финальному HTML-эталону
|
||||||
|
`docs/research/frontend-overview-redesign/example.html` без изменения URL-структуры счёта.
|
||||||
|
|
||||||
|
**Архитектура:** backend расширяет существующий broker read API минимальными агрегатами и историей
|
||||||
|
стоимости. Frontend остаётся в FSD-границах `entities/broker-*`, `widgets/broker-dashboard`,
|
||||||
|
`widgets/broker-account-layout`; dashboard-композиция меняется с промежуточной версии на финальную
|
||||||
|
HTML parity структуру.
|
||||||
|
|
||||||
|
**Технологии:** NestJS, Prisma/T-Bank broker operations read path, Swagger/OpenAPI codegen, React 18,
|
||||||
|
TanStack Query, TanStack Router, MUI + `@moex-vibe/design-system`, Vitest, Testing Library.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Canonical Research Source
|
||||||
|
|
||||||
|
- Использовать как visual source of truth:
|
||||||
|
`docs/research/frontend-overview-redesign/example.html`.
|
||||||
|
- Не использовать как source of truth:
|
||||||
|
`docs/research/2026-06-27-broker-account-redesign.html`, пока файл не синхронизирован с финальным
|
||||||
|
макетом.
|
||||||
|
- Production UI не переносит demo-control `Данные / Загрузка`; этот control нужен только HTML-макету.
|
||||||
|
|
||||||
|
## Файлы
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
- Modify: `apps/backend/src/modules/tbank/dto/broker-analytics-response.dto.ts` — добавить
|
||||||
|
`totalFees`, `totalTaxesPaid`.
|
||||||
|
- Create: `apps/backend/src/modules/tbank/dto/broker-portfolio-history-response.dto.ts` — DTO для
|
||||||
|
6-месячной истории стоимости.
|
||||||
|
- Modify: `apps/backend/src/modules/tbank/dto/broker-envelope.dto.ts` — envelope для history endpoint.
|
||||||
|
- Modify: `apps/backend/src/modules/tbank/dto/broker-operation-query.dto.ts` — query `categories`.
|
||||||
|
- Modify: `apps/backend/src/modules/tbank/services/broker-analytics.service.ts` — агрегировать fee/tax.
|
||||||
|
- Create: `apps/backend/src/modules/tbank/services/broker-portfolio-history.service.ts` — read-model
|
||||||
|
истории стоимости.
|
||||||
|
- Modify: `apps/backend/src/modules/tbank/services/broker-operations.service.ts` — применять
|
||||||
|
category-фильтр до ответа.
|
||||||
|
- Modify: `apps/backend/src/modules/tbank/tbank.controller.ts` — endpoint portfolio history.
|
||||||
|
- Modify: backend tests рядом с изменёнными сервисами/controller.
|
||||||
|
|
||||||
|
### Frontend data
|
||||||
|
|
||||||
|
- Modify: `apps/frontend/src/shared/api/index.ts` — экспорт нового `BrokerPortfolioHistory`.
|
||||||
|
- Create: `apps/frontend/src/entities/broker-account/api/brokerPortfolioHistoryApi.ts`.
|
||||||
|
- Create: `apps/frontend/src/entities/broker-account/model/useBrokerPortfolioHistory.ts`.
|
||||||
|
- Modify: `apps/frontend/src/entities/broker-account/index.ts` — экспорт hook/API.
|
||||||
|
- Modify: `apps/frontend/src/entities/broker-operation/api/brokerOperationApi.ts` — query `categories`.
|
||||||
|
|
||||||
|
### Frontend UI
|
||||||
|
|
||||||
|
- Modify: `apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx` — title yield
|
||||||
|
рядом с заголовком счёта, без page-header skeleton.
|
||||||
|
- Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx` — финальный порядок блоков
|
||||||
|
и источники данных.
|
||||||
|
- Replace/Remove: `BrokerDashboardHero.tsx` из overview-композиции; файл можно оставить только если
|
||||||
|
больше используется в тестах/экспортах, но в `/broker/:accountId` он не рендерится.
|
||||||
|
- Create: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerPortfolioHistoryCard.tsx`.
|
||||||
|
- Modify: `BrokerDashboardAnalyticsCard.tsx`, `BrokerDashboardAllocationCard.tsx`,
|
||||||
|
`BrokerDashboardEventsCard.tsx`, `BrokerDashboardSkeleton.tsx`.
|
||||||
|
- Modify/Create helpers в `apps/frontend/src/widgets/broker-dashboard/lib/` для chart points, latest
|
||||||
|
event rows, analytics display и visual tones.
|
||||||
|
- Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx` и helper unit tests.
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
- Modify: `docs/features/broker-account-overview-html-parity/tasks.md` по факту выполнения.
|
||||||
|
- Не переписывать `docs/features/broker-dashboard-redesign/*`; старая фича остаётся историей
|
||||||
|
промежуточной итерации.
|
||||||
|
|
||||||
|
## Data Contracts
|
||||||
|
|
||||||
|
### Analytics
|
||||||
|
|
||||||
|
`BrokerAnalyticsDto` расширяется:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
totalFees: number
|
||||||
|
totalTaxesPaid: number
|
||||||
|
```
|
||||||
|
|
||||||
|
Расчёт:
|
||||||
|
|
||||||
|
- `totalFees` = сумма absolute payment values executed операций категории `fee`;
|
||||||
|
- `totalTaxesPaid` = сумма absolute payment values executed операций категории `tax`;
|
||||||
|
- значения возвращаются как положительные агрегаты;
|
||||||
|
- frontend отображает их со знаком минус и negative tone.
|
||||||
|
|
||||||
|
### Portfolio History
|
||||||
|
|
||||||
|
Endpoint:
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /api/v1/broker/accounts/:accountId/portfolio/history?months=6
|
||||||
|
```
|
||||||
|
|
||||||
|
Response data:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type BrokerPortfolioHistoryData = {
|
||||||
|
accountId: string
|
||||||
|
points: Array<{
|
||||||
|
month: string
|
||||||
|
label: string
|
||||||
|
value: BrokerMoneyDto
|
||||||
|
}>
|
||||||
|
asOf: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Правила v1:
|
||||||
|
|
||||||
|
- `months` по умолчанию 6, допустимый диапазон 2-12;
|
||||||
|
- `points.length === months`;
|
||||||
|
- `month` в формате `YYYY-MM`;
|
||||||
|
- `label` — короткое русское имя месяца;
|
||||||
|
- последняя точка равна текущей `portfolio.totals.portfolio`;
|
||||||
|
- предыдущие точки могут быть estimated read-model из текущей стоимости и executed операций за период;
|
||||||
|
- контракт должен позволять позже заменить estimated calculation на persisted snapshots.
|
||||||
|
|
||||||
|
### Operation Categories
|
||||||
|
|
||||||
|
`BrokerOperationQueryDto` получает:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
categories?: string
|
||||||
|
```
|
||||||
|
|
||||||
|
Правила:
|
||||||
|
|
||||||
|
- comma-separated значения из `trade,income,tax,fee,transfer,other`;
|
||||||
|
- неизвестные категории игнорировать или валидировать с `400`; выбрать один вариант и покрыть тестом;
|
||||||
|
- filtering выполняется до формирования page response;
|
||||||
|
- overview `Последние события` запрашивает `categories=income,tax,fee`, `state=OPERATION_STATE_EXECUTED`,
|
||||||
|
`limit=7`.
|
||||||
|
|
||||||
|
## Implementation Tasks
|
||||||
|
|
||||||
|
### Task 1: Docs and research alignment
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `docs/features/broker-account-overview-html-parity/spec.md`
|
||||||
|
- `docs/features/broker-account-overview-html-parity/plan.md`
|
||||||
|
- `docs/features/broker-account-overview-html-parity/tasks.md`
|
||||||
|
|
||||||
|
- Проверить, что docs ссылаются на `docs/research/frontend-overview-redesign/example.html`.
|
||||||
|
- Проверить, что docs явно запрещают использовать старый dated HTML как canonical reference.
|
||||||
|
- Зафиксировать финальный порядок блоков и отсутствие production demo-toggle.
|
||||||
|
|
||||||
|
### Task 2: Backend analytics contract
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `apps/backend/src/modules/tbank/dto/broker-analytics-response.dto.ts`
|
||||||
|
- `apps/backend/src/modules/tbank/services/broker-analytics.service.ts`
|
||||||
|
- `apps/backend/src/modules/tbank/services/broker-analytics.service.spec.ts`
|
||||||
|
- `apps/backend/src/modules/tbank/tbank.controller.spec.ts`
|
||||||
|
|
||||||
|
- Добавить `totalFees`, `totalTaxesPaid` в DTO и тестовый response.
|
||||||
|
- Расширить analytics service наборами fee/tax типов через существующую категоризацию операций.
|
||||||
|
- Считать fee/tax только по executed операциям с `payment`.
|
||||||
|
- Возвращать округление до копеек аналогично текущим analytics агрегатам.
|
||||||
|
|
||||||
|
### Task 3: Backend portfolio history endpoint
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `apps/backend/src/modules/tbank/dto/broker-portfolio-history-response.dto.ts`
|
||||||
|
- `apps/backend/src/modules/tbank/dto/broker-envelope.dto.ts`
|
||||||
|
- `apps/backend/src/modules/tbank/services/broker-portfolio-history.service.ts`
|
||||||
|
- `apps/backend/src/modules/tbank/tbank.controller.ts`
|
||||||
|
- `apps/backend/src/modules/tbank/tbank.controller.spec.ts`
|
||||||
|
|
||||||
|
- Добавить DTO для history point и envelope.
|
||||||
|
- Добавить service, который проверяет account access через `BrokerAccountsService`.
|
||||||
|
- Получить текущую стоимость через existing portfolio service или общий read path без дублирования T-Bank
|
||||||
|
вызовов сверх необходимого.
|
||||||
|
- Вернуть 6 monthly points для default query.
|
||||||
|
- Покрыть default months, invalid account и shape response тестами.
|
||||||
|
|
||||||
|
### Task 4: Backend operation category filtering
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `apps/backend/src/modules/tbank/dto/broker-operation-query.dto.ts`
|
||||||
|
- `apps/backend/src/modules/tbank/services/broker-operations.service.ts`
|
||||||
|
- `apps/backend/src/modules/tbank/services/broker-operations.service.spec.ts`
|
||||||
|
|
||||||
|
- Добавить `categories` query.
|
||||||
|
- Применить category filtering к mapped operations page перед отдачей response.
|
||||||
|
- Если T-Bank page может содержать меньше 7 подходящих операций после фильтрации, documented v1 поведение:
|
||||||
|
endpoint возвращает подходящие операции из текущей fetched page; full backfill pagination не требуется.
|
||||||
|
- Покрыть `categories=income,tax,fee` и неизвестную категорию тестом.
|
||||||
|
|
||||||
|
### Task 5: OpenAPI and frontend generated types
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `apps/frontend/src/shared/api/types.ts`
|
||||||
|
- `apps/frontend/src/shared/api/index.ts`
|
||||||
|
|
||||||
|
- Запустить backend dev server.
|
||||||
|
- Выполнить `npm run codegen -w apps/frontend`.
|
||||||
|
- Не редактировать generated `types.ts` вручную.
|
||||||
|
- Экспортировать новые frontend aliases из `shared/api/index.ts`.
|
||||||
|
- Проверить, что generated schemas содержат `totalFees`, `totalTaxesPaid`,
|
||||||
|
`BrokerPortfolioHistoryDataDto`.
|
||||||
|
|
||||||
|
### Task 6: Frontend data hooks
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `apps/frontend/src/entities/broker-account/api/brokerPortfolioHistoryApi.ts`
|
||||||
|
- `apps/frontend/src/entities/broker-account/model/useBrokerPortfolioHistory.ts`
|
||||||
|
- `apps/frontend/src/entities/broker-account/index.ts`
|
||||||
|
- `apps/frontend/src/entities/broker-operation/api/brokerOperationApi.ts`
|
||||||
|
|
||||||
|
- Добавить `getBrokerPortfolioHistory(accountId, { months })`.
|
||||||
|
- Добавить `useBrokerPortfolioHistory(accountId, { months: 6 })` с query key
|
||||||
|
`['broker', 'portfolio-history', accountId, months]`.
|
||||||
|
- Добавить `categories` в `BrokerOperationQuery`.
|
||||||
|
- Не менять существующие hooks detailed вкладок.
|
||||||
|
|
||||||
|
### Task 7: Page title yield
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx`
|
||||||
|
- `apps/frontend/src/pages/broker-account/ui/BrokerAccountOverviewPage.tsx`
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx`
|
||||||
|
|
||||||
|
- Перенести compact yield UI рядом с `Брокерский счёт`.
|
||||||
|
- Убрать отдельный hero KPI из overview.
|
||||||
|
- Не показывать page-title skeleton в loading state.
|
||||||
|
- Сохранить доступность: доходность имеет `aria-label="Доходность счёта"` или эквивалент.
|
||||||
|
|
||||||
|
### Task 8: Portfolio history card
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerPortfolioHistoryCard.tsx`
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/lib/dashboardPortfolioHistory.ts`
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx`
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardSkeleton.tsx`
|
||||||
|
|
||||||
|
- Создать карточку `Стоимость портфеля за 6 месяцев`.
|
||||||
|
- Нарисовать SVG line/area chart без visible point markers.
|
||||||
|
- Использовать 6 backend points и подписи месяцев из response.
|
||||||
|
- Сделать loading chart indicator без skeleton месяцев.
|
||||||
|
- Обеспечить одинаковую min-height loaded/loading.
|
||||||
|
- Первая и последняя точки графика должны совпадать с краями области.
|
||||||
|
|
||||||
|
### Task 9: Analytics card parity
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx`
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts`
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx`
|
||||||
|
|
||||||
|
- Summary row: `Стоимость портфеля`, `Всего доходов`.
|
||||||
|
- Detail grid: `Пополнения`, `Выводы`, `Дивиденды`, `Купоны`, `Комиссия`,
|
||||||
|
`Уплаченные налоги`.
|
||||||
|
- Удалить `Нетто` и `Всего получено` из overview-card.
|
||||||
|
- Отображать `totalFees` и `totalTaxesPaid` как negative UI amounts.
|
||||||
|
- Сохранить skeleton геометрию 2 + 6 карточек.
|
||||||
|
|
||||||
|
### Task 10: Allocation card parity
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAllocationCard.tsx`
|
||||||
|
- `apps/frontend/src/entities/broker-position/model/brokerAllocation.ts`
|
||||||
|
|
||||||
|
- Переименовать карточку в `Структура`.
|
||||||
|
- Убрать subtitle `Структура портфеля`.
|
||||||
|
- Показать итоговую стоимость под заголовком.
|
||||||
|
- Отобразить только строки `Акции`, `Облигации`, `Деньги` для overview parity.
|
||||||
|
- Сохранить корректное поведение для отсутствующих/нулевых значений.
|
||||||
|
|
||||||
|
### Task 11: Latest events card parity
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardEventsCard.tsx`
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/lib/dashboardEvents.ts`
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts`
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx`
|
||||||
|
|
||||||
|
- Переключить overview source с `useBrokerEvents` на `useBrokerOperations` с
|
||||||
|
`categories=income,tax,fee`, `limit=7`.
|
||||||
|
- Убрать filters toolbar, count badge, footer summary и колонку `Статус`.
|
||||||
|
- Переименовать карточку в `Последние события`.
|
||||||
|
- Отсортировать rows новые → старые.
|
||||||
|
- Инструмент: название сверху жирным, ticker/ISIN снизу серым.
|
||||||
|
- Тип: бейдж `Дивиденд`, `Купон`, `Погашение`, `Налог`, `Комиссия`.
|
||||||
|
- Налоговые/комиссионные/отрицательные операции отображать красным.
|
||||||
|
|
||||||
|
### Task 12: Tests, build, visual QA
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx`
|
||||||
|
- backend specs из предыдущих задач
|
||||||
|
- `docs/features/broker-account-overview-html-parity/tasks.md`
|
||||||
|
|
||||||
|
- Backend targeted tests:
|
||||||
|
`npm run test -w apps/backend -- src/modules/tbank/services/broker-analytics.service.spec.ts src/modules/tbank/services/broker-operations.service.spec.ts src/modules/tbank/tbank.controller.spec.ts`
|
||||||
|
- Frontend targeted tests:
|
||||||
|
`npm run test -w apps/frontend -- --run src/widgets/broker-dashboard`
|
||||||
|
- Full checks:
|
||||||
|
`npm run test:frontend`
|
||||||
|
`npm run lint -w apps/frontend`
|
||||||
|
`npm run build:frontend`
|
||||||
|
- Visual QA:
|
||||||
|
desktop `/broker/:accountId`;
|
||||||
|
mobile viewport `390x844`;
|
||||||
|
compare against `docs/research/frontend-overview-redesign/example.html`.
|
||||||
|
- Update `tasks.md` statuses and notes after verification.
|
||||||
|
|
||||||
|
## Risks and Decisions
|
||||||
|
|
||||||
|
- History chart v1 uses estimated read-model; exact historical market value requires future snapshots.
|
||||||
|
- Category filtering after one fetched T-Bank page may underfill latest events; acceptable for v1 unless
|
||||||
|
testing shows too many empty overview rows.
|
||||||
|
- The HTML mock uses static values. React must match structure and visual behavior, not literal amounts.
|
||||||
|
- `Последние события` is intentionally based on executed operations, not calendar events, because the
|
||||||
|
final HTML shows already happened cashflow rows and removes status/forecast semantics.
|
||||||
|
|
||||||
|
## Verification Matrix
|
||||||
|
|
||||||
|
- Spec requirements map to tasks 2-11.
|
||||||
|
- Backend API requirements map to tasks 2-5.
|
||||||
|
- Frontend order and visual parity map to tasks 7-11.
|
||||||
|
- Loading stability and mobile overflow are verified in task 12.
|
||||||
208
docs/features/broker-account-overview-html-parity/spec.md
Normal file
208
docs/features/broker-account-overview-html-parity/spec.md
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
# Финальный редизайн обзора брокерского счёта по HTML-эталону
|
||||||
|
|
||||||
|
Дата: 2026-06-27
|
||||||
|
Статус: спецификация подготовлена
|
||||||
|
Эпик: [Портфель брокера](../../epics/BrokerPortfolio.md)
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
|
||||||
|
Маршрут `/broker/:accountId` уже был переведён на dashboard-композицию в рамках
|
||||||
|
`docs/features/broker-dashboard-redesign`, но эта итерация отражает промежуточный вариант:
|
||||||
|
`Hero → События → Доходы → Аналитика доходности → Аллокация`.
|
||||||
|
|
||||||
|
После интерактивной дизайн-итерации пользователь согласовал финальный HTML-эталон в
|
||||||
|
`docs/research/frontend-overview-redesign/example.html`. Именно этот файл является canonical visual
|
||||||
|
reference для текущей фичи. Файл `docs/research/2026-06-27-broker-account-redesign.html` содержит более
|
||||||
|
раннюю версию и не должен использоваться как источник истины, пока не будет синхронизирован.
|
||||||
|
|
||||||
|
Финальный экран должен выглядеть как HTML-эталон, но работать на реальных данных приложения,
|
||||||
|
существующих FSD-границах, backend API и текущей светлой теме MoexVibe.
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Переделать обзор брокерского счёта `/broker/:accountId` так, чтобы production React-страница визуально
|
||||||
|
и поведенчески соответствовала финальному HTML-эталону:
|
||||||
|
|
||||||
|
`Заголовок счёта → Стоимость портфеля за 6 месяцев → Аналитика доходности → Структура → Последние события`.
|
||||||
|
|
||||||
|
## Пользовательский результат
|
||||||
|
|
||||||
|
Пользователь на `/broker/:accountId` может:
|
||||||
|
|
||||||
|
- сразу увидеть название счёта, текущую доходность и дневное изменение;
|
||||||
|
- увидеть график стоимости портфеля за последние 6 месяцев;
|
||||||
|
- увидеть ключевые показатели доходности, включая комиссии и уплаченные налоги;
|
||||||
|
- увидеть структуру портфеля в компактном bar-chart виде;
|
||||||
|
- увидеть последние уже произошедшие события/денежные операции по счёту;
|
||||||
|
- перейти в подробные вкладки `Акции`, `Облигации`, `Операции`, `События`, `Аналитика`.
|
||||||
|
|
||||||
|
## Область изменений
|
||||||
|
|
||||||
|
Входит в scope:
|
||||||
|
|
||||||
|
- frontend-маршрут `/broker/:accountId` и виджеты `widgets/broker-dashboard`;
|
||||||
|
- layout заголовка счёта в `widgets/broker-account-layout`;
|
||||||
|
- backend read endpoints брокерского домена, необходимые для честного отображения HTML parity;
|
||||||
|
- OpenAPI/codegen для новых или расширенных DTO;
|
||||||
|
- тесты backend/frontend и визуальная проверка desktop/mobile.
|
||||||
|
|
||||||
|
Не входит в scope:
|
||||||
|
|
||||||
|
- редизайн подробных вкладок `Акции`, `Облигации`, `Операции`, `События`, `Аналитика`;
|
||||||
|
- dark mode;
|
||||||
|
- demo-переключатель `Данные / Загрузка` из HTML-эталона;
|
||||||
|
- полноценное хранилище исторических снапшотов портфеля;
|
||||||
|
- изменение URL-структуры `/broker/:accountId/*`.
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
### 1. Общая композиция
|
||||||
|
|
||||||
|
- `/broker/:accountId` остаётся overview выбранного брокерского счёта.
|
||||||
|
- Страница использует порядок блоков из HTML-эталона:
|
||||||
|
`Заголовок счёта`, `Стоимость портфеля за 6 месяцев`, `Аналитика доходности`, `Структура`,
|
||||||
|
`Последние события`.
|
||||||
|
- Отдельный hero KPI-блок удаляется из overview-композиции.
|
||||||
|
- Блок `Доходы` удаляется из overview-композиции. Подробные доходные операции остаются доступны через
|
||||||
|
вкладку `Операции`.
|
||||||
|
- Существующая навигация счёта остаётся горизонтальными вкладками над dashboard-контентом.
|
||||||
|
- На desktop и mobile блоки идут одной колонкой в одинаковом порядке.
|
||||||
|
- Production UI не содержит demo-toggle `Данные / Загрузка`.
|
||||||
|
|
||||||
|
### 2. Заголовок счёта
|
||||||
|
|
||||||
|
- Заголовок показывает название счёта или fallback `Брокерский счёт`.
|
||||||
|
- Справа от заголовка, визуально меньшим блоком, показывается доходность счёта:
|
||||||
|
`Доходность`, значение процента и supporting text `За день: ...`.
|
||||||
|
- Доходность берётся из `analytics.totalReturnPercent`, если доступна, иначе из
|
||||||
|
`portfolio.yields.expectedPercent`, иначе отображается `—`.
|
||||||
|
- Дневное изменение берётся из `portfolio.yields.daily`, иначе отображается `—`.
|
||||||
|
- Отрицательная доходность окрашивается красным, положительная зелёным, недоступная нейтральным.
|
||||||
|
- В loading-состоянии заголовок не показывает skeleton-полосы. Layout должен сохранять стабильную
|
||||||
|
высоту и не прыгать после загрузки.
|
||||||
|
|
||||||
|
### 3. Стоимость портфеля за 6 месяцев
|
||||||
|
|
||||||
|
- Первый dashboard-блок называется `Стоимость портфеля за 6 месяцев`.
|
||||||
|
- Карточка использует зелёный градиентный фон и тонкую зелёную рамку как в HTML-эталоне.
|
||||||
|
- В карточке отображаются:
|
||||||
|
- label `Текущая стоимость`;
|
||||||
|
- текущая стоимость портфеля из `portfolio.totals.portfolio`;
|
||||||
|
- плавный line/area chart по 6 месячным точкам.
|
||||||
|
- График показывает ровно 6 подписей месяцев и 6 значений, соответствующих этим месяцам.
|
||||||
|
- Первая точка линии начинается у левого края области графика, последняя — у правого края.
|
||||||
|
- Visible point markers не отображаются; линия плавная.
|
||||||
|
- Подписи месяцев не выходят за границы карточки.
|
||||||
|
- Loading-состояние графика использует chart-like indicator без skeleton-подписей месяцев.
|
||||||
|
- Высота карточки в loading и loaded состояниях должна совпадать.
|
||||||
|
|
||||||
|
### 4. Аналитика доходности
|
||||||
|
|
||||||
|
- Блок называется `Аналитика доходности`.
|
||||||
|
- Верхний summary-row содержит только:
|
||||||
|
- `Стоимость портфеля`;
|
||||||
|
- `Всего доходов`.
|
||||||
|
- Detail-grid содержит ровно:
|
||||||
|
- `Пополнения`;
|
||||||
|
- `Выводы`;
|
||||||
|
- `Дивиденды`;
|
||||||
|
- `Купоны`;
|
||||||
|
- `Комиссия`;
|
||||||
|
- `Уплаченные налоги`.
|
||||||
|
- Поля `Нетто` и `Всего получено` не отображаются в overview-карточке.
|
||||||
|
- RUB отображается как `₽`, а не `RUB`.
|
||||||
|
- Положительные финансовые значения окрашиваются зелёным, отрицательные/уменьшающие баланс —
|
||||||
|
красным.
|
||||||
|
- `Комиссия` и `Уплаченные налоги` отображаются как отрицательные UI-суммы, даже если backend хранит
|
||||||
|
агрегат абсолютным положительным числом.
|
||||||
|
- Loading-состояние использует skeleton-карточки той же геометрии, чтобы интерфейс не прыгал.
|
||||||
|
|
||||||
|
### 5. Структура
|
||||||
|
|
||||||
|
- Блок называется `Структура`.
|
||||||
|
- Под заголовком показывается итоговая стоимость портфеля.
|
||||||
|
- Под итогом отображаются горизонтальные бары:
|
||||||
|
- `Акции`;
|
||||||
|
- `Облигации`;
|
||||||
|
- `Деньги`.
|
||||||
|
- Каждая строка показывает название сектора, сумму и процент.
|
||||||
|
- Цвета соответствуют HTML-эталону: акции — синий, облигации — янтарный, деньги — фиолетовый.
|
||||||
|
- Если значение сектора отсутствует или равно нулю, строка остаётся читаемой и не ломает layout.
|
||||||
|
- Общая карточка не содержит subtitle `Структура портфеля`.
|
||||||
|
|
||||||
|
### 6. Последние события
|
||||||
|
|
||||||
|
- Блок называется `Последние события`.
|
||||||
|
- Блок показывает только уже произошедшие записи, которые меняют или отражают денежный поток счёта.
|
||||||
|
- Источник данных — executed broker operations с категориями `income`, `tax`, `fee`.
|
||||||
|
- Записи сортируются от новых к старым.
|
||||||
|
- В overview показывается не более 7 последних записей.
|
||||||
|
- Таблица содержит колонки:
|
||||||
|
- `Дата`;
|
||||||
|
- `Инструмент`;
|
||||||
|
- `Тип`;
|
||||||
|
- `Сумма`.
|
||||||
|
- Колонка `Статус` не отображается.
|
||||||
|
- Toolbar фильтров, count badge и footer summary не отображаются в overview-карточке.
|
||||||
|
- В колонке `Инструмент` название отображается сверху жирным, ticker/ISIN снизу серым.
|
||||||
|
- Для операций без названия основная строка берётся из ticker/figi/description без дублирования в subtitle.
|
||||||
|
- Тип операции отображается бейджем: `Дивиденд`, `Купон`, `Погашение`, `Налог`, `Комиссия` или
|
||||||
|
безопасный fallback.
|
||||||
|
- Всё, что уменьшает баланс, отображается красным: сумма, типовой бейдж и tone строки/значения.
|
||||||
|
- Для налоговых операций в колонке `Тип` должен быть бейдж `Налог`, а не исходный income/coupon label.
|
||||||
|
- Блок содержит ссылку `Все события` на подробную вкладку `/broker/:accountId/events` или согласованный
|
||||||
|
detailed cashflow route, если реализация выделит его отдельно.
|
||||||
|
- Ошибка загрузки последних событий не ломает остальные блоки.
|
||||||
|
|
||||||
|
### 7. Backend API
|
||||||
|
|
||||||
|
- `BrokerAnalyticsDto` расширяется полями:
|
||||||
|
- `totalFees: number`;
|
||||||
|
- `totalTaxesPaid: number`.
|
||||||
|
- `totalFees` считается из executed broker operations категории `fee`.
|
||||||
|
- `totalTaxesPaid` считается из executed broker operations категории `tax`.
|
||||||
|
- Оба агрегата возвращаются абсолютными положительными числами; frontend отвечает за знак отображения.
|
||||||
|
- Добавляется endpoint:
|
||||||
|
`GET /api/v1/broker/accounts/:accountId/portfolio/history?months=6`.
|
||||||
|
- Endpoint возвращает envelope с данными:
|
||||||
|
- `accountId: string`;
|
||||||
|
- `points: Array<{ month: string; label: string; value: BrokerMoneyDto }>`;
|
||||||
|
- `asOf: string`.
|
||||||
|
- `month` имеет формат `YYYY-MM`.
|
||||||
|
- `label` содержит короткое русское имя месяца для оси.
|
||||||
|
- `points.length` равен запрошенному `months`, по умолчанию 6.
|
||||||
|
- Для v1 допускается estimated read-model из текущей стоимости портфеля и executed operations. Shape
|
||||||
|
контракта должен позволять позже заменить расчёт на реальные snapshots без изменения frontend.
|
||||||
|
- `BrokerOperationQueryDto` расширяется фильтром `categories?: string`.
|
||||||
|
- `categories` принимает comma-separated категории из существующего набора:
|
||||||
|
`trade,income,tax,fee,transfer,other`.
|
||||||
|
- `GET /broker/accounts/:accountId/operations` применяет `categories` до пагинации/лимита, чтобы overview
|
||||||
|
не фильтровал неполную страницу на клиенте.
|
||||||
|
|
||||||
|
### 8. Загрузка, ошибки и пустые состояния
|
||||||
|
|
||||||
|
- Ошибка portfolio остаётся page-level ошибкой.
|
||||||
|
- Ошибка analytics, history или latest events отображается только внутри соответствующей карточки.
|
||||||
|
- Недоступные значения отображаются как `—`, не подменяются нулём.
|
||||||
|
- Loading-состояния должны сохранять высоты карточек и не вызывать layout shift.
|
||||||
|
- На mobile не должно быть общего горизонтального overflow. Горизонтальный scroll допустим только внутри
|
||||||
|
таблицы, если без него невозможно сохранить читаемость.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- `/broker/:accountId` отображает блоки в порядке:
|
||||||
|
`Стоимость портфеля за 6 месяцев`, `Аналитика доходности`, `Структура`, `Последние события`.
|
||||||
|
- Верхняя строка страницы показывает `Брокерский счёт` и доходность рядом с ним, без отдельного hero.
|
||||||
|
- Production UI не показывает demo-toggle `Данные / Загрузка`.
|
||||||
|
- График стоимости имеет 6 подписей месяцев и 6 точек данных; линия начинается слева и заканчивается
|
||||||
|
справа.
|
||||||
|
- Loading графика не содержит skeleton-подписей месяцев и имеет ту же высоту, что loaded состояние.
|
||||||
|
- Analytics overview показывает `Комиссия` и `Уплаченные налоги`, не показывает `Нетто` и
|
||||||
|
`Всего получено`.
|
||||||
|
- `Структура` показывает итоговую стоимость и бары `Акции`, `Облигации`, `Деньги`.
|
||||||
|
- `Последние события` показывает только произошедшие записи, отсортированные новые → старые.
|
||||||
|
- Таблица `Последние события` не содержит toolbar, count badge, footer summary и колонку `Статус`.
|
||||||
|
- Операции налогов/комиссий и другие списания отображаются красным.
|
||||||
|
- Backend Swagger содержит новые analytics fields, portfolio history endpoint и `categories` query.
|
||||||
|
- Frontend generated types обновлены через codegen.
|
||||||
|
- Desktop и mobile визуально соответствуют `docs/research/frontend-overview-redesign/example.html`.
|
||||||
117
docs/features/broker-account-overview-html-parity/tasks.md
Normal file
117
docs/features/broker-account-overview-html-parity/tasks.md
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
# Финальный редизайн брокерского overview по HTML — задачи
|
||||||
|
|
||||||
|
Дата: 2026-06-27
|
||||||
|
Статус: документация подготовлена, реализация не начата
|
||||||
|
|
||||||
|
## Документация и pre-flight
|
||||||
|
|
||||||
|
- [x] Создать `docs/features/broker-account-overview-html-parity/spec.md`.
|
||||||
|
- [x] Создать `docs/features/broker-account-overview-html-parity/plan.md`.
|
||||||
|
- [x] Создать `docs/features/broker-account-overview-html-parity/tasks.md`.
|
||||||
|
- [x] Зафиксировать canonical visual reference:
|
||||||
|
`docs/research/frontend-overview-redesign/example.html`.
|
||||||
|
- [x] Зафиксировать, что `docs/research/2026-06-27-broker-account-redesign.html` не является
|
||||||
|
source of truth для этой итерации.
|
||||||
|
- [x] Зафиксировать финальный порядок блоков:
|
||||||
|
`Заголовок счёта → Стоимость портфеля за 6 месяцев → Аналитика доходности → Структура → Последние события`.
|
||||||
|
- [x] Зафиксировать, что production UI не переносит demo-toggle `Данные / Загрузка`.
|
||||||
|
- [ ] Перед началом реализации убедиться, что работа идёт в feature branch.
|
||||||
|
- [ ] Перед началом реализации запустить baseline checks текущей ветки.
|
||||||
|
|
||||||
|
## Backend contract
|
||||||
|
|
||||||
|
- [ ] Расширить `BrokerAnalyticsDto` полями `totalFees` и `totalTaxesPaid`.
|
||||||
|
- [ ] Обновить `BrokerAnalyticsService`: считать комиссии из executed операций категории `fee`.
|
||||||
|
- [ ] Обновить `BrokerAnalyticsService`: считать уплаченные налоги из executed операций категории `tax`.
|
||||||
|
- [ ] Обновить `broker-analytics.service.spec.ts` для новых агрегатов и округления.
|
||||||
|
- [ ] Добавить DTO для `BrokerPortfolioHistoryData`.
|
||||||
|
- [ ] Добавить envelope DTO для portfolio history endpoint.
|
||||||
|
- [ ] Добавить `BrokerPortfolioHistoryService`.
|
||||||
|
- [ ] Добавить endpoint `GET /api/v1/broker/accounts/:accountId/portfolio/history?months=6`.
|
||||||
|
- [ ] Покрыть portfolio history default months и response shape тестами.
|
||||||
|
- [ ] Добавить `categories?: string` в `BrokerOperationQueryDto`.
|
||||||
|
- [ ] Обновить `BrokerOperationsService`: применять category filtering для operations response.
|
||||||
|
- [ ] Покрыть `categories=income,tax,fee` и неизвестные категории тестами.
|
||||||
|
- [ ] Обновить `TBankController` и `tbank.controller.spec.ts` под новый endpoint/DTO.
|
||||||
|
|
||||||
|
## OpenAPI и frontend data layer
|
||||||
|
|
||||||
|
- [ ] Запустить backend dev server для Swagger JSON.
|
||||||
|
- [ ] Выполнить `npm run codegen -w apps/frontend`.
|
||||||
|
- [ ] Проверить, что generated types содержат `totalFees`, `totalTaxesPaid` и portfolio history schemas.
|
||||||
|
- [ ] Экспортировать новый `BrokerPortfolioHistory` alias из `apps/frontend/src/shared/api/index.ts`.
|
||||||
|
- [ ] Добавить `getBrokerPortfolioHistory`.
|
||||||
|
- [ ] Добавить `useBrokerPortfolioHistory`.
|
||||||
|
- [ ] Добавить `categories` в frontend `BrokerOperationQuery`.
|
||||||
|
- [ ] Не редактировать `apps/frontend/src/shared/api/types.ts` вручную.
|
||||||
|
|
||||||
|
## Frontend composition
|
||||||
|
|
||||||
|
- [ ] Перестроить `BrokerDashboard` на финальный порядок блоков.
|
||||||
|
- [ ] Убрать `BrokerDashboardHero` из overview-render path.
|
||||||
|
- [ ] Перенести compact yield UI в заголовок счёта рядом с `Брокерский счёт`.
|
||||||
|
- [ ] Убедиться, что page title loading state не показывает skeleton-полосы.
|
||||||
|
- [ ] Создать `BrokerPortfolioHistoryCard`.
|
||||||
|
- [ ] Подключить `useBrokerPortfolioHistory(accountId, { months: 6 })`.
|
||||||
|
- [ ] Заменить overview `BrokerDashboardIncomeCard` на `BrokerPortfolioHistoryCard`.
|
||||||
|
- [ ] Обновить `BrokerDashboardSkeleton` под финальный порядок и стабильные высоты.
|
||||||
|
|
||||||
|
## Frontend visual parity
|
||||||
|
|
||||||
|
- [ ] Карточка `Стоимость портфеля за 6 месяцев`: зелёный градиентный фон и зелёная рамка.
|
||||||
|
- [ ] График стоимости: 6 месячных значений, 6 подписей месяцев, плавная линия без visible markers.
|
||||||
|
- [ ] График стоимости: первая точка у левого края, последняя у правого края.
|
||||||
|
- [ ] Loading графика: chart-like indicator без skeleton месяцев.
|
||||||
|
- [ ] Analytics summary: только `Стоимость портфеля` и `Всего доходов`.
|
||||||
|
- [ ] Analytics detail grid: `Пополнения`, `Выводы`, `Дивиденды`, `Купоны`, `Комиссия`,
|
||||||
|
`Уплаченные налоги`.
|
||||||
|
- [ ] Analytics overview не показывает `Нетто` и `Всего получено`.
|
||||||
|
- [ ] `Комиссия` и `Уплаченные налоги` отображаются как отрицательные UI-суммы.
|
||||||
|
- [ ] Карточка структуры называется `Структура`.
|
||||||
|
- [ ] Карточка структуры не показывает subtitle `Структура портфеля`.
|
||||||
|
- [ ] Карточка структуры показывает итоговую стоимость под заголовком.
|
||||||
|
- [ ] Карточка структуры показывает бары `Акции`, `Облигации`, `Деньги`.
|
||||||
|
- [ ] Карточка последних событий называется `Последние события`.
|
||||||
|
- [ ] Последние события используют executed operations, а не calendar events.
|
||||||
|
- [ ] Последние события отсортированы новые → старые.
|
||||||
|
- [ ] Последние события не показывают toolbar, count badge, footer summary и колонку `Статус`.
|
||||||
|
- [ ] Инструмент в последних событиях: название сверху жирным, ticker/ISIN снизу серым.
|
||||||
|
- [ ] Налоги/комиссии/списания отображаются красным и с корректным бейджем типа.
|
||||||
|
- [ ] На mobile нет page-level horizontal overflow.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
- [ ] Backend targeted:
|
||||||
|
`npm run test -w apps/backend -- src/modules/tbank/services/broker-analytics.service.spec.ts src/modules/tbank/services/broker-operations.service.spec.ts src/modules/tbank/tbank.controller.spec.ts`
|
||||||
|
- [ ] Frontend targeted:
|
||||||
|
`npm run test -w apps/frontend -- --run src/widgets/broker-dashboard`
|
||||||
|
- [ ] Full frontend:
|
||||||
|
`npm run test:frontend`
|
||||||
|
- [ ] Frontend lint:
|
||||||
|
`npm run lint -w apps/frontend`
|
||||||
|
- [ ] Frontend build:
|
||||||
|
`npm run build:frontend`
|
||||||
|
- [ ] Проверить OpenAPI/codegen после backend изменений.
|
||||||
|
|
||||||
|
## Visual QA
|
||||||
|
|
||||||
|
- [ ] Проверить `/broker/:accountId` на desktop против
|
||||||
|
`docs/research/frontend-overview-redesign/example.html`.
|
||||||
|
- [ ] Проверить `/broker/:accountId` на viewport `390x844`.
|
||||||
|
- [ ] Проверить, что loading/loaded высоты карточек не вызывают layout shift.
|
||||||
|
- [ ] Проверить, что подписи месяцев графика не выходят за границы карточки.
|
||||||
|
- [ ] Проверить, что таблица последних событий читаема на mobile.
|
||||||
|
|
||||||
|
## Definition of Done
|
||||||
|
|
||||||
|
- [ ] Все acceptance criteria из `spec.md` выполнены.
|
||||||
|
- [ ] Backend tests проходят.
|
||||||
|
- [ ] Frontend targeted tests проходят.
|
||||||
|
- [ ] `npm run test:frontend` проходит.
|
||||||
|
- [ ] `npm run lint -w apps/frontend` проходит.
|
||||||
|
- [ ] `npm run build:frontend` проходит.
|
||||||
|
- [ ] Generated OpenAPI types обновлены через codegen.
|
||||||
|
- [ ] Visual QA desktop/mobile выполнена.
|
||||||
|
- [ ] Существующие detailed вкладки `Акции`, `Облигации`, `Операции`, `События`, `Аналитика`
|
||||||
|
остаются доступны.
|
||||||
|
- [ ] `tasks.md` обновлён по факту выполнения.
|
||||||
Loading…
x
Reference in New Issue
Block a user