# UX Design Задача пользователя: в режиме «Все счета» (`/accounts/all/...`) быстро увидеть, что несколько записей — это один и тот же факт (один день, одна бумага, один тип), понять общую сумму и при этом сразу видеть вклад каждого счёта. Нормативное поведение (ключ группы, состав, порядок, правила итога) задано в `proposal.md` и трёх spec; этот документ его не повторяет и не меняет, а только определяет представление. Допущение: design system — Mantine + `@wealthledger/ui` по `docs/design/overview.md` (семантические роли, направление несут знак и текст, не только цвет). ## Requirements Covered - `brokerage-account-ledger` — `Full ledger presents compact rows` (MODIFIED, группа в полном журнале) - `brokerage-account-ledger` — `Account overview includes recent ledger events` (MODIFIED, группа в обзоре) - `brokerage-account-ledger` — `User can retrieve an account ledger` (MODIFIED, режим `all`, `partial`) - `brokerage-account-ledger` — `All-accounts ledger groups same-day entries of one instrument` - `brokerage-account-ledger` — `All-accounts ledger group shows each entry and an exact total` - `brokerage-account-ledger` — `All-accounts ledger grouping preserves order and limits` - `brokerage-account-upcoming-events` — `Account page presents an upcoming events list` (MODIFIED, прогнозный итог группы) - `brokerage-account-upcoming-events` — `All-accounts upcoming events group same-day events of one instrument` - `brokerage-account-upcoming-events` — `All-accounts upcoming event group shows each event and a forecast total` - `brokerage-account-upcoming-events` — `All-accounts upcoming event grouping preserves order and limits` - `brokerage-account-income-analytics` — `Realized income analytics is account-scoped and period-bound` (MODIFIED, режим `all`) - `brokerage-account-income-analytics` — `Income payments remain traceable to exact Ledger Entries` (MODIFIED, «Выплаты» в режиме `all`) ## User Flows Цель пользователя — «что и сколько пришло/придёт по бумаге X и с каких счетов». 1. **Просмотр списка.** Пользователь открывает любой из 4 списков в режиме «Все счета» → видит заголовок группы (дата, бумага, тип, итог) → читает строки счетов под ним, раскрывать ничего не нужно. 2. **Переход к счёту.** Из строки группы (журнал, выплаты) пользователь открывает `/accounts/{id}` по имени счёта; возврат — кнопкой «Назад» браузера, список и позиция восстанавливаются как сейчас. 3. **Листание журнала.** «Назад/Вперёд» меняют страницу; группа собирается заново из показанных записей страницы. Пользователь не получает пометок о неполноте на границе страницы. 4. **Обзор.** На `/accounts/all` в блоках «Ближайшие события» и «Последние операции» группа занимает место первой записи; блок по-прежнему содержит не более 5 записей/событий, как сейчас. 5. **Одиночная запись / одиночный счёт.** Запись без пары, без Instrument или любой список в режиме одного счёта выглядят как сейчас; пользовательский путь не меняется. Ветвление по итогу: итог известен → показан в заголовке; итог скрыт (см. UI States) → пользователь складывает строки сам или смотрит на состояние страницы (`partial`-Alert). ## Information Architecture Группа — один визуальный блок (рамка `Paper withBorder`) внутри существующего списка, на месте первой записи. Состав блока: заголовок + вложенный список строк счетов. Заголовок повторяет композицию текущей карточки (иконка/логотип | название и метаданные | значение), строка счёта — компактнее, без логотипа. Лейблы заголовка: заголовок = «`<тип>` · `<инструмент>`» (как в карточках событий/выплат), метаданные = дата (+ «дата реестра» для дивиденда по реестру), значение = итог, подпись значения = подпись типа (в событиях — «Прогноз»). ### Журнал (`/accounts/all/operations`, обзор «Последние операции») ``` ┌──────────────────────────────────────────────────────────┐ │ [logo] Купон · X +300 RUB │ заголовок │ 12 мая 2026 Купон │ │ ──────────────────────────────────────────────────────── │ │ Брокерский +200 RUB Купон │ строка │ ИИС +100 RUB Купон │ строка └──────────────────────────────────────────────────────────┘ ``` Строка: имя счёта слева; справа денежное изменение со знаком и подпись типа (по `Full ledger presents compact rows`). Количество и цена в строках группы не показываются (Presentation Decisions, п. 3). Имя счёта — обычный текст во всех списках, как в текущей метадате строки (`withDetails` в этих списках не используется). ### События (`/accounts/all/events`, обзор «Ближайшие события») ``` ┌──────────────────────────────────────────────────────────┐ │ [logo] Дивиденд · X 10 USD │ │ 12 мая 2026 · дата реестра 200 RUB │ итоги по валютам │ Прогноз │ │ ──────────────────────────────────────────────────────── │ │ Брокерский 10 USD │ │ ИИС Сумма неизвестна│ └──────────────────────────────────────────────────────────┘ ``` Пример выше показывает два разных состояния сразу; на практике итог при строке «Сумма неизвестна» скрыт (см. UI States). Строки счёта в событиях — только текст (ссылки на счёт в текущей карточке события нет). ### Выплаты (`/accounts/all/income`) ``` ┌──────────────────────────────────────────────────────────┐ │ [logo] Купон · X +300 RUB │ │ Исполнено 12 мая 2026 Купон │ │ ──────────────────────────────────────────────────────── │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ Брокерский +200 RUB │ │ отдельная карточка │ └──────────────────────────────────────────────────────┘ │ внутри группы │ ┌──────────────────────────────────────────────────────┐ │ │ │ ИИС +100 RUB │ │ │ └──────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘ ``` В «Выплатах» spec требует «отдельную сразу видимую карточку» записи внутри группы: вложенные карточки без логотипа и с границей, но визуально легче внешней. Подпись «Исполнено» сохраняет различие факта и прогноза. Размещение в 4 списках: страница журнала и «Выплаты» — вертикальный стек с зазором `sm`; страница событий — тот же стек; блоки обзора — тот же компонент группы без дополнительной рамки блока. Позиция — по правилам порядка из spec (группа на месте первой записи). ## UI States | Состояние | Представление | | --- | --- | | Одиночная запись | Как сейчас: текущая карточка, без группы и вложенного списка | | Группа с итогом | Заголовок с итогом `+300 RUB` (тон по знаку), строки счетов | | Мультивалютный итог | В заголовке столбец значений: по строке на валюту, `+150 RUB` над `+10 USD`; порядок — по первому появлению валюты в записях группы (Presentation Decisions, п. 2); без конвертации и общей суммы | | Итог скрыт | Значение в заголовке отсутствует; строки счетов видны. Причина: запись без суммы / событие с неизвестной суммой / период `partial`. Для экранных дикторов — скрытый текст «Итог недоступен» (без различения причин) | | Нулевой итог | `0 RUB` без знака, нейтральный тон; строки с их знаками | | Offer без сумм | Группа без итога; строки: имя счёта + «Сумма не определена» (существующий текст `events.noAmount`) | | Прогноз | Итог и суммы строк в событиях несут подпись «Прогноз» (существующий `payout.forecast`); тон нейтральный, как у текущей карточки прогноза | | Строка без денег (ledger) | Нейтральный текст `payout.noMoney`/«без движения денег» как в одиночной записи; итог группы скрыт | | Обзор, 5 записей | Не более 5 записей/событий, как сейчас; группа может занимать часть из них, например 2 из 5; кнопка «Все …» ведёт на полную страницу | | Граница страницы журнала | Группа из показанных записей, без пометки о неполноте и без «ещё N на другой странице» | | Loading / empty / error / partial-Alert | Не меняются: `LoadingSkeleton`, `StateView`, `Alert` и `SourceSyncStatus` из существующих страниц; группы строятся только для загруженных данных | Знак: «+» и «−» всегда в тексте; цвет (positive/negative/neutral) — дополнение. ## Visual Package Решение: **reuse + один новый тонкий контейнер**. Изображения (PNG/SVG) не создаются: изменение — повторная композиция существующих карточек, а структура и тексты описаны текстовыми макетами выше; отдельный макет дал бы расхождение с живыми компонентами и токенами Mantine. Переиспользуется: - `EntryCard` (`@wealthledger/ui`) — заголовок группы: логотип/инициал, `title`, `metadata` (дата), `value` (итог), `valueLabel` (тип/«Прогноз»), `tone`. Отличие: в `value` нужно выводить несколько значений для нескольких валют (сейчас одна строка) и допустить отсутствие значения. - `LedgerEntryRow` / `PayoutCard` (executed) — единичные записи и карточки внутри группы выплат; в группе опускаются логотип, дата и название инструмента (они в заголовке). - `BrokerageEventCard` / `PayoutCard` (forecast) — единичные события; тексты `events.type.*`, `events.noAmount`, `payout.forecast`. - Форматирование: `formatMoney`, `formatDate`, `instrumentLogoUrl`; тексты `ledger.noInstrument`, `payout.noMoney`. - Токены: `--wl-color-border`, `--wl-color-subtle`, `--wl-color-surface`, `Paper withBorder`. Новое (предложение, с обоснованием «нужно показать иерархию заголовок → строки»): - Контейнер группы (рамка, заголовок, вложенный список) и компактная строка счёта (имя счёта слева, сумма справа, без `Avatar`). `EntryCard` всегда рисует `Avatar`, поэтому для строки счёта нужна его облегчённая форма либо отдельная разметка; выбор — за реализацией. Отступ строк слева — под текстовым блоком заголовка (≈ 40 px аватар + `sm`), либо без отступа на узкой ширине. ## Responsive - Базовый макет — mobile-first: заголовок — сетка из трёх колонок как `EntryCard` (`auto | 1fr | auto`). - **≥ 768 px**: заголовок в одну строку; строки счетов — имя слева, сумма и подпись справа. - **< 576 px (узкий мобильный)**: колонка значения заголовка переносится под название (название и метаданные — вверху, итог — следующей строкой справа/слева), мультивалютный итог — столбцом; название инструмента и имя счёта переносятся (`wrap`, без `lineClamp`), отступ строк слева убирается. Строка счёта: имя счёта сверху, сумма с подписью типа ниже, если вместе не помещаются; длинное имя счёта не обрезается. - Никакого горизонтального скролла; минимальная ширина — 320 px. - Ввод: touch и мышь; в группе нет новых целей нажатия. ## Accessibility Ориентир — WCAG 2.2 AA. - **Семантика списка.** Внешний список — `