Files
specs/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/design.md
T
ksv741 678a671d23 feat(portfolio-view): sync delta spec and archive change
- Remove old combined requirement, add two focused requirements:
  'Состояния и ошибки Portfolio на клиенте' and
  'Представление карточек позиций на клиенте'
- Archive extend-instrument-logo-to-bonds change
2026-08-25 09:48:21 +03:00

59 lines
7.6 KiB
Markdown

# extend-instrument-logo-to-bonds — Design
## Context
Данные уже готовы: `logoName` приходит в `PortfolioPositionDto` для позиций любого типа (passthrough из T-Bank metadata, без персистенции), фронтенд строит CDN-URL через `instrumentLogoUrl` и рендерит логотип компонентом `PositionLogo` (40px, фолбэк первой буквой через `onError`). Ограничение «только акции» существует в одном месте представления страницы счёта.
Осложнение: локальный проп `stock` на `PositionSection`/`PositionCard` управляет сразу тремя аспектами — одноколоночной раскладкой grid, рендером логотипа и форматом абсолютного дохода (знаковое значение против текстовой подписи). Паритет облигаций означает, что все три аспекта применяются к секции `BOND` целиком; менять их по отдельности не нужно.
Ограничения репозитория: FSD-границы и public API (dependency-cruiser, Knip), только typed client поверх generated OpenAPI, i18n через локали, gate `npm ci && npm run verify` под Node 22.
## Goals / Non-Goals
**Goals:**
- Секции `BOND` получают полное представление плиток `STOCK`: одна карточка в ряду, визуальный логотип с буква-фолбэком, знаковый доход без подписи.
- Минимальный диф в слое представления страницы счёта; переиспользование существующих `instrumentLogoUrl` и `PositionLogo` без изменений.
- Тесты, зеркалирующие существующие сценарии акций для облигаций.
**Non-Goals:**
- Логотипы для общей секции иных типов (фондов, валют и т.д.) — гейт остаётся allowlist по типам секций.
- Изменения backend, API-контракта, generated types и OpenAPI snapshot.
- Экстракция `PositionLogo`/`instrumentLogoUrl` в entities ради переиспользования — потребителя по-прежнему один экран.
- Логотипы на странице upcoming events; понятие «эмитент» в модели.
## Decisions
**D1. Разъединить смысл флага, сохранив один проп.** Проп `stock` переименовывается в презентационный термин (например, `wide`), отражающий «широкая плитка во всю строку + логотип + знаковый доход». Секции `STOCK` и `BOND` передают его, общая секция иных типов — нет.
*Alternative:* два независимых пропа (`onePerRow`, `withLogo`). Отклонено: сегодня они всегда движутся вместе; разъединять, пока не появился тип, которому нужен только один аспект, — спекуляция.
**D2. Логотип без изменений.** `PositionLogo` и `instrumentLogoUrl` реиспользуются как есть, включая `key={logoSrc ?? "no-logo"}` для сброса failed-state между карточками и CDN-размер 160 для 40px аватара. Новых настроек размера/радиуса не вводится.
**D3. Формат доходности следует за флагом.** Знаковый формат без подписи применяется к `STOCK` и `BOND`, текстовая подпись остаётся только у секции иных типов — как прямое следствие D1, без отдельной ветки по типу.
**D4. Тесты повторяют паттерны акций.** В тестах страницы счёта добавляются случаи: облигация с `logoName` рендерит изображение с брендовым URL; облигация без `logoName` — буква-фолбэк без img; знаковый доход облигации без текстовой подписи; одноколоночная секция облигаций. Существующие тесты секции иных типов служат регрессионной сеткой неизменённого пути.
## Delivery Map
| Area | Status | Architectural outcome or N/A reason |
| --- | --- | --- |
| frontend | `affected` | Представление страницы счёта: единый презентационный флаг для секций `STOCK` и `BOND`; логотип и знаковый доход на карточках облигаций; обновлённые unit/component тесты |
| backend | `N/A` | `logoName` уже возвращается для всех типов позиций; контракт Portfolio и зависимости не меняются |
| specs | `affected` | Утверждённые Delta Specs синхронизируются в главную спеку `portfolio-view` при archive после реализации |
| docs | `affected` | As-built: `docs/features/portfolio-view.md` отражает паритет облигаций; глоссарий не меняется (термин Instrument Logo уже типо-агностичен) |
### Contract Gate Decision
| Gate | Status | Reason |
| --- | --- | --- |
| runtime-openapi | `N/A` | API-контракт не меняется ни на одной стороне: backend вне scope, generated types не обновляются |
## Risks / Trade-offs
- **Редкие бренды у облигаций.** Брендовые данные T-Bank для облигаций встречаются реже, чем у акций, поэтому буква-фолбэк будет видна чаще. Mitigation: фолбэк — штатное, уже протестированное поведение; спецификация явно допускает оба исхода.
- **Связанность трёх аспектов представления.** Общий флаг означает: нельзя включить логотип, не сделав плитку широкой. Осознанный trade-off ради минимального дифа; точка разъединения обозначена в D1.
- **Визуальная регрессия других секций.** Путь секции иных типов не меняется (ветка по умолчанию того же флага); риск ограничен одним файлом представления и покрыт существующими тестами.
## Migration Plan
Поставка одним изменением frontend без флагов миграции: изменение чисто презентационное, состояние и данные не затрагиваются. Rollback — revert коммита frontend. Порядок: имплементация → `npm ci && npm run verify` → semantic verification против Proposal/Delta Specs/Design → as-built docs → workspace gate `verify-workspace-change.sh` → archive.