330 lines
17 KiB
Markdown
330 lines
17 KiB
Markdown
# Финальный 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.
|