212 lines
22 KiB
Markdown
212 lines
22 KiB
Markdown
# 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.
|
|
|
|
- **Семантика списка.** Внешний список — `<ul>`; каждая одиночная запись и каждая группа — `<li>`.
|
|
Внутри группы-`<li>` заголовок (обычный блок, не `<h*>`, чтобы не засорять навигацию по
|
|
заголовкам страницы) и вложенный `<ul>` со строками-`<li>`. Группы в обзоре — внутри уже
|
|
существующих секций с их заголовками.
|
|
- **Доступное имя группы.** Вложенный `<ul>` получает `aria-label` вида: «Купон, X, 12 мая 2026,
|
|
итог +300 RUB» (дата, инструмент, тип, итог, мультивалютный итог — через запятую). При скрытом
|
|
итоге — «… итог недоступен». Для знаков в озвучке: «плюс 300 рублей», «минус …» — через
|
|
`aria-label` значения либо скрытый текст; сам знак в тексте остаётся.
|
|
- **Итог скрыт.** Визуально значения нет; в DOM есть `visually-hidden` текст «Итог недоступен» и
|
|
он включён в имя группы. Причина скрытия `partial` уже объявляется существующим `Alert role="status"`.
|
|
- **Фокус.** Заголовок и строки группы не фокусируемы: новых интерактивных элементов нет. Если одиночная карточка содержит ссылку, она остаётся как есть, а порядок Tab совпадает с порядком DOM.
|
|
- **Не только цвет.** Направление передаёт знак «+»/«−» (минус как «−»), подпись типа и слова
|
|
«Прогноз»/«Исполнено»; цвет только усиливает. Контраст текста ≥ 4.5:1 (dimmed `gray.6` на `subtle`
|
|
проверить), границы групп ≥ 3:1 не требуются — это декор, смысл не теряется без границы.
|
|
- **Динамика.** Смена страницы журнала не перемещает фокус неожиданно; пагинация — как сейчас.
|
|
- **Масштаб.** Текст масштабируется до 200 % без потери информации (перенос строк).
|
|
|
|
## Presentation Decisions
|
|
|
|
Решения, которые относятся только к представлению и не меняют поведение по specs:
|
|
|
|
1. **Видимая причина скрытого итога.** Видимого маркера нет. Скринридер получает текст «Итог недоступен». Строки с суммами остаются видны, так что отсутствие итога не выглядит пустотой.
|
|
2. **Несколько валют в заголовке.** Каждая валюта на отдельной строке, в порядке первого появления в записях группы.
|
|
3. **Количество и цена в строках сделок.** Не показываются, как и в несгруппированном журнале. Specs требуют только счёт и денежное изменение.
|
|
4. **Ссылка на счёт.** Сохраняется текущее поведение: имя счёта — текст во всех четырёх списках.
|
|
5. **Подпись «Исполнено» / «Прогноз».** Ставится у итога в заголовке. В строках событий подпись остаётся у суммы, как в одиночной карточке.
|
|
6. **Дата реестра.** Применяется только к группам прогнозных событий. Исполненные выплаты датируются датой исполнения.
|
|
|
|
## Findings → Specs
|
|
|
|
Нет. Два пробела, найденных при проектировании, закрыты решением Change Owner и внесены в specs, G2 инвалидирован:
|
|
- прогнозный итог скрывается, если полнота прогноза не подтверждена;
|
|
- запись с нулевой суммой считается записью без денежного изменения и скрывает итог.
|