spec: add quiet user feedback change and sync canonical specs
Part of the quiet-user-feedback change; frontend and docs land in their own repositories.
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: wealth-ledger-sdd
|
||||
created: 2026-10-04
|
||||
@@ -0,0 +1,424 @@
|
||||
## Context
|
||||
|
||||
Frontend уже разделяет получение данных, page VM и presentation. TanStack
|
||||
Query хранит успешные результаты; инвестиционные ресурсы дополнительно
|
||||
возвращают `data` и `meta` с полнотой, свежестью и состоянием источника.
|
||||
`useResourceQuery` сохраняет общую query identity обычного чтения и наблюдения,
|
||||
а `useScreenSync` наблюдает известную работу и предоставляет ручные действия.
|
||||
На счетах и портфеле используются `InvestmentRefreshState` и
|
||||
`SourceSyncStatus`; сходные элементы встроены в остальные finance views.
|
||||
|
||||
Сейчас query retries и focus refetch отключены, ошибка status GET прекращает
|
||||
наблюдение, а `observe()` также запрещает retry. В банковских формах отдельно
|
||||
существуют технические повторы загрузки справочников и перечитывание записи
|
||||
после конфликта версий. Это разные случаи: удаление всех обработчиков с именем
|
||||
`retry` или `onReload` без сохранения их ответственности недопустимо.
|
||||
|
||||
Предоставленный frontend OpenAPI уже содержит обычное чтение ресурсов,
|
||||
`sync=observe`, status GET и принудительный screen refresh. Дополнительная
|
||||
сверка реализации показала: обычное чтение запрашивает missing/stale источники,
|
||||
свежие полные источники переиспользуются, существующая running work объединяется.
|
||||
Принудительный refresh обходит freshness и охватывает зависимости экрана.
|
||||
Поэтому он не подходит для автоматического восстановления обычного чтения.
|
||||
|
||||
Основные specs текущего store отстают от части реализованной синхронизации.
|
||||
Есть пересечение с active change `persist-tbank-data`; его старые UI-требования
|
||||
не используются как новая норма поверх согласованных quiet Delta Specs.
|
||||
Это различие учитывается в порядке согласования требований ниже.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Сосредоточить transport recovery в существующем query layer, а восстановление
|
||||
инвестиционной работы — у существующего владельца `useScreenSync`.
|
||||
- Упростить presentation interface: view получает готовое содержимое,
|
||||
недоступность или необходимое пользовательское действие, а не набор
|
||||
технических статусов и callbacks повторной загрузки.
|
||||
- Сохранить один финансовый кэш, действующую отмену запросов, границы доступа
|
||||
и точные серверные финансовые результаты.
|
||||
- Встроить редкие уведомления в существующие Mantine theme и i18n.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Новый query client, offline-хранилище, event bus или универсальный scheduler.
|
||||
- Автоматическое повторение мутаций, изменение auth refresh или правил
|
||||
разрешения конфликтов пользовательских данных.
|
||||
- Новые endpoints, удаление существующего refresh endpoint, изменение DTO,
|
||||
серверных TTL, алгоритмов расчёта или схемы хранения.
|
||||
- Переработка композиции финансовых страниц и диаграмм.
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Два владельца восстановления вместо одного глобального retry
|
||||
|
||||
`shared/api` владеет классификацией transport errors, ограниченным retry и
|
||||
deadline application GET. Существующие authenticated query hooks применяют
|
||||
общую политику к чтению карт, справочников и статуса подключения. Auth bootstrap
|
||||
и пользовательские mutations сохраняют текущую отдельную политику.
|
||||
|
||||
`useResourceQuery` использует тот же transport recovery, но жизненный цикл
|
||||
инвестиционной работы остаётся в `features/tbank-connection`. `useScreenSync`
|
||||
становится автоматической orchestration текущего экрана: его callers больше
|
||||
не передают `refresh`/`retry` в UI. Page VM продолжает владеть смыслом готовности
|
||||
конкретного результата, выбранными параметрами и доступными действиями.
|
||||
|
||||
Альтернативы: глобальный retry всех запросов и mutations нарушает границы
|
||||
записи и доступа; независимые timers каждой страницы размножают политику.
|
||||
Собственный универсальный recovery service дублирует Query lifecycle.
|
||||
|
||||
### 2. Конечная transport policy
|
||||
|
||||
Для application GET принимаются следующие параметры:
|
||||
|
||||
| Параметр | Решение |
|
||||
| --- | --- |
|
||||
| HTTP deadline | 15 секунд на попытку, включая чтение тела ответа |
|
||||
| Автоматические повторы | Не более двух после исходного запроса |
|
||||
| Паузы | 1 и 2 секунды; серия не перезапускается от render |
|
||||
| Временные ошибки | Подтверждённая transport/network failure, timeout, HTTP 408, 500, 502, 503, 504, `TBANK_UNAVAILABLE` |
|
||||
| Не повторять | Отмена, ошибки ввода, 401/403/404, известные access/domain failures, 409, неизвестный постоянный исход |
|
||||
| Rate limiting | 429 не повторяется короткой серией без доступного корректного `Retry-After`; наличие серверной отсрочки исключает более ранний повтор |
|
||||
| Новая серия | Новый актуальный запрос, возвращение сети или возвращение пользователя на экран |
|
||||
|
||||
Классификация использует существующие `ApiErrorCode` и структурированный
|
||||
Problem Details status, с приоритетом отказа доступа над общим HTTP status.
|
||||
Отсутствие известного code само по себе не означает временную ошибку.
|
||||
Transport failure маркируется там, где выполняется GET: ошибка render,
|
||||
`select` или преобразования данных не превращается в network failure.
|
||||
Если `Retry-After` используется, его transport metadata сохраняется локально
|
||||
в ошибке клиента, без нового поля публичного DTO.
|
||||
|
||||
Deadline применяется только к application GET, не к auth endpoints и не к
|
||||
mutations. Сигнал Query объединяется с сигналом timeout; исходная отмена
|
||||
сохраняет причину и не повторяется. Существующий единственный auth refresh
|
||||
не дублируется query retry. Восстановление GET после исчерпанного auth refresh
|
||||
не допускается до восстановления сессии.
|
||||
|
||||
Для обычных authenticated reads применяются штатные Query focus/reconnect
|
||||
events и текущие entity `staleTime`. На инвестиционных ресурсах обработка этих
|
||||
событий координируется `useScreenSync`: параллельный автоматический refetch
|
||||
тех же ресурсов из второго владельца выключен. Lifecycle события берутся
|
||||
из Query focus/online managers, без второго набора browser listeners.
|
||||
|
||||
Уход последнего consumer отменяет запрос и его retries через существующий
|
||||
AbortSignal. Imperative чтение, начатое screen owner, также прекращается
|
||||
при его cleanup, если у запроса не осталось других активных consumers.
|
||||
|
||||
### 3. Восстановление источника — обычным GET, наблюдение — только observe
|
||||
|
||||
Для инвестиционного ресурса разделяются три ситуации:
|
||||
|
||||
| Ситуация | Действие владельца |
|
||||
| --- | --- |
|
||||
| Не получен HTTP-ответ | Ограниченный transport retry того же режима чтения |
|
||||
| Известна running work | Status GET и чтение проекции с `sync=observe` |
|
||||
| Необходимые данные missing/stale, а work failed/cancelled либо отсутствует | Ограниченный повтор обычного GET ресурса без `sync=observe` |
|
||||
|
||||
Успешный HTTP `200` с `data: null` или `sync.state: failed` не считается
|
||||
успешным восстановлением источника. Источники определяются из `meta` и
|
||||
`meta.dependencies`; `unavailableReason` и предметные ограничения не считаются
|
||||
временным сбоем. Неудачная прежняя принудительная сверка уже полного свежего
|
||||
результата не запускается заново только ради удаления её внутреннего статуса.
|
||||
|
||||
После terminal source failure допускаются две повторные обычные загрузки
|
||||
нуждающегося в данных ресурса: через 5 и 15 секунд после соответствующего
|
||||
неуспеха. Бюджет относится к активации текущей выборки, а не к ID очередной
|
||||
серверной попытки. Новый ID работы, успешное чтение failed metadata и повторный
|
||||
render не обнуляют бюджет. После его исчерпания следующий запуск возможен
|
||||
при новом lifecycle событии восстановления или выборе новой выборки.
|
||||
|
||||
Transport retry и source recovery не вкладываются бесконтрольно: следующая
|
||||
source attempt начинается только после завершения предыдущего GET и его
|
||||
transport retries. Для одной серии это не более трёх обычных загрузок ресурса,
|
||||
каждая не более чем с тремя HTTP-попытками. Наблюдение running work не является
|
||||
новой source attempt и никогда не вызывает принудительный POST refresh.
|
||||
|
||||
Status observation сохраняет существующий интервал 1 секунда при активном
|
||||
экране и известной running work. Повторный tick не отменяет выполняющееся
|
||||
чтение или его retry. После terminal status выполняется заключительное
|
||||
observe-чтение; если другое чтение ещё идёт, заключительное выполняется после
|
||||
него, а не теряется. После ухода, потери сессии или terminal status периодический
|
||||
опрос соответствующей работы прекращается. В скрытой вкладке и offline
|
||||
новые циклы приостанавливаются; возвращение возобновляет актуальную работу.
|
||||
|
||||
`404` status ID не означает автоматически потерю доступа ко всем счетам:
|
||||
ресурс перечитывается по действующему адресу, где проверяется текущий доступ.
|
||||
Подтверждённые access errors ресурса либо статуса подключения сохраняют
|
||||
существующую invalidation личных данных. Коды отказа из metadata также не
|
||||
должны маскироваться успешным HTTP-ответом.
|
||||
|
||||
Обычное чтение и observe остаются в одной generated query identity. Внутри
|
||||
владельца режим запроса задаётся явно; observe не оставляет постоянный
|
||||
observe-only queryFn для последующего обычного восстановления. При чтении
|
||||
проверяется фактическое in-flight состояние Query, а не только снимок props.
|
||||
Повторный event не создаёт второй запрос того же key. Переход на новую выборку
|
||||
отменяет старые timers и не позволяет late response менять новую область.
|
||||
|
||||
### 4. UI readiness отдельно от query status
|
||||
|
||||
Page VM выбирает presentation в следующем порядке:
|
||||
|
||||
1. Подтверждённый access failure: скрытые личные данные и необходимое действие.
|
||||
2. Доступный результат текущей выборки: loaded, partial или подтверждённый empty.
|
||||
3. Отсутствие результата: первоначальный skeleton либо unavailable.
|
||||
|
||||
Loaded view не получает `isFetching`, время получения и callbacks технического
|
||||
повтора. Технические metadata остаются у data owner для восстановления и
|
||||
определения полноты. Не вводится второй кэш успешных финансовых payload.
|
||||
|
||||
Первичное ожидание области ограничивается 10 секундами. Ошибка завершившегося
|
||||
получения или истечение этого срока переводит её в unavailable. Это UI budget,
|
||||
а не отмена полезной source work: поздний успешный результат заменяет состояние
|
||||
автоматически. Одна общая небольшая presentation-функция/hook в `shared/lib`
|
||||
владеет временем ожидания и latch unavailable для identity области. Она
|
||||
не выполняет запросы, не знает T-Bank и не копирует финансовые данные.
|
||||
Page VM передаёт readiness и identity; смена выборки сбрасывает ожидание,
|
||||
очередная попытка той же выборки не вызывает мигание skeleton.
|
||||
|
||||
Доступность проверяется явно, а не через truthiness суммы. `data: []` и полный
|
||||
пустой Portfolio — успешные результаты; `data: null` не равен пустому результату.
|
||||
Для неполных totals/comparisons выводится `—` с доступной подписью
|
||||
«Итог пока недоступен»/«Сравнение пока недоступно». Для неполного списка
|
||||
допустима подпись «Показана часть операций», без статуса синхронизации.
|
||||
|
||||
Ключи loaded containers не зависят от `isFetching`, revision или времени ответа.
|
||||
Фильтры, локальные раскрытия, формы и focus сохраняют существующих владельцев.
|
||||
В банковской пагинации прежняя страница не используется как placeholder новой:
|
||||
текущий `placeholderData`, допускающий иной page, убирается для этого перехода.
|
||||
Сохранение данных при refetch той же страницы обеспечивается самим Query.
|
||||
|
||||
`InvestmentRefreshState` и `SourceSyncStatus` удаляются вместе с их callers
|
||||
и ненужными presentation props. Общие `StateView` и page skeleton переиспользуются;
|
||||
нейтральная недоступность не требует нового компонента-обёртки. Ошибки форм,
|
||||
валидация и сообщения, объясняющие финансовый смысл, не удаляются поиском строк.
|
||||
|
||||
### 5. Формы и конфликты сохраняют самостоятельное подтверждение
|
||||
|
||||
Запросы категорий, исходной покупки возврата и текущей версии записи получают
|
||||
обычную политику чтения. Временный refetch не очищает ранее доступные варианты
|
||||
и ввод. Когда необходимые данные отсутствуют, соответствующее сохранение
|
||||
недоступно; отмена и выход из формы остаются доступны.
|
||||
|
||||
После `BANK_VERSION_CONFLICT` или аналогичного предметного конфликта существующая
|
||||
invalidation автоматически получает актуальную запись. VM использует этот
|
||||
результат вместо отдельной кнопки перечитывания. Пользователь по-прежнему
|
||||
видит конфликт, сохраняет ввод и явно принимает новую версию перед повторным
|
||||
сохранением или удалением. Фоновый ответ не принимает версию за него.
|
||||
Пока новая версия неизвестна, повтор опасной записи заблокирован, но форма
|
||||
не находится в бесконечном submit и допускает закрытие.
|
||||
|
||||
Создание и правка карт/операций, удаление, категории и изменение подключения
|
||||
сохраняют `retry: false`, version и существующие intent IDs. Общая read policy
|
||||
не означает автоматического повторения неуспешной записи.
|
||||
|
||||
### 6. Mantine Notifications с явным владельцем сообщения
|
||||
|
||||
Используется `@mantine/notifications` **9.6.1**, согласованный с установленными
|
||||
`@mantine/core` и `@mantine/hooks` 9.6.1. Registry подтверждает эту версию и
|
||||
peer requirements React/React DOM `^19.2.0`, совместимые с manifest проекта.
|
||||
Стили пакета подключаются после Mantine core; один `Notifications` host
|
||||
находится в application composition под MantineProvider.
|
||||
|
||||
Параметры: `limit=2`, `autoClose=4000`, desktop `bottom-right`, narrow viewport
|
||||
`bottom-center`; видимая кнопка закрытия имеет русское доступное имя. Сообщение
|
||||
не получает отдельный заголовок «Успех» и не переносит focus. Текст принадлежит
|
||||
существующему i18n; имя пользователя, token и необработанный error в него не входят.
|
||||
Очередь очищается при завершении или смене пользовательской сессии.
|
||||
|
||||
Уведомление вызывается из одного обработчика успешного действия в его VM,
|
||||
не из `useEffect` по `isSuccess` и не из глобального query/mutation interceptor.
|
||||
ID относится к выполнению действия, а не к названию операции вообще. Повторное
|
||||
завершение того же выполнения подавляется у владельца и получает тот же ID;
|
||||
следующее отдельное действие имеет новый ID. Не создаётся постоянный журнал
|
||||
показанных сообщений.
|
||||
|
||||
Решение о необходимости подтверждения принадлежит VM, знающей оставшийся
|
||||
экран. Критерий проверяется по фактически видимой выборке, а не по одному
|
||||
наличию успешного HTTP-ответа:
|
||||
|
||||
| Действие и оставшийся контекст | Обратная связь |
|
||||
| --- | --- |
|
||||
| Созданная/изменённая карта или операция видна в обновлённом списке | Содержимое подтверждает результат, toast отсутствует |
|
||||
| Успешная операция не видна из-за периода, страницы, фильтра или другого счёта | Одно «Операция сохранена» после закрытия формы |
|
||||
| Удалённая запись исчезла из видимого списка | Toast отсутствует |
|
||||
| Запись удалена, но оставшийся экран не отражает её исчезновение | Одно «Операция удалена» |
|
||||
| Создание/удаление подключения меняет видимый статус | Toast отсутствует |
|
||||
| Замена token при прежнем статусе «Подключён» | Одно «Подключение обновлено» |
|
||||
| Категория появилась или изменена в открытом списке/поле | Toast отсутствует |
|
||||
| Сохранение успешно, но связанное чтение не подтвердило результат на экране | Краткое подтверждение сохранения без сообщения о сбое чтения |
|
||||
|
||||
VM получает outcome связанного обновления от существующего владельца
|
||||
invalidation; её решение о подтверждении выполняется один раз для action ID.
|
||||
Успех записи отделён от успеха последующего GET: ошибка чтения не становится
|
||||
ошибкой сохранения и не предлагает повторить уже выполненную запись.
|
||||
Получение видимого подтверждения ограничено тем же 10-секундным UI budget;
|
||||
после него при отсутствии подтверждения используется одно уведомление.
|
||||
Отдельный глобальный наблюдатель mutation results для этого не вводится.
|
||||
|
||||
Альтернатива `react-toastify` добавляет вторую визуальную систему без нужной
|
||||
дополнительной функции. Собственный toast из Mantine `Notification` потребовал
|
||||
бы обслуживать очередь, timers и доступность; готовый пакет дешевле.
|
||||
|
||||
## UI/UX Applicability and Package
|
||||
|
||||
**Applicability and scope:** `new/changed` — меняются feedback и состояния
|
||||
существующих экранов, а не их финансовая композиция.
|
||||
|
||||
**Package revision:** `quiet-user-feedback-ui-v1`.
|
||||
|
||||
Пакет полностью inline. Отдельные PNG/SVG не нужны: новая геометрия страниц,
|
||||
иллюстрации и визуализация финансовых данных не вводятся. Следующие описания
|
||||
и схемы входят в единое Design approval.
|
||||
|
||||
**Screens, states and interactions:**
|
||||
|
||||
| Область | Loaded / background failure | Нет результата | Действия |
|
||||
| --- | --- | --- | --- |
|
||||
| Счета, портфель, режим всех счетов | Заголовок сразу переходит в карточки и содержимое; строки синхронизации отсутствуют | Page-owned skeleton → нейтральная недоступность | Открыть счёт, перейти в раздел, настроить отсутствующее подключение |
|
||||
| Операции, доход, события | Фильтры и доступный результат сохраняются; даты событий остаются | Недоступность только текущей выборки или расчёта | Применить/сбросить фильтры, сменить страницу, навигация |
|
||||
| Поиск и детали инструментов | Поиск, график и доступные секции без технических controls | Незагруженная секция сохраняет свой заголовок и локальное состояние | Поиск, выбор инструмента и диапазона |
|
||||
| Карты, сводки и история | Данные и пользовательские controls сохраняются | Локальная недоступность без ложного нулевого остатка | Добавить/изменить, фильтры, страницы |
|
||||
| Настройки подключения | Статус и введённый token сохраняются при временном сбое чтения | Нет формы до первого подтверждённого статуса | Подключить/заменить/отключить после получения статуса |
|
||||
| Формы и подтверждения удаления | Локальное ожидание явного действия; ошибки у владельца | Недоступность нужного справочника или актуальной версии | Сохранить/отменить; явное принятие новой версии при конфликте |
|
||||
|
||||
Схема loaded портфеля:
|
||||
|
||||
```text
|
||||
Счета / Основной счёт
|
||||
Основной счёт
|
||||
|
||||
[ ₽ 125 000,50 ] [ $ 320,00 ]
|
||||
Структура портфеля
|
||||
Позиции / Деньги / Последние операции / Доход
|
||||
```
|
||||
|
||||
Между заголовком и содержимым нет зарезервированного места под удалённые
|
||||
кнопки, даты и баннеры. Temporary refetch failure имеет тот же вид, что loaded.
|
||||
|
||||
Схема локальной недоступности:
|
||||
|
||||
```text
|
||||
Получено купонами и дивидендами
|
||||
Пока недоступно
|
||||
|
||||
Открыть пассивный доход →
|
||||
```
|
||||
|
||||
Ссылка присутствует только там, где у блока уже есть предметная навигация.
|
||||
Нет дополнительного «Попробуйте позже», countdown или обещания завершения.
|
||||
Для основной страницы сохраняются shell и существующие пути навигации.
|
||||
Нейтральное состояние — читаемый текст, не warning/error Alert. Символ `—`
|
||||
для отсутствующей суммы всегда имеет доступное объяснение.
|
||||
|
||||
Схема редкого подтверждения:
|
||||
|
||||
```text
|
||||
Операция сохранена ×
|
||||
```
|
||||
|
||||
Нотификация объявляется ненавязчиво, не закрывает modal, не переводит фокус
|
||||
и не является единственным носителем ошибки или необходимого действия.
|
||||
При отображении над формой учитывается её focus trap; закрытие уведомления
|
||||
не отбирает доступ к кнопкам формы. Повторные фоновые попытки не создают
|
||||
live-announcements. Skeleton busy остаётся только у реально ожидающей области.
|
||||
|
||||
**Responsive behavior:** существующие layout и semantic tokens сохраняются.
|
||||
При ширине 320–767 px нейтральные состояния занимают ширину области; toast
|
||||
располагается снизу по центру с отступами и учётом safe area, без горизонтального
|
||||
скролла. От 768 px — справа снизу. Сообщения переносятся, кнопка закрытия
|
||||
остаётся доступной. Контейнер уведомлений не блокирует pointer events вне
|
||||
самих сообщений и не перекрывает единственный путь к основным controls.
|
||||
Появление учитывает reduced motion.
|
||||
|
||||
**Reused source and differences:** формального reuse чужого SDD UI package нет.
|
||||
Применяются текущие Mantine theme, `StateView` и page-owned skeleton; их
|
||||
ответственность сохраняется. Отдельная новая theme или макет неизменяемых
|
||||
финансовых секций не создаются.
|
||||
|
||||
## Delivery Map
|
||||
|
||||
| Area | Status | Architectural outcome or N/A reason |
|
||||
| --- | --- | --- |
|
||||
| frontend | `affected` | Query recovery, автоматическое наблюдение ресурсов, упрощённые VM/views, сохранение form conflict flow, Mantine Notifications, согласованные UI/data conventions и каталог состояний |
|
||||
| backend | `N/A` | Существующие обычные GET, observe и server-side work deduplication достаточны; DTO, force endpoint, TTL, хранение и серверные повторы не меняются |
|
||||
| specs | `affected` | Согласованные Delta Specs заменяют старые UI/retry obligations; новое правило feedback входит в canonical specs и индекс; пересечение с persist lifecycle разрешается без переписывания чужого active change |
|
||||
| docs | `affected` | As-built объяснение тихой загрузки, восстановления и уведомлений заменяет описание ручного refresh в UI resilience, data freshness и затронутых предметных страницах; принятое сквозное решение фиксируется в журнале ADR |
|
||||
|
||||
## Solution Assessment
|
||||
|
||||
Обязательный trigger — изменение общей политики caching/recovery и
|
||||
координации concurrent reads; дополнительно выбирается общий механизм уведомлений.
|
||||
|
||||
| Вариант | Fit и зрелость | Security и ограничения | Operating / migration cost | Решение |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Существующий TanStack Query + текущие resource/screen owners | Уже владеет cache, retry, cancellation, focus/online events; domain metadata остаётся отдельной | Сохраняет session cancellation; требует отличать read failure от failed work и запретить retries mutations | Без новой runtime-инфраструктуры; изменения сосредоточены в существующих owners | Выбран |
|
||||
| Новый scheduler или state-machine runtime | Потребовал бы повторить текущий query lifecycle | Добавляет второе состояние и риск late-response/cache рассогласования | Новая dependency и миграция всех consumers без текущей необходимости | Отклонён |
|
||||
| Периодический force refresh | Не соответствует ограниченному recovery текущих missing/stale данных | Повторяет внешнюю работу и игнорирует freshness | Лишние RPC и нагрузка; усложняет rate limiting | Отклонён |
|
||||
| Mantine Notifications 9.6.1 | Зрелый companion текущей UI-системы; очередь, lifecycle, responsive position | Только доверенные локализованные тексты, без HTML/error payload | Один согласованный пакет; нет нового сервиса или отдельной темы | Выбран |
|
||||
| React-Toastify | Зрелый самостоятельный механизм, но дополнительная функция не нужна | Потребует отдельно согласовывать styling и accessibility | Вторая UI-интеграция и её поддержка | Отклонён |
|
||||
| Собственная очередь над Mantine Notification | Встроенный primitive есть, готовой очереди в core нет | Собственные timers, focus/live-region и cleanup повышают риск | Собственный код дороже готового companion | Отклонён |
|
||||
|
||||
Выбор опирается на текущие исходники и публичную документацию
|
||||
[Query retries](https://tanstack.com/query/latest/docs/framework/react/guides/query-retries),
|
||||
[Query cancellation](https://tanstack.com/query/latest/docs/framework/react/guides/query-cancellation),
|
||||
[focus refetch](https://tanstack.com/query/latest/docs/framework/react/guides/window-focus-refetching)
|
||||
и [Mantine Notifications](https://mantine.dev/x/notifications/).
|
||||
Документация объясняет возможности библиотек; domain recovery и UI-пакет
|
||||
полностью определены здесь и не зависят от доступа к этим ссылкам.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Тишина не означает актуальность в реальном времени.** TTL сам не создаёт
|
||||
расписание; после исчерпания recovery budget новая серия ждёт lifecycle
|
||||
event. Это ограничивает нагрузку ценой возможного ожидания следующего
|
||||
возвращения на экран при долгой недоступности источника.
|
||||
- **Короткий UI budget и долгая история.** Через 10 секунд может появиться
|
||||
«Пока недоступно», хотя работа продолжается. Готовый результат заменяет
|
||||
это состояние; уже доступные области не ждут чужой истории.
|
||||
- **Умножение повторов.** Transport, status polling и source recovery имеют
|
||||
разных владельцев и конечные бюджеты. Новые source IDs и failed metadata
|
||||
не запускают неограниченную серию.
|
||||
- **Access loss важнее cached content.** Ошибки доступа проверяются до loaded
|
||||
presentation. Status ID not found не смешивается с account not found.
|
||||
- **Конфликт версии не является ошибкой чтения.** Автоматическое получение
|
||||
новой версии не подтверждает её принятие и не повторяет запись.
|
||||
- **Уведомления могут дублировать содержимое.** Решение остаётся у action VM,
|
||||
где известна видимая выборка; глобальный toast на каждый success исключён.
|
||||
- **Исходные ветки не равны новой canonical норме.** Существующие local
|
||||
изменения переноса PortfolioStructure сохраняются; design не требует их
|
||||
отката. Удалённая актуальность specs ранее не была подтверждена из-за DNS;
|
||||
фактическая база поставки требует сверки перед её объединением.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Сохранить текущую canonical базу quiet change и порядок требований:
|
||||
утверждённое quiet-поведение заменяет старые обязательства показывать
|
||||
статусы и ручной retry. Технические возможности уже предоставленного API
|
||||
используются без автоматического переноса всех artifacts `persist-tbank-data`.
|
||||
Его последующий sync/archive не должен восстанавливать отменённые UI-правила:
|
||||
пересекающийся change требует отдельного rebase и approval до своего archive.
|
||||
Если он попадает в canonical specs раньше quiet, quiet deltas адаптируются
|
||||
к новой базе с сохранением согласованного поведения и повторным approval
|
||||
затронутых artifacts. Чужие active artifacts сейчас не переписываются.
|
||||
2. Ввести recovery у существующих владельцев чтения и отделить presentation
|
||||
readiness от transport status. Сохранить текущие auth/access/invalidation
|
||||
semantics и различие обычного чтения и observe.
|
||||
3. Перевести экраны и формы на тихую presentation, убрать ручные технические
|
||||
controls и сохранить предметные conflict actions. Подключить единый host
|
||||
уведомлений и явные action outcomes. Эти части выпускаются одним frontend
|
||||
изменением: удаление recovery-кнопок не поставляется отдельно от замены.
|
||||
4. Согласовать новые conventions и presentation states, затем обновить
|
||||
as-built docs и canonical specs по принятому lifecycle. Серверный rollout
|
||||
и миграция данных для этого решения не требуются.
|
||||
|
||||
### Rollback Contract
|
||||
|
||||
| Field | Decision |
|
||||
| --- | --- |
|
||||
| Status | `required` |
|
||||
| Reason | Recovery касается сохранения личных снимков, access invalidation и взаимодействия с финансовыми mutations |
|
||||
| Trigger | Новая политика показывает данные прежней границы, создаёт неконтролируемые запросы, повторяет запись или ломает восстановление без ручных controls |
|
||||
| Actions | Вернуть предыдущую frontend-поставку целиком: старые query policies вместе с прежними UI recovery controls; не откатывать только автоматическое восстановление под уже тихим UI |
|
||||
| Data safety | Не менять БД, серверные данные, connection revision, intent IDs и versions; не повторять пользовательские mutations для компенсации; при подозрении на неправильный показ завершить локальную сессию и очистить её кэш |
|
||||
| Verification | После отката данные разделены по сессии и подключению, временный сбой имеет прежний доступный recovery path, чтение ограничено прежней политикой, сохранение и удаление требуют явного действия |
|
||||
@@ -0,0 +1,102 @@
|
||||
## Why
|
||||
|
||||
Пользователь WealthLedger хочет видеть свои финансы, а не обслуживать получение
|
||||
данных. Сейчас сообщения «Источник не удалось обновить», «Сохранённые данные
|
||||
не изменены», время последней загрузки и действия «Обновить»/«Повторить»
|
||||
перекладывают на него контроль технической работы приложения. Эти элементы
|
||||
повторяются на разных экранах и отвлекают от содержимого.
|
||||
|
||||
Простого удаления надписей недостаточно: восстановление после временного сбоя
|
||||
не должно зависеть от исчезнувшей кнопки. Нужен единый принцип:
|
||||
тихое чтение и обновление, самостоятельное восстановление и понятный результат
|
||||
явных действий пользователя.
|
||||
|
||||
## What Changes
|
||||
|
||||
- На экранах просмотра приложение сохраняет доступные данные при фоновом
|
||||
обновлении и временном сбое, не показывает технические предупреждения,
|
||||
индикаторы фоновой работы, отметки времени получения и элементы ручного
|
||||
обновления или повторной загрузки. Это относится к счетам, портфелю, операциям,
|
||||
доходам, событиям, инструментам, поиску, картам и чтению состояния подключения.
|
||||
Те же сообщения не переносятся во всплывающие уведомления или подсказки.
|
||||
- После временных сбоев приложение самостоятельно возвращается к нормальной
|
||||
работе, не требуя от пользователя повторять загрузку или перезагружать страницу.
|
||||
- Первичное ожидание сохраняет понятную структуру будущего содержимого. При
|
||||
затянувшейся недоступности вместо бесконечной загрузки показывается спокойное
|
||||
локальное состояние «Пока недоступно» без технического повтора. После
|
||||
восстановления содержимое появляется автоматически. Сбой одного блока не
|
||||
скрывает остальные доступные блоки.
|
||||
- Отсутствие данных, подтверждённая пустота и неполный финансовый результат
|
||||
остаются различимыми. Неизвестные суммы не становятся нулями, незавершённый
|
||||
расчёт не выдаётся за окончательный, а незагруженный список — за отсутствие
|
||||
операций. Неполнота передаётся состоянием конкретного результата без отчёта
|
||||
о процессе синхронизации. Даты операций, выплат и выбранных периодов сохраняются.
|
||||
- Редкие краткие уведомления подтверждают завершённые действия пользователя,
|
||||
когда результат иначе легко пропустить. Очевидные изменения экрана не
|
||||
сопровождаются дублирующим уведомлением. Фоновая загрузка не порождает
|
||||
уведомлений об успехе или ошибке, а повтор одного события — каскад сообщений.
|
||||
- Ошибки ввода и неуспешное сохранение остаются у соответствующего поля или
|
||||
формы с сохранением ввода. Действия, необходимые от пользователя, например
|
||||
вход или восстановление подключения, остаются понятными и доступными.
|
||||
Исчезающее уведомление не является единственным сообщением о таком препятствии.
|
||||
- Обновление данных не сбрасывает выбранные счёт, инструмент, период, фильтры
|
||||
и не прерывает работу пользователя. Личные данные остаются доступны только
|
||||
тому, кто имеет право их просматривать.
|
||||
|
||||
Ожидаемый результат: пользователь видит устойчивое финансовое содержимое,
|
||||
не управляет загрузкой и получает обратную связь только там, где она помогает
|
||||
понять результат его действия или необходимый следующий шаг.
|
||||
|
||||
Non-goals:
|
||||
|
||||
- Скрывать неуспешные изменения данных или выдавать их за сохранённые.
|
||||
- Менять финансовые расчёты, валютный смысл, даты событий и правила доступа.
|
||||
- Добавлять центр уведомлений, историю сообщений или уведомления вне приложения.
|
||||
- Обещать актуальность данных в реальном времени.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `specs/frontend-user-feedback/spec.md`: единая политика редких уведомлений
|
||||
о действиях пользователя, локальных ошибок и отсутствия технического шума
|
||||
на всех экранах приложения, включая карты и инструменты.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `specs/frontend-data-resilience/spec.md`: тихое сохранение доступного
|
||||
результата и самостоятельное восстановление вместо предупреждений
|
||||
и ручного повтора; защита границ личных данных сохраняется.
|
||||
- `specs/frontend-loading-presentation/spec.md`: отсутствие индикации фонового
|
||||
обновления при доступных данных и переход от первичного ожидания к устойчивому
|
||||
локальному состоянию недоступности; явные действия сохраняют обратную связь.
|
||||
- `specs/frontend-ui-resilience/spec.md`: ожидаемые состояния не требуют
|
||||
предупреждения об устаревании и ручного повтора; навигация после
|
||||
аварийной ошибки и действия восстановления доступа остаются доступными.
|
||||
- `specs/brokerage-accounts/spec.md`: тихое отображение сохранённого списка,
|
||||
автоматическое восстановление временных ошибок и отсутствие ручной загрузки.
|
||||
- `specs/portfolio-view/spec.md`: показ портфеля без времени получения,
|
||||
предупреждений об обновлении и кнопок повтора, с автоматическим восстановлением
|
||||
и сохранением корректного смысла доступных финансовых значений.
|
||||
- `specs/brokerage-account-ledger/spec.md`: локальное тихое восстановление
|
||||
последних операций и журнала без технических действий пользователя.
|
||||
- `specs/brokerage-account-upcoming-events/spec.md`: сохранение доступных событий
|
||||
при временной ошибке и локальное автоматическое восстановление вместо ручного
|
||||
повтора и технической ошибки блока.
|
||||
- `specs/brokerage-account-income-analytics/spec.md`: локальная недоступность
|
||||
незавершённого расчёта и автоматическое восстановление дохода и его сводки
|
||||
без выдачи неполного результата за окончательный.
|
||||
- `specs/tbank-connection/spec.md`: автоматическое восстановление чтения статуса
|
||||
без кнопки повтора; ошибки создания, замены и удаления подключения сохраняют
|
||||
явную обратную связь и введённые данные.
|
||||
|
||||
## Impact
|
||||
|
||||
Изменение охватывает просмотр счетов, портфеля, операций, доходов, будущих
|
||||
событий, инструментов и карт, поиск и настройки подключения.
|
||||
|
||||
Пользователю больше не нужно следить за обновлением данных и устранять
|
||||
временные сбои загрузки вручную. Интерфейс становится спокойнее, а сообщения
|
||||
помогают понять результат собственного действия или необходимый следующий шаг.
|
||||
Финансовый смысл данных, понятность ошибок сохранения и защита личной
|
||||
информации сохраняются.
|
||||
@@ -0,0 +1,82 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Realized analytics never presents an incomplete result as complete
|
||||
|
||||
Система MUST формировать итог выбранного периода, его trend buckets,
|
||||
instrument breakdown и drill-down только после успешного получения всех
|
||||
необходимых cursor pages исполненных операций этого периода. Каждое comparison
|
||||
MUST дополнительно требовать полного результата соответствующего периода
|
||||
сравнения. Ошибка любой необходимой страницы MUST NOT возвращать частичные
|
||||
totals, comparisons, buckets или
|
||||
breakdown как полный результат. При временной недоступности клиент MUST
|
||||
сохранять предыдущий полный результат той же выборки либо показывать
|
||||
нейтральную недоступность соответствующего расчёта без технических сообщений
|
||||
и ручного повтора. Восстановление SHALL выполняться автоматически по
|
||||
`frontend-data-resilience`.
|
||||
|
||||
Доступные независимые части MUST оставаться видимыми; неполное сравнение MUST
|
||||
обозначаться как недоступное без нулевой базы и окончательной разницы. Успешный
|
||||
полный период без подходящих выплат MUST возвращать нулевой результат и
|
||||
отображаться с объяснением пустого состояния. Неизвестный результат MUST NOT
|
||||
подменяться таким нулевым результатом.
|
||||
|
||||
#### Scenario: Все страницы операций успешно получены
|
||||
- **WHEN** для расчёта требуется несколько cursor pages и каждая страница успешно получена
|
||||
- **THEN** система формирует результат по всем подходящим Ledger Entries со всех страниц
|
||||
|
||||
#### Scenario: Одна из страниц операций недоступна
|
||||
- **WHEN** получение любой необходимой cursor page завершается временной ошибкой
|
||||
- **THEN** страница сохраняет предыдущий полный результат той же выборки либо показывает нейтральную недоступность расчёта без ручного повтора и не выдаёт частичный результат за полный
|
||||
|
||||
#### Scenario: В периоде нет купонов и дивидендов
|
||||
- **WHEN** все необходимые операции успешно получены, но среди них нет подходящих coupon или dividend Ledger Entries
|
||||
- **THEN** страница показывает нулевой результат и объясняет, что за выбранный период купоны и дивиденды не найдены
|
||||
|
||||
#### Scenario: Основной период готов, сравнение пока нет
|
||||
- **WHEN** выбранный период полностью рассчитан, а необходимый период сравнения ещё не получен
|
||||
- **THEN** основной доход остаётся доступным, сравнение обозначается как недоступное без нулевой базы, частичной разницы и сообщения о синхронизации
|
||||
|
||||
### Requirement: Account overview has an independent current-month income summary
|
||||
|
||||
Обзор выбранного Brokerage Account MUST показывать компактный source-currency
|
||||
summary «Получено купонами и дивидендами» за период от первого дня текущего
|
||||
месяца по Europe/Moscow до сегодняшнего дня включительно и действие перехода
|
||||
на страницу «Пассивный доход». Summary MUST всегда использовать этот current-month
|
||||
период и MUST NOT наследовать последний пользовательский период страницы аналитики.
|
||||
Пустой или неуспешный расчёт MUST оставаться локальным состоянием блока и
|
||||
MUST NOT заменять успешно отображённый Portfolio или другие блоки обзора.
|
||||
Временная ошибка обновления MUST сохранять доступную сводку той же выборки без
|
||||
предупреждения и ручного повтора. При отсутствии результата SHALL показываться
|
||||
нейтральная недоступность после первичного ожидания с автоматическим восстановлением.
|
||||
|
||||
#### Scenario: Обзор показывает доход текущего месяца
|
||||
- **WHEN** обзор Brokerage Account загружен и current-month analytics успешно рассчитана
|
||||
- **THEN** он показывает отдельные source-currency итоги с начала текущего месяца по сегодняшний день и переход к полной аналитике
|
||||
|
||||
#### Scenario: Пользовательский период не влияет на обзор
|
||||
- **WHEN** User ранее применил на странице аналитики период, отличный от текущего месяца
|
||||
- **THEN** summary обзора по-прежнему рассчитывается от первого дня текущего месяца по сегодняшний день
|
||||
|
||||
#### Scenario: Current-month summary недоступен
|
||||
- **WHEN** Portfolio обзора успешно загружен, но расчёт current-month summary завершается временной ошибкой
|
||||
- **THEN** Portfolio и остальные блоки остаются доступными, а summary сохраняет предыдущий результат той же выборки либо показывает нейтральную недоступность без ручного повтора
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Доход обновляется без технических сообщений
|
||||
|
||||
Страница дохода MUST сохранять применённый период, черновик фильтров и
|
||||
доступный результат при фоновом обновлении. Временная ошибка SHALL
|
||||
восстанавливаться автоматически без кнопок обновления и повтора, времени
|
||||
получения данных и уведомлений об их загрузке. Изменение периода MUST NOT
|
||||
подписывать прежний результат новыми датами. Ошибки пользовательского периода
|
||||
MUST оставаться у полей и MUST NOT запускать автоматический повтор ошибочного
|
||||
ввода. Подтверждённая потеря доступа MUST скрывать личные результаты.
|
||||
|
||||
#### Scenario: Доход обновляется при редактировании периода
|
||||
- **WHEN** пользователь редактирует черновик дат, а фоновое обновление применённого периода завершилось
|
||||
- **THEN** черновик и фокус сохраняются, результат относится к прежнему применённому периоду и техническое уведомление не появляется
|
||||
|
||||
#### Scenario: Новый период пока недоступен
|
||||
- **WHEN** пользователь применил другой допустимый период, но его расчёт ещё не получен
|
||||
- **THEN** страница показывает ожидание или недоступность нового результата, не выдаёт прежние суммы за доход нового периода и восстанавливается автоматически
|
||||
@@ -0,0 +1,46 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Account overview includes recent ledger events
|
||||
|
||||
Обзор счёта MUST показывать пять последних исполненных записей журнала этого
|
||||
счёта и действие перехода к его полному журналу. Каждая запись MUST иметь ту же
|
||||
визуальную идентичность инструмента и семантику денежного движения, что и запись
|
||||
полного журнала. Неуспешный или пустой запрос MUST оставаться локальным состоянием
|
||||
блока и MUST NOT заменять успешно отображённый Portfolio. При временном сбое
|
||||
обновления блок MUST сохранять доступные записи без технического предупреждения
|
||||
или ручного повтора. При отсутствии результата он SHALL показывать первичное
|
||||
ожидание либо нейтральную недоступность, автоматически восстанавливая чтение.
|
||||
Неподтверждённая пустота MUST NOT представляться отсутствием операций.
|
||||
|
||||
#### Scenario: Отображение последних событий
|
||||
- **WHEN** загружается обзор счёта и его журнал содержит исполненные операции
|
||||
- **THEN** обзор показывает не более пяти последних записей и ссылку на полный журнал
|
||||
|
||||
#### Scenario: Последние записи недоступны
|
||||
- **WHEN** обзор счёта успешно загружается, но запрос последних записей завершается временной ошибкой
|
||||
- **THEN** Portfolio остаётся доступным, а блок сохраняет прежние записи либо показывает нейтральную недоступность без технического повтора и восстанавливается автоматически
|
||||
|
||||
#### Scenario: Последняя запись не содержит visual metadata инструмента
|
||||
- **WHEN** одна из последних записей не имеет доступной presentation metadata инструмента
|
||||
- **THEN** обзор показывает для неё понятный текстовый fallback и сохраняет дату, тип, инструмент и денежное изменение записи доступными для чтения
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Журнал восстанавливает чтение без ручного обновления
|
||||
|
||||
Страница журнала SHALL сохранять записи текущей выборки при временном сбое
|
||||
обновления и восстанавливать чтение автоматически по `frontend-data-resilience`.
|
||||
Период, категории и выбранная страница MUST сохраняться; результат другой
|
||||
выборки MUST NOT подменять текущую. Страница MUST NOT показывать время получения,
|
||||
технические статусы или действия ручного повтора. Даты операций, фильтры,
|
||||
действия их применения и переходы по страницам MUST оставаться доступными.
|
||||
При отсутствии результата SHALL применяться первичное ожидание и нейтральная
|
||||
недоступность, а не сообщение об отсутствии операций до подтверждения полноты.
|
||||
|
||||
#### Scenario: Обновление журнала временно не удалось
|
||||
- **WHEN** для применённого периода и страницы уже есть записи, а обновление завершается временной ошибкой
|
||||
- **THEN** записи и параметры просмотра сохраняются без предупреждения и кнопки повтора до автоматического восстановления
|
||||
|
||||
#### Scenario: Следующая страница ещё не получена
|
||||
- **WHEN** пользователь перешёл к следующей странице, но её загрузка временно не удалась
|
||||
- **THEN** область новой страницы показывает ожидание или нейтральную недоступность, не выдаёт прежние записи за новую страницу и восстанавливает чтение автоматически
|
||||
@@ -0,0 +1,49 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Account page presents an upcoming events list
|
||||
|
||||
Страница Brokerage Account MUST предоставлять действие перехода на отдельную
|
||||
страницу «Будущие события». Эта страница MUST показывать все полученные события
|
||||
компактным списком без фильтров, пагинации и суммарного итога, с датой, типом,
|
||||
инструментом и прогнозной суммой в исходной валюте, если она известна. Страница
|
||||
MUST объяснять, что прогноз основан на позиции счёта, указан до налогов и может
|
||||
быть неполным из-за доступности данных источника. Это предметное ограничение
|
||||
прогноза MUST NOT превращаться в отчёт о технической синхронизации.
|
||||
|
||||
При временном сбое обновления страница MUST сохранять ранее доступный результат
|
||||
той же выборки без технических сообщений, времени получения и ручного повтора.
|
||||
При отсутствии результата SHALL показываться первичное ожидание либо нейтральная
|
||||
недоступность с автоматическим восстановлением по `frontend-data-resilience`.
|
||||
Подтверждённая потеря доступа MUST скрывать личные данные независимо от наличия
|
||||
сохранённого результата.
|
||||
|
||||
#### Scenario: Просмотр ближайших событий
|
||||
- **WHEN** User открывает «Будущие события» из обзора Brokerage Account
|
||||
- **THEN** он видит отсортированный список событий ближайших 30 дней, где offer обозначен без суммы
|
||||
|
||||
#### Scenario: Пустое состояние страницы
|
||||
- **WHEN** запрос будущих событий успешно возвращает полный пустой список
|
||||
- **THEN** страница сообщает, что на ближайшие 30 дней событий не найдено, и объясняет предметные ограничения покрытия источника
|
||||
|
||||
#### Scenario: Ошибка страницы
|
||||
- **WHEN** запрос будущих событий завершается временной ошибкой
|
||||
- **THEN** страница сохраняет доступный результат той же выборки либо показывает нейтральную недоступность без ручного повтора, восстанавливая чтение автоматически и не выдавая частичные данные за полный прогноз
|
||||
|
||||
### Requirement: Account overview includes upcoming events
|
||||
|
||||
Обзор Brokerage Account MUST показывать не более пяти ближайших Future Cash
|
||||
Event и действие перехода на полную страницу «Будущие события». Неуспешный
|
||||
или пустой запрос этого блока MUST оставаться локальным состоянием блока и
|
||||
не заменять успешно отображённый Portfolio. При временном сбое обновления
|
||||
блок MUST сохранять доступные события без технического предупреждения; при
|
||||
отсутствии результата SHALL показываться нейтральная недоступность после
|
||||
первичного ожидания. Восстановление MUST выполняться автоматически без ручного
|
||||
повтора и без подмены неизвестного результата подтверждённой пустотой.
|
||||
|
||||
#### Scenario: Отображение ближайших событий
|
||||
- **WHEN** обзор счёта успешно загружен и календарь содержит будущие события
|
||||
- **THEN** обзор показывает первые пять событий в порядке даты и действие перехода к полной странице
|
||||
|
||||
#### Scenario: Ближайшие события недоступны
|
||||
- **WHEN** обзор счёта успешно загружен, но запрос будущих событий завершается временной ошибкой
|
||||
- **THEN** Portfolio остаётся доступным, а блок сохраняет прежние события либо показывает нейтральную недоступность без технической ошибки и ручного повтора
|
||||
@@ -0,0 +1,101 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Экран списка брокерских счетов
|
||||
|
||||
Клиент SHALL загружать список при открытии защищённого маршрута `/accounts`
|
||||
без ручного обновления. Клиент MUST использовать успешный frontend-кэш списка
|
||||
в течение 60 секунд. После истечения этого срока открытие или возвращение
|
||||
на экран SHALL инициировать получение актуального списка; имеющийся Client
|
||||
Cached Snapshot MUST оставаться видимым во время обновления и после временной
|
||||
ошибки без технических статусов, времени получения и действий повтора.
|
||||
Автоматическое восстановление SHALL соответствовать `frontend-data-resilience`.
|
||||
|
||||
При успешном непустом ответе клиент MUST отобразить адаптивный одноколоночный
|
||||
список широких интерактивных карточек счетов с локализованными типом и, если
|
||||
доступна, датой открытия без времени; каждая карточка MUST ясно показывать тип,
|
||||
имя, финансовую сводку, вторичные метаданные и заметное действие открытия счёта,
|
||||
вести на `/accounts/:accountId` по всей области и сохранять доступное
|
||||
клавиатурное состояние. После успешной замены или удаления T-Bank connection
|
||||
клиент MUST инвалидировать список счетов.
|
||||
|
||||
Для доступной `portfolioSummary` карточка MUST показывать не более двух
|
||||
Money-итогов в исходных валютах и обозначать наличие остальных валют, если
|
||||
они есть; она MUST NOT показывать или подразумевать FX-конвертацию,
|
||||
межвалютный агрегированный итог либо Cash Balance как отдельный второй итог.
|
||||
При `portfolioSummary: null` карточка MUST показывать локальное нейтральное
|
||||
состояние недоступности сводки, сохраняя метаданные счёта и возможность открыть
|
||||
его. При доступной пустой сводке карточка MUST не выдавать отсутствие итогов
|
||||
за временную ошибку.
|
||||
|
||||
#### Scenario: Пользователь открывает счёт из списка
|
||||
- **WHEN** клиент успешно загрузил непустой список и User выбирает карточку счёта
|
||||
- **THEN** клиент переходит на `/accounts/:accountId` с `id` выбранного счёта
|
||||
|
||||
#### Scenario: Список пуст
|
||||
- **WHEN** клиент получает успешный полный пустой массив
|
||||
- **THEN** клиент показывает штатное сообщение об отсутствии инвестиционных счетов T-Bank
|
||||
|
||||
#### Scenario: Список берётся из свежего frontend-кэша
|
||||
- **WHEN** User повторно открывает список в течение 60 секунд после успешной загрузки
|
||||
- **THEN** клиент показывает сохранённые счета без нового API-запроса
|
||||
|
||||
#### Scenario: Устаревший список загружается повторно
|
||||
- **WHEN** User открывает список после истечения 60 секунд frontend-кэша и уже существует последний успешный список
|
||||
- **THEN** клиент оставляет этот список видимым во время нового запроса без индикации фонового обновления
|
||||
|
||||
#### Scenario: Ошибка фонового обновления не скрывает snapshot
|
||||
- **WHEN** обновление устаревшего списка завершается временной ошибкой при наличии последнего успешного Client Cached Snapshot
|
||||
- **THEN** клиент сохраняет snapshot видимым без предупреждения и ручного повтора и восстанавливает получение данных автоматически
|
||||
|
||||
#### Scenario: Карточка ограничивает валютные итоги
|
||||
- **WHEN** доступная сводка счёта содержит итоги в трёх или более валютах
|
||||
- **THEN** карточка показывает не более двух исходных валютных итогов и обозначает наличие остальных без общего сконвертированного значения
|
||||
|
||||
#### Scenario: Недоступность сводки локальна карточке
|
||||
- **WHEN** клиент получает список, в котором `portfolioSummary` одного счёта равна `null`
|
||||
- **THEN** он показывает нейтральную недоступность только этой сводки, не скрывает остальные карточки и сохраняет переход на детали счёта
|
||||
|
||||
#### Scenario: Пустая сводка не является ошибкой
|
||||
- **WHEN** клиент получает доступную `portfolioSummary` с пустым `totalsByCurrency`
|
||||
- **THEN** он показывает нейтральное состояние отсутствия финансовых итогов, а не состояние временной ошибки
|
||||
|
||||
### Requirement: Клиентские состояния ошибки списка счетов
|
||||
|
||||
Клиент SHALL автоматически восстанавливать временно неуспешное чтение списка
|
||||
по правилам `frontend-data-resilience` без кнопок обновления и повтора. При
|
||||
`TBANK_NOT_CONNECTED` клиент MUST показать необходимость подключения и действие
|
||||
открытия настроек T-Bank. При `INVALID_TBANK_TOKEN` клиент MUST показать
|
||||
необходимость восстановить подключение и то же действие. Эти подтверждённые
|
||||
состояния MUST скрывать личный список и MUST NOT запускать автоматические повторы.
|
||||
|
||||
При временной недоступности, включая `TBANK_UNAVAILABLE`, клиент MUST сохранять
|
||||
успешный результат текущей границы доступа без предупреждений. Если результата
|
||||
нет, после первичного ожидания клиент SHALL показать спокойную локальную
|
||||
недоступность без технических подробностей. Неизвестная ошибка MUST получать
|
||||
безопасное локальное представление; отсутствие известного кода MUST NOT
|
||||
разрешать бесконечные повторы или обход доступа. Временный сбой MUST NOT
|
||||
предлагать перенастройку действующего подключения как способ повторить загрузку.
|
||||
|
||||
#### Scenario: Подключение требуется
|
||||
- **WHEN** клиент получает `TBANK_NOT_CONNECTED`
|
||||
- **THEN** клиент не показывает список и предлагает открыть настройки T-Bank
|
||||
|
||||
#### Scenario: Известная ошибка не повторяется автоматически
|
||||
- **WHEN** запрос списка завершается подтверждённой ошибкой подключения или доступа, требующей действия пользователя
|
||||
- **THEN** клиент не выполняет автоматических повторов и предлагает соответствующее действие вместо технической повторной загрузки
|
||||
|
||||
#### Scenario: Token требует внимания
|
||||
- **WHEN** клиент получает `INVALID_TBANK_TOKEN`
|
||||
- **THEN** клиент скрывает личный список и предлагает восстановить подключение в настройках T-Bank
|
||||
|
||||
#### Scenario: Временная ошибка T-Bank
|
||||
- **WHEN** клиент получает `TBANK_UNAVAILABLE` без доступного результата списка
|
||||
- **THEN** клиент автоматически восстанавливает получение и после первичного ожидания показывает нейтральную недоступность без повторной загрузки и предложения перенастроить подключение
|
||||
|
||||
#### Scenario: Неизвестная ошибка загрузки
|
||||
- **WHEN** клиент получает ошибку без известного кода списка счетов и без доступного результата
|
||||
- **THEN** клиент показывает безопасное нейтральное состояние без технического сообщения или кнопки повтора, применяя только допустимое ограниченное восстановление
|
||||
|
||||
#### Scenario: Известная ошибка при сохранённом snapshot
|
||||
- **WHEN** фоновое обновление списка с последним успешным Client Cached Snapshot получает `TBANK_UNAVAILABLE`
|
||||
- **THEN** клиент сохраняет snapshot видимым без предупреждения, времени получения и ручных действий восстановления
|
||||
@@ -0,0 +1,127 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Client Cached Snapshot остаётся доступным при обновлении Accounts и Portfolio
|
||||
|
||||
Frontend MUST сохранять последний успешный результат списка Brokerage Accounts,
|
||||
Portfolio и других экранов чтения при фоновом обновлении и временной ошибке
|
||||
для того же пользователя, подключения и набора данных. Подтверждённый пустой
|
||||
результат MUST также считаться успешным. Смена счёта, инструмента, поискового
|
||||
запроса, страницы или применённого периода MUST NOT представлять данные прежней
|
||||
выборки как результат новой.
|
||||
|
||||
Доступный результат MUST оставаться видимым без предупреждений о свежести,
|
||||
времени получения, индикаторов фонового обновления и действий ручного повтора.
|
||||
Frontend MUST NOT подменять внутренние признаки свежести и полноты, успешное
|
||||
время источника или ошибку обновления только ради отсутствия их отображения.
|
||||
Client Cached Snapshot MUST оставаться отличным от server-side fallback;
|
||||
ошибка чтения MUST NOT изменять `Portfolio.isStale`.
|
||||
|
||||
Обновление MUST сохранять выбранные фильтры, период, открытые разделы, ввод,
|
||||
фокус и положение просмотра, пока соответствующее содержимое существует.
|
||||
При logout, смене подключения или подтверждённом отказе доступа личные данные
|
||||
прежней границы MUST перестать отображаться; поздний результат прежней загрузки
|
||||
MUST NOT возвращать их на экран.
|
||||
|
||||
#### Scenario: Список Brokerage Accounts обновляется в фоне
|
||||
- **WHEN** экран Accounts уже показывает Client Cached Snapshot и начинается фоновое обновление того же списка
|
||||
- **THEN** frontend оставляет список видимым без индикации обновления, времени получения и технических действий
|
||||
|
||||
#### Scenario: Обновление Portfolio завершается ошибкой после успешного ответа
|
||||
- **WHEN** экран Brokerage Account уже показывает Client Cached Snapshot Portfolio, а последующее обновление этого же Portfolio завершается временной ошибкой
|
||||
- **THEN** frontend сохраняет Portfolio видимым без предупреждения и ручного повтора, а восстановление выполняется автоматически
|
||||
|
||||
#### Scenario: Server-side fallback и client cached snapshot различаются
|
||||
- **WHEN** backend возвращает Portfolio с `isStale` либо фоновое обновление Client Cached Snapshot завершается временной ошибкой
|
||||
- **THEN** frontend сохраняет доступные данные без технических сообщений и не подменяет один внутренний вид состояния другим
|
||||
|
||||
#### Scenario: Подтверждённая пустота переживает фоновую ошибку
|
||||
- **WHEN** текущая выборка ранее успешно вернула полный пустой результат, а её обновление временно не удалось
|
||||
- **THEN** подтверждённое пустое состояние сохраняется без предупреждения и не заменяется первичной загрузкой
|
||||
|
||||
#### Scenario: Изменились параметры выборки
|
||||
- **WHEN** пользователь применил другой период или открыл другой счёт, а данных новой выборки ещё нет
|
||||
- **THEN** экран показывает ожидание или недоступность новой выборки и не подписывает прежние суммы новым периодом или счётом
|
||||
|
||||
#### Scenario: Обновление не сбрасывает работу пользователя
|
||||
- **WHEN** пользователь раскрыл раздел, выбрал фильтры или вводит данные, а фоновое чтение завершилось
|
||||
- **THEN** обновление не сбрасывает эти действия, не переносит фокус и не возвращает пользователя в начало сохранившегося содержимого
|
||||
|
||||
#### Scenario: Изменился доступ к личным данным
|
||||
- **WHEN** пользователь вышел, успешно сменил подключение либо получил подтверждённый отказ доступа к текущим данным
|
||||
- **THEN** прежние личные данные скрываются, а позднее завершение старой загрузки не восстанавливает их
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Временные сбои чтения восстанавливаются автоматически
|
||||
|
||||
Frontend SHALL автоматически повторять временно неуспешное чтение и восстанавливать
|
||||
получение необходимых текущему экрану данных без ручного обновления. Повторы
|
||||
одной попытки MUST иметь конечный предел и паузы, исключающие непрерывную
|
||||
череду запросов. При исчерпании предела доступные данные SHALL сохраняться,
|
||||
а при их отсутствии SHALL показываться локальная недоступность. Возвращение сети,
|
||||
возвращение пользователя к открытому экрану и повторное открытие экрана MUST
|
||||
возобновлять получение отсутствующих или требующих обновления данных без
|
||||
перезагрузки страницы. Свежий полный результат MUST NOT требовать обращения
|
||||
к источнику только из-за такого события.
|
||||
|
||||
Восстановление MUST учитывать сбой чтения результата и сбой самого получения
|
||||
данных из источника, даже если чтение состояния завершилось успешно. Наблюдение
|
||||
уже выполняющейся работы MUST NOT запускать её повторно. Уход с экрана MUST
|
||||
прекращать новые попытки его восстановления; это не требует отменять уже начатую
|
||||
работу источника. Автоматическое восстановление MUST NOT повторять запросы,
|
||||
требующие исправления ввода, входа или восстановления доступа, и MUST NOT
|
||||
переносить старые параметры в новую пользовательскую выборку.
|
||||
|
||||
#### Scenario: Кратковременный сбой чтения
|
||||
- **WHEN** чтение текущей выборки временно не удалось, а следующая разрешённая автоматическая попытка успешна
|
||||
- **THEN** экран самостоятельно показывает результат без ручного действия и без сообщения о сбое или восстановлении
|
||||
|
||||
#### Scenario: Источник завершил работу ошибкой
|
||||
- **WHEN** чтение сохранённых данных успешно, но получение необходимых новых данных из источника временно не удалось
|
||||
- **THEN** приложение применяет ограниченное автоматическое восстановление к неуспешной работе, а не считает успешное чтение доказательством её успеха
|
||||
|
||||
#### Scenario: Повторы исчерпаны
|
||||
- **WHEN** временная недоступность сохраняется после конечного предела автоматических попыток
|
||||
- **THEN** приложение прекращает серию повторов, сохраняет доступный результат либо локальную недоступность без бесконечной загрузки и ожидает следующего события восстановления
|
||||
|
||||
#### Scenario: Сеть или пользователь вернулись
|
||||
- **WHEN** после временного сбоя восстановилась сеть либо пользователь вернулся к экрану с отсутствующим или требующим обновления результатом
|
||||
- **THEN** получение данных возобновляется автоматически для текущей выборки и при успехе заменяет состояние недоступности содержимым
|
||||
|
||||
#### Scenario: Несколько событий восстановления совпали
|
||||
- **WHEN** сеть восстановилась одновременно с возвращением пользователя на экран
|
||||
- **THEN** приложение не запускает дублирующую работу получения одного набора данных
|
||||
|
||||
#### Scenario: Работа источника ещё выполняется
|
||||
- **WHEN** приложение восстанавливает чтение состояния уже выполняющейся работы
|
||||
- **THEN** оно продолжает получение её результата без повторного запуска той же работы
|
||||
|
||||
#### Scenario: Пользователь покинул экран
|
||||
- **WHEN** пользователь ушёл со страницы с ожидающим восстановлением
|
||||
- **THEN** новые попытки для покинутого экрана прекращаются, а уже начатая работа не изменяет содержимое другой выборки
|
||||
|
||||
#### Scenario: Ошибка требует действия пользователя
|
||||
- **WHEN** подтверждены недействительное подключение, отсутствие доступа или некорректный ввод
|
||||
- **THEN** автоматическая серия повторов прекращается и появляется соответствующее действие или локальная ошибка без технической кнопки повторной загрузки
|
||||
|
||||
### Requirement: Неполный результат не выдаётся за окончательный
|
||||
|
||||
Frontend MUST различать неизвестный результат, доступную часть данных и
|
||||
подтверждённую пустоту. Неполные суммы и сравнения MUST NOT показываться как
|
||||
окончательные или заменяться нулями. Доступные записи MAY оставаться видимыми,
|
||||
но их список MUST NOT представляться полным при неподтверждённой полноте.
|
||||
Состояние SHALL относиться к содержимому конкретной области, например
|
||||
недоступному итогу, без отчёта о прогрессе синхронизации. Готовая область MUST
|
||||
NOT ждать догрузки истории, которая не влияет на её результат.
|
||||
|
||||
#### Scenario: Итог ещё не определён
|
||||
- **WHEN** часть данных, необходимых для суммы или сравнения, отсутствует
|
||||
- **THEN** соответствующее значение обозначается как недоступное без вымышленного нуля или окончательного частичного итога
|
||||
|
||||
#### Scenario: Пустота ещё не подтверждена
|
||||
- **WHEN** записи не получены и полнота выбранного периода неизвестна
|
||||
- **THEN** экран не сообщает об отсутствии операций или выплат за этот период
|
||||
|
||||
#### Scenario: Период готов раньше остальной истории
|
||||
- **WHEN** данные показанного периода полны, а не влияющая на него история ещё загружается
|
||||
- **THEN** пользователь видит готовый результат без сообщения о догрузке остальной истории
|
||||
@@ -0,0 +1,52 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Контекстная индикация действий и фонового обновления
|
||||
|
||||
Frontend MUST сохранять уже доступное содержимое или элемент управления
|
||||
основным контекстом, когда выполняется пользовательское действие либо фоновое
|
||||
обновление Client Cached Snapshot. Явное действие пользователя SHALL иметь
|
||||
компактную индикацию ожидания рядом с соответствующим элементом управления.
|
||||
Фоновое обновление при доступном результате MUST проходить без индикаторов,
|
||||
технических сообщений и объявлений о ходе работы. В обоих случаях frontend
|
||||
MUST NOT заменять уже доступный контекст skeleton placeholders.
|
||||
|
||||
#### Scenario: Выполняется пользовательское действие
|
||||
- **WHEN** User запустил действие и его результат ещё ожидается
|
||||
- **THEN** frontend сохраняет соответствующий элемент управления или окружающий контекст видимым, показывает компактную индикацию процесса и не заменяет интерфейс skeleton placeholders
|
||||
|
||||
#### Scenario: Обновляется уже показанный snapshot
|
||||
- **WHEN** frontend обновляет данные при наличии видимого Client Cached Snapshot
|
||||
- **THEN** frontend оставляет snapshot видимым без индикации фонового обновления и без замены snapshot skeleton placeholders
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Первичное ожидание не становится бесконечной загрузкой
|
||||
|
||||
При отсутствии результата frontend SHALL сначала показывать структурное
|
||||
ожидание соответствующей области. После неуспешного завершения получения
|
||||
либо истечения ограниченного времени первичного ожидания область MUST переходить
|
||||
в спокойное локализованное состояние недоступности без технических подробностей
|
||||
и ручного повтора. Это состояние MUST NOT утверждать, что результат пустой,
|
||||
или требовать перезагрузки страницы. Продолжающееся автоматическое восстановление
|
||||
MUST NOT заставлять область постоянно переключаться между недоступностью и
|
||||
skeleton. После успешного получения содержимое SHALL появляться автоматически.
|
||||
|
||||
Отсутствие результата одной области MUST NOT скрывать остальные доступные
|
||||
области. Первичная загрузка MUST сохранять доступное обозначение занятости,
|
||||
а состояние недоступности MUST NOT продолжать объявляться бесконечно занятым.
|
||||
|
||||
#### Scenario: Первый результат успешно получен
|
||||
- **WHEN** экран ожидает первый результат и получает его в пределах первоначального ожидания
|
||||
- **THEN** структурные placeholders сменяются содержимым либо подтверждённым пустым состоянием
|
||||
|
||||
#### Scenario: Первое получение данных не удалось
|
||||
- **WHEN** у области нет результата и первоначальная загрузка завершилась ошибкой или превысила ограниченное время ожидания
|
||||
- **THEN** область показывает нейтральную недоступность без кнопки повтора, вымышленных данных и бесконечного индикатора занятости
|
||||
|
||||
#### Scenario: Восстановление продолжается после недоступности
|
||||
- **WHEN** область уже показывает недоступность и выполняется очередная разрешённая попытка восстановления
|
||||
- **THEN** область сохраняет устойчивое состояние без мигания skeleton и автоматически показывает содержимое после успеха
|
||||
|
||||
#### Scenario: Один блок недоступен
|
||||
- **WHEN** основной результат загружен, а дополнительный блок не получил данных
|
||||
- **THEN** состояние недоступности ограничено этим блоком и не заменяет доступное содержимое страницы
|
||||
@@ -0,0 +1,20 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Ожидаемые finance states не становятся route failures
|
||||
|
||||
Frontend SHALL сохранять локальное отображение известных состояний Brokerage
|
||||
Account, Portfolio и T-Bank connection. Эти состояния MUST NOT заменяться
|
||||
глобальным fallback. Временная ошибка фонового чтения MUST сохранять доступный
|
||||
результат без предупреждения об устаревании, технической ошибки и ручного
|
||||
повтора. При отсутствии результата SHALL показываться локальная недоступность
|
||||
с автоматическим восстановлением. Подтверждённая необходимость входа,
|
||||
восстановления подключения или выбора доступного счёта MUST сохранять понятное
|
||||
объяснение и соответствующее действие, не маскируясь сохранёнными личными данными.
|
||||
|
||||
#### Scenario: Portfolio Snapshot устарел
|
||||
- **WHEN** frontend получает устаревший Portfolio Snapshot в действующей границе доступа
|
||||
- **THEN** frontend отображает Portfolio без предупреждения об устаревании и без глобального fallback
|
||||
|
||||
#### Scenario: Ошибка T-Bank connection
|
||||
- **WHEN** запрос T-Bank connection завершается известной ошибкой
|
||||
- **THEN** frontend сохраняет локальное состояние: временный сбой чтения восстанавливается автоматически, а подтверждённая необходимость действия пользователя сопровождается этим действием вместо глобального fallback
|
||||
@@ -0,0 +1,112 @@
|
||||
## Purpose
|
||||
|
||||
Определяет спокойную и понятную обратную связь во всех разделах WealthLedger:
|
||||
пользователь видит финансовые данные и результат своих действий, а не состояние
|
||||
внутреннего процесса получения данных.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Чтение данных не требует внимания к техническим состояниям
|
||||
|
||||
На экранах счетов, портфеля, операций, доходов, событий, инструментов, поиска,
|
||||
карт и настроек подключения приложение MUST NOT показывать время получения
|
||||
данных, признаки их технического устаревания, ход фонового обновления и сообщения
|
||||
об успехе либо временной ошибке такого обновления. Приложение MUST NOT предоставлять
|
||||
кнопки или иные действия ручного обновления и повторной загрузки, включая их
|
||||
иконные варианты. Эти сведения MUST NOT переноситься в уведомления, подсказки
|
||||
или объявления вспомогательных технологий. Даты операций, выплат, открытия
|
||||
счёта и выбранных периодов MUST сохранять свой предметный смысл.
|
||||
|
||||
Запрет технических действий MUST NOT удалять применение фильтров, переходы между
|
||||
страницами, поиск, отправку формы, вход, подключение банка или безопасную навигацию.
|
||||
После неуспешного сохранения пользователь SHALL иметь возможность вновь отправить
|
||||
сохранённый ввод обычным действием формы.
|
||||
|
||||
#### Scenario: Фоновая загрузка завершилась ошибкой
|
||||
- **WHEN** на любом из перечисленных экранов уже есть доступный результат и его фоновое обновление временно не удалось
|
||||
- **THEN** результат остаётся видимым без предупреждения, времени получения, индикатора, уведомления и действия ручного повтора
|
||||
|
||||
#### Scenario: Фоновая загрузка завершилась успешно
|
||||
- **WHEN** приложение получает обновлённые данные без явного действия сохранения со стороны пользователя
|
||||
- **THEN** содержимое обновляется без сообщения об успешной загрузке и без переноса фокуса
|
||||
|
||||
#### Scenario: Просмотр списка карт и операций карты
|
||||
- **WHEN** временная ошибка возникает при обновлении уже показанных карт, сводки или операций выбранной карты
|
||||
- **THEN** соответствующий результат остаётся доступным без технического предупреждения и ручного повтора, а действия работы с картой сохраняются
|
||||
|
||||
#### Scenario: Поиск или новый диапазон инструмента пока недоступен
|
||||
- **WHEN** пользователь изменил поисковый запрос либо диапазон графика, а соответствующий результат ещё не получен
|
||||
- **THEN** экран показывает ожидание или локальную недоступность нового результата без кнопки повтора и не выдаёт результат прежнего запроса или диапазона за новый
|
||||
|
||||
#### Scenario: Предметные даты остаются видимыми
|
||||
- **WHEN** экран показывает дату операции, выплаты или выбранный период
|
||||
- **THEN** эти даты остаются доступны независимо от отсутствия времени технического получения данных
|
||||
|
||||
### Requirement: Краткое уведомление подтверждает неочевидный результат действия
|
||||
|
||||
Приложение SHALL показывать краткое локализованное уведомление об успешном
|
||||
явном действии пользователя, если после завершения на экране отсутствует
|
||||
однозначное подтверждение результата. Если результат уже очевиден по состоянию
|
||||
формы или содержимого, приложение MUST NOT дублировать его уведомлением.
|
||||
Уведомление MUST описывать выполненное действие, а не внутренний процесс.
|
||||
Ожидание действия MUST NOT представляться его успешным завершением.
|
||||
|
||||
#### Scenario: Результат действия иначе легко пропустить
|
||||
- **WHEN** действие успешно завершилось, его форма закрылась, а оставшийся экран не подтверждает результат однозначно
|
||||
- **THEN** пользователь получает одно краткое уведомление о выполненном действии
|
||||
|
||||
#### Scenario: Результат уже очевиден
|
||||
- **WHEN** успешное сохранение оставляет на экране явное подтверждение результата
|
||||
- **THEN** приложение не добавляет дублирующее уведомление
|
||||
|
||||
#### Scenario: Приём действия ещё не означает успех
|
||||
- **WHEN** приложение только начало выполнение действия и окончательный результат неизвестен
|
||||
- **THEN** оно не показывает уведомление об успешном завершении
|
||||
|
||||
### Requirement: Уведомления не мешают работе и не дублируются
|
||||
|
||||
Уведомления MUST быть доступны как текст, не полагаться только на цвет и не
|
||||
перехватывать клавиатурный фокус. Подтверждение успеха SHALL автоматически
|
||||
закрываться и позволять доступное ручное закрытие. Повторная обработка одного
|
||||
результата MUST NOT создавать повторное уведомление. Приложение MUST ограничивать
|
||||
число одновременно видимых уведомлений и MUST NOT создавать каскад сообщений
|
||||
из-за фонового восстановления данных.
|
||||
|
||||
#### Scenario: Один результат обработан повторно
|
||||
- **WHEN** приложение повторно обрабатывает результат того же пользовательского действия
|
||||
- **THEN** пользователь не получает второе уведомление об этом результате
|
||||
|
||||
#### Scenario: Работа с клавиатурой продолжается
|
||||
- **WHEN** уведомление появляется во время ввода или навигации с клавиатуры
|
||||
- **THEN** текущий фокус сохраняется, текст уведомления доступен вспомогательным технологиям, а действие закрытия имеет понятное доступное имя
|
||||
|
||||
#### Scenario: Несколько действий завершились рядом по времени
|
||||
- **WHEN** несколько различных действий успешно завершаются за короткое время
|
||||
- **THEN** уведомления соблюдают конечный предел одновременного показа и не блокируют доступ к содержимому и действиям страницы
|
||||
|
||||
### Requirement: Ошибки действий и препятствия доступа остаются понятными
|
||||
|
||||
Ошибка ввода MUST показываться у соответствующего поля. При неуспешном
|
||||
сохранении приложение MUST сохранять ввод и показывать понятную локальную ошибку
|
||||
в контексте действия, не объявляя его успешным. Если продолжение требует входа,
|
||||
подключения банка или другого действия пользователя, объяснение и применимое
|
||||
действие MUST оставаться доступны в соответствующей области. Исчезающее
|
||||
уведомление MUST NOT быть единственным способом сообщить такое препятствие.
|
||||
Приложение MUST NOT самостоятельно повторять изменение пользовательских данных
|
||||
только потому, что включено автоматическое восстановление чтения.
|
||||
|
||||
#### Scenario: Операция не сохранилась
|
||||
- **WHEN** пользователь отправил форму операции, а сохранение завершилось ошибкой
|
||||
- **THEN** введённые значения остаются в форме вместе с локальной ошибкой, обычное действие сохранения снова доступно и подтверждение успеха отсутствует
|
||||
|
||||
#### Scenario: Поле содержит недопустимое значение
|
||||
- **WHEN** пользователь пытается отправить форму с недопустимым значением
|
||||
- **THEN** ошибка относится к этому полю и не заменяется общим всплывающим уведомлением
|
||||
|
||||
#### Scenario: Требуется восстановить доступ
|
||||
- **WHEN** приложение подтвердило необходимость входа или восстановления подключения
|
||||
- **THEN** соответствующая область предлагает необходимое действие и сохраняет объяснение до устранения препятствия или ухода пользователя
|
||||
|
||||
#### Scenario: Ответ на изменение данных не получен
|
||||
- **WHEN** соединение прервалось после отправки пользовательского изменения и результат неизвестен
|
||||
- **THEN** приложение не объявляет успех и не повторяет изменение автоматически как обычную загрузку данных
|
||||
@@ -0,0 +1,73 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Состояния и ошибки Portfolio на клиенте
|
||||
|
||||
Клиент SHALL загружать Portfolio при открытии защищённого маршрута
|
||||
`/accounts/:accountId` и использовать успешный ответ в течение 30 секунд.
|
||||
Временные сбои и возвращение к экрану с требующими обновления данными SHALL
|
||||
обрабатываться автоматическим восстановлением по `frontend-data-resilience`.
|
||||
Клиент MUST NOT предоставлять ручное обновление или повторную загрузку.
|
||||
|
||||
При первичной загрузке без успешного результата того же Brokerage Account
|
||||
клиент MUST показывать структурный skeleton ожидаемого содержимого, включая
|
||||
сводку, без вымышленных сумм. Область MUST иметь доступное обозначение занятости;
|
||||
skeleton MUST NOT быть интерактивным или получать фокус. Неуспешное или
|
||||
затянувшееся первичное получение SHALL переходить в локальную недоступность
|
||||
по `frontend-loading-presentation`, а не в бесконечную загрузку.
|
||||
|
||||
Истечение срока свежести MUST NOT скрывать доступный Client Cached Snapshot.
|
||||
При обновлении и временной ошибке клиент MUST сохранять данные того же
|
||||
Portfolio, включая валютные итоги, без индикации фоновой работы, предупреждений,
|
||||
времени получения и уведомлений. Исходное `lastSuccessfulAt` и `isStale` MUST
|
||||
сохранять внутренний смысл: скрытие этих сведений MUST NOT делать сохранённый
|
||||
снимок новым успешным результатом или подтверждением текущего доступа.
|
||||
Server-side stale и ошибка клиентского чтения MUST NOT подменять друг друга.
|
||||
|
||||
Клиент MUST показывать штатное empty-состояние только при подтверждённом полном
|
||||
результате с пустыми `positions` и `cashBalances`, без вымышленных нулевых карточек.
|
||||
Неполный итог MUST NOT выдаваться за окончательный; доступные независимые данные
|
||||
MUST сохраняться на экране. При подтверждённом `404` клиент MUST скрыть личный
|
||||
Portfolio и предложить возврат к списку счетов. При `TBANK_NOT_CONNECTED` или
|
||||
`INVALID_TBANK_TOKEN` он MUST скрыть личные данные и предложить восстановление
|
||||
подключения. Эти состояния MUST NOT маскироваться сохранённым снимком или
|
||||
вызывать техническую повторную загрузку.
|
||||
|
||||
#### Scenario: Показ устаревшего Portfolio
|
||||
- **WHEN** клиент получает успешный ответ с `isStale: true` в действующей границе доступа
|
||||
- **THEN** клиент показывает позиции, остатки и доступные валютные итоги без времени последнего снимка, предупреждения и ручного обновления
|
||||
|
||||
#### Scenario: Недоступный счёт на клиенте
|
||||
- **WHEN** клиент получает подтверждённый `404` текущего Portfolio
|
||||
- **THEN** клиент не показывает личный Portfolio, включая ранее сохранённый, и предлагает вернуться к списку Brokerage Account без действия повтора
|
||||
|
||||
#### Scenario: Первичная загрузка Portfolio
|
||||
- **WHEN** клиент ожидает Portfolio этого Brokerage Account без ранее полученного успешного ответа
|
||||
- **THEN** он показывает структурный skeleton, включая место сводки, с доступным обозначением загрузки, без вымышленных или нулевых сумм и без фокусируемых placeholders
|
||||
|
||||
#### Scenario: Обновление после истечения срока свежести
|
||||
- **WHEN** срок свежести показанного Portfolio истёк и началось фоновое обновление данных того же Brokerage Account
|
||||
- **THEN** клиент сохраняет прежние валютные итоги без индикации обновления и не заменяет доступный состав и сводку skeleton-состоянием
|
||||
|
||||
#### Scenario: Ошибка обновления после успешного ответа
|
||||
- **WHEN** клиент уже показывает Client Cached Snapshot Portfolio, а обновление этих же данных завершается временной ошибкой
|
||||
- **THEN** клиент сохраняет данные и суммы без предупреждения и действия повтора; ошибка не делает снимок свежим и не изменяет `isStale`
|
||||
|
||||
#### Scenario: Ошибка клиентского обновления при server-side stale
|
||||
- **WHEN** клиент показывает Portfolio с `isStale: true`, а фоновое обновление этого же Portfolio завершается временной ошибкой
|
||||
- **THEN** прежние суммы остаются видимыми без технических предупреждений и ручных действий, сохраняя различие внутренних состояний
|
||||
|
||||
#### Scenario: Повторное обновление успешно
|
||||
- **WHEN** после временной ошибки автоматическое восстановление получает успешный Portfolio Snapshot
|
||||
- **THEN** до ответа прежние суммы остаются видимыми, затем клиент показывает новый результат без уведомления об обновлении и без переноса пользовательского фокуса
|
||||
|
||||
#### Scenario: Штатный пустой Portfolio на клиенте
|
||||
- **WHEN** успешно полученный полный Portfolio содержит пустые `positions` и `cashBalances`
|
||||
- **THEN** клиент показывает существующее empty-состояние без валютных карточек с вымышленными нулевыми итогами
|
||||
|
||||
#### Scenario: Первичный Portfolio пока недоступен
|
||||
- **WHEN** данных Portfolio нет, а первичное получение не удалось или превысило ограниченное время ожидания
|
||||
- **THEN** клиент показывает нейтральную недоступность без кнопки повтора и автоматически заменяет её содержимым после успешного восстановления
|
||||
|
||||
#### Scenario: Требуется восстановить подключение
|
||||
- **WHEN** клиент получает `TBANK_NOT_CONNECTED` или `INVALID_TBANK_TOKEN` при наличии сохранённого личного Portfolio
|
||||
- **THEN** личные данные скрываются, а клиент предлагает необходимое действие подключения без технической повторной загрузки
|
||||
@@ -0,0 +1,45 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Управление подключением в настройках
|
||||
|
||||
Клиент SHALL показывать в настройках статус T-Bank connection, текст о
|
||||
необходимости read-only T-Bank Invest token, поле token и действие подключения
|
||||
либо замены. Для существующего подключения клиент MUST показывать действие
|
||||
удаления с modal-подтверждением. Во время создания, замены или удаления все
|
||||
действия секции подключения и действия modal MUST быть отключены. После
|
||||
успешного создания или замены клиент MUST очистить поле token и обновить статус;
|
||||
при ошибке token MUST остаться в поле.
|
||||
|
||||
Временный сбой чтения статуса SHALL восстанавливаться автоматически без кнопки
|
||||
повтора и технического предупреждения. Пока первый статус неизвестен, клиент
|
||||
MUST NOT показывать форму как подтверждённое отсутствие подключения: после
|
||||
первичного ожидания SHALL отображаться нейтральная недоступность секции. Если
|
||||
статус уже известен для текущей сессии, временный сбой его фонового чтения MUST
|
||||
NOT скрывать доступную секцию или очищать ввод. Подтверждённое изменение
|
||||
сессии или доступа MUST NOT маскироваться старым статусом.
|
||||
|
||||
Ошибки создания, замены и удаления MUST оставаться локальными, сохранять ввод
|
||||
и возвращать применимые действия формы. Эти действия MUST NOT повторяться
|
||||
автоматически как чтение статуса. Подтверждения успеха SHALL следовать
|
||||
`frontend-user-feedback`: очевидное изменение статуса не требует дублирующего
|
||||
уведомления, неочевидный результат получает одно краткое подтверждение.
|
||||
|
||||
#### Scenario: Пользователь подтверждает удаление
|
||||
- **WHEN** User подтверждает удаление в modal
|
||||
- **THEN** клиент блокирует действия секции до результата и после успешного ответа показывает состояние без подключения
|
||||
|
||||
#### Scenario: Ошибка загрузки статуса
|
||||
- **WHEN** клиент временно не может получить статус подключения
|
||||
- **THEN** он автоматически восстанавливает чтение, сохраняет известный статус текущей сессии либо показывает нейтральную недоступность после первичного ожидания без формы и кнопки повтора
|
||||
|
||||
#### Scenario: Ошибка проверки token в клиенте
|
||||
- **WHEN** создание или замена получает `INVALID_TBANK_TOKEN` либо `TBANK_UNAVAILABLE`
|
||||
- **THEN** клиент показывает соответственно локализованное сообщение о token и правах либо о временной недоступности T-Bank в контексте формы, возвращает интерактивность и сохраняет введённый token
|
||||
|
||||
#### Scenario: Фоновое чтение не сбрасывает ввод
|
||||
- **WHEN** пользователь редактирует token в доступной секции, а фоновое чтение статуса временно не удалось
|
||||
- **THEN** ввод и доступная секция сохраняются без технического предупреждения или уведомления
|
||||
|
||||
#### Scenario: Удаление не удалось
|
||||
- **WHEN** пользователь подтвердил удаление подключения, а действие завершилось ошибкой
|
||||
- **THEN** клиент не показывает успех, сохраняет известный статус и локальную ошибку, возвращает применимые действия и не повторяет удаление автоматически
|
||||
@@ -0,0 +1,159 @@
|
||||
# Implementation tasks
|
||||
|
||||
Основание: согласованные Delta Specs и [Design](design.md), включая inline
|
||||
UI/UX package `quiet-user-feedback-ui-v1` и Solution Assessment.
|
||||
|
||||
Задачи выполняются по порядку групп и пунктов. Каждая задача имеет одного
|
||||
владельца репозитория; технические проверки выполняет implementer. Проверки
|
||||
сети используют synthetic fixtures и существующий MSW, а не реальный T-Bank.
|
||||
Перед Node-командами в каждом репозитории выполняется `nvm use --silent`.
|
||||
Указанные ниже проверки — план, а не свидетельство выполненной реализации.
|
||||
|
||||
Перед изменениями сохраняются чужие staged/unstaged/new files, в частности
|
||||
перенос PortfolioStructure. База требований сверяется по Migration Plan Design:
|
||||
изменение порядка интеграции `persist-tbank-data` требует согласования
|
||||
затронутых artifacts, а не молчаливого перезаписывания active change.
|
||||
Recovery и удаление ручных controls поставляются вместе. Backend и workspace
|
||||
не получают implementation tasks: они не входят в affected Delivery Map.
|
||||
|
||||
## 1. Принятые решения и зависимости
|
||||
|
||||
- [x] 1.1 [frontend] Применить Solution Assessment: оставить TanStack Query и существующих resource/screen owners, добавить exact `@mantine/notifications@9.6.1` с согласованным lockfile без второго query runtime или toast framework.
|
||||
- **Covers:** `N/A` — dependency preparation не вводит отдельного пользовательского поведения; реализует выбранные средства из Design §1 и §6 до custom recovery work.
|
||||
- **Verification:** `npm ci`; `npm ls @mantine/core @mantine/hooks @mantine/notifications react react-dom`; сверить exact versions, peer compatibility и отсутствие посторонних dependency changes.
|
||||
|
||||
## 2. Ограниченное восстановление чтения
|
||||
|
||||
- [x] 2.1 [frontend] В существующем `shared/api` реализовать классификацию временных GET failures, 15-секундный deadline с отменой чтения тела и до двух повторов с паузами 1/2 секунды; исключить auth endpoints, mutations, access/domain errors и отмену.
|
||||
- **Covers:** `frontend-data-resilience` — `Временные сбои чтения восстанавливаются автоматически`; `frontend-user-feedback` — `Ошибки действий и препятствия доступа остаются понятными`.
|
||||
- **Verification:** MSW-проверки через public client/query interface: recovery после network/timeout/503, конечное число запросов, прерванное чтение тела, отсутствие retries для 401/403/404/409 и malformed result, поведение 429 и серверной отсрочки по Design; `npm run test:run -- src/shared/api`.
|
||||
|
||||
- [x] 2.2 [frontend] Подключить read policy к authenticated query hooks и `useResourceQuery`, сохранив одну generated cache identity, независимые ordinary/observe режимы и существующие session/access fences; убрать локальные запреты retry только там, где они заменяются принятой политикой.
|
||||
- **Covers:** `frontend-data-resilience` — `Client Cached Snapshot остаётся доступным при обновлении Accounts и Portfolio`; `frontend-data-resilience` — `Временные сбои чтения восстанавливаются автоматически`.
|
||||
- **Verification:** Расширить resource lifecycle MSW-сценарии: cached snapshot при ошибке, `data: null` при HTTP success, ordinary GET после observe, unmount/cancel и late response после logout/connection replacement; `npm run test:run -- src/shared/api src/features/tbank-connection/model/connection-boundary`.
|
||||
|
||||
- [x] 2.3 [frontend] Перевести `useScreenSync` на ограниченное восстановление failed/cancelled missing/stale источников обычным GET: две дополнительные source attempts с паузами 5/15 секунд, бюджет на текущую выборку и отсутствие автоматического force POST.
|
||||
- **Covers:** `frontend-data-resilience` — `Временные сбои чтения восстанавливаются автоматически`; `frontend-data-resilience` — `Неполный результат не выдаётся за окончательный`.
|
||||
- **Verification:** MSW + управляемое время: failed source при HTTP 200, новая work ID без сброса бюджета, исчерпание серии, fresh complete после неудачного force, permanent unavailableReason; проверить число ordinary GET и отсутствие POST `/api/v1/tbank-sync`; `npm run test:run -- src/features/tbank-connection/model`.
|
||||
|
||||
- [x] 2.4 [frontend] Согласовать status observation и заключительное observe-чтение с in-flight запросами, сохранив интервал 1 секунда только для running work; различить status ID not found и подтверждённый отказ доступа к ресурсу.
|
||||
- **Covers:** `frontend-data-resilience` — `Временные сбои чтения восстанавливаются автоматически`; `frontend-data-resilience` — `Client Cached Snapshot остаётся доступным при обновлении Accounts и Portfolio`.
|
||||
- **Verification:** MSW-сценарии: медленное observe не отменяется каждым tick, terminal status не теряет заключительное чтение, HTTP/status failure не запускает force sync, `404` work ID перечитывает ресурс, access loss очищает личный snapshot; `npm run test:run -- src/features/tbank-connection/model/screen-sync.test.tsx src/shared/api/resource-query.test.tsx`.
|
||||
|
||||
- [x] 2.5 [frontend] Подключить единый lifecycle recovery на focus/reconnect и смену выборки: обычные reads используют штатную Query policy, инвестиционные — screen owner; отменять неактуальные timers и не дублировать восстановление при совпадении событий.
|
||||
- **Covers:** `frontend-data-resilience` — `Временные сбои чтения восстанавливаются автоматически`; `frontend-data-resilience` — `Client Cached Snapshot остаётся доступным при обновлении Accounts и Portfolio`.
|
||||
- **Verification:** Через Query focus/online managers и MSW проверить fresh/stale/missing results, offline/hidden pause, одновременный reconnect/focus, повторное открытие, переход на другой счёт/период и уход последнего consumer; `npm run test:run -- src/shared/api src/features/tbank-connection/model`.
|
||||
|
||||
## 3. Состояния ожидания и доступного результата
|
||||
|
||||
- [x] 3.1 [frontend] Реализовать общую presentation-only модель ограниченного первичного ожидания в `shared/lib`: 10-секундный budget, устойчивый unavailable latch для identity области и автоматический переход к результату; применить её к существующим loading/state presentation без второго кэша.
|
||||
- **Covers:** `frontend-loading-presentation` — `Первичное ожидание не становится бесконечной загрузкой`; `frontend-loading-presentation` — `Контекстная индикация действий и фонового обновления`.
|
||||
- **Verification:** Минимальная runnable проверка собственного state transition: skeleton → timeout/error unavailable → loaded, отсутствие мигания при retry, сброс только при новой выборке, cleanup и доступное busy-состояние; `npm run test:run -- src/shared/lib` и `npm run typecheck`.
|
||||
|
||||
- [x] 3.2 [frontend] В общих investment section models разделить readiness, полноту и access failure: успешные пустые данные не теряются, missing/partial не становятся нулевым итогом, а технические metadata остаются у владельца данных.
|
||||
- **Covers:** `frontend-data-resilience` — `Неполный результат не выдаётся за окончательный`; `frontend-ui-resilience` — `Ожидаемые finance states не становятся route failures`.
|
||||
- **Verification:** Проверки через потребляющие views: `null` против полного `[]`, partial rows против полного empty, недоступный total/comparison, independent ready section и access failure при cached content; `npm run test:run -- src/features/instrument-details src/pages/account-income`.
|
||||
|
||||
## 4. Инвестиционные экраны без технических controls
|
||||
|
||||
- [x] 4.1 [frontend] Перевести Accounts и AccountPage, включая «Все счета», на новые состояния: убрать даты получения, refresh/retry и технические предупреждения, сохранить валютные итоги, доступные snapshots, контекст и предметную навигацию.
|
||||
- **Covers:** `brokerage-accounts` — `Экран списка брокерских счетов`; `brokerage-accounts` — `Клиентские состояния ошибки списка счетов`; `portfolio-view` — `Состояния и ошибки Portfolio на клиенте`; `frontend-data-resilience` — `Client Cached Snapshot остаётся доступным при обновлении Accounts и Portfolio`; `frontend-ui-resilience` — `Ожидаемые finance states не становятся route failures`.
|
||||
- **Verification:** RTL/MSW: initial/loaded/empty/partial/unavailable, quiet refetch failure, сохранение раскрытий и focus, отсутствие вымышленных нулей, `404` и invalid token скрывают снимок; `npm run test:run -- src/pages/accounts src/pages/account src/app/routes/AppRoutes/AllAccounts.test.tsx`.
|
||||
|
||||
- [x] 4.2 [frontend] Перевести полный журнал и recent operations на локальное тихое восстановление; сохранить применённые фильтры, cursor navigation, даты и смысл строк, не подменять новую страницу прежней.
|
||||
- **Covers:** `brokerage-account-ledger` — `Account overview includes recent ledger events`; `brokerage-account-ledger` — `Журнал восстанавливает чтение без ручного обновления`.
|
||||
- **Verification:** RTL/MSW: background failure с прежними записями, missing новой страницы, отсутствие false empty и retry, локальный сбой preview не скрывает Portfolio; `npm run test:run -- src/pages/account-operations src/pages/account`.
|
||||
|
||||
- [x] 4.3 [frontend] Перевести страницу будущих событий и preview на сохранение доступного результата и нейтральную локальную недоступность; убрать технические статусы, сохранив даты и ограничения финансового прогноза.
|
||||
- **Covers:** `brokerage-account-upcoming-events` — `Account page presents an upcoming events list`; `brokerage-account-upcoming-events` — `Account overview includes upcoming events`.
|
||||
- **Verification:** RTL/MSW: cached events при временной ошибке, неизвестный результат против полного empty, offer без суммы, потеря доступа и независимость preview; `npm run test:run -- src/pages/account-events src/entities/brokerage-event src/pages/account`.
|
||||
|
||||
- [x] 4.4 [frontend] Перевести доход и current-month summary на тихое обновление; оставить независимые готовые результаты, недоступные comparisons без нулевой базы и неизменный пользовательский черновик периода.
|
||||
- **Covers:** `brokerage-account-income-analytics` — `Realized analytics never presents an incomplete result as complete`; `brokerage-account-income-analytics` — `Account overview has an independent current-month income summary`; `brokerage-account-income-analytics` — `Доход обновляется без технических сообщений`; `frontend-data-resilience` — `Неполный результат не выдаётся за окончательный`.
|
||||
- **Verification:** RTL/MSW: полный старый результат при временном сбое, missing/partial текущего периода, готовый доход с недоступным сравнением, смена периода без переименования прежних сумм, сохранение черновика; `npm run test:run -- src/pages/account-income src/pages/account`.
|
||||
|
||||
- [x] 4.5 [frontend] Убрать refresh/retry из поиска акций и облигаций, связав VM с автоматическим recovery и ожиданием текущего debounced query; сохранить фильтры и отсутствие подмены результата прежним запросом.
|
||||
- **Covers:** `frontend-user-feedback` — `Чтение данных не требует внимания к техническим состояниям`; `frontend-loading-presentation` — `Первичное ожидание не становится бесконечной загрузкой`.
|
||||
- **Verification:** RTL/MSW: смена query во время запроса, delayed/failed поиск с последующим восстановлением, отсутствие false no-results и технических controls; `npm run test:run -- src/features/instrument-search src/pages/stocks src/pages/bonds`.
|
||||
|
||||
- [x] 4.6 [frontend] Перевести детали акции/облигации и их секции на readiness текущей выборки: график, личные позиции, история, сведения и выплаты сохраняют доступное содержимое без технических дат и действий.
|
||||
- **Covers:** `frontend-user-feedback` — `Чтение данных не требует внимания к техническим состояниям`; `frontend-data-resilience` — `Неполный результат не выдаётся за окончательный`; `frontend-ui-resilience` — `Ожидаемые finance states не становятся route failures`.
|
||||
- **Verification:** RTL/MSW: independent sections, новый диапазон свечей и instrument ID не показывают прежний результат как новый, partial history не является полной; `npm run test:run -- src/features/instrument-details src/pages/stock src/pages/bond`.
|
||||
|
||||
## 5. Карты, формы и настройки подключения
|
||||
|
||||
- [x] 5.1 [frontend] Перевести CardsPage, CardPage и BankSummary на тихое чтение, ограниченное ожидание и сохранение текущей выборки; удалить placeholder прежней страницы при переходе к другой странице банковских операций.
|
||||
- **Covers:** `frontend-user-feedback` — `Чтение данных не требует внимания к техническим состояниям`; `frontend-data-resilience` — `Client Cached Snapshot остаётся доступным при обновлении Accounts и Portfolio`; `frontend-loading-presentation` — `Первичное ожидание не становится бесконечной загрузкой`.
|
||||
- **Verification:** RTL/MSW: карты/summary/history при refetch failure, `page` и период меняются без подмены данных, полный empty отличается от missing, формы не размонтируются из-за фоновой ошибки; `npm run test:run -- src/pages/cards src/pages/card/ui/CardPage src/entities/bank-card/api`.
|
||||
|
||||
- [x] 5.2 [frontend] Сохранить ввод и предметные действия BankCardForm и CategoryManager, заменив техническое перечитывание автоматической загрузкой; актуальную версию после конфликта принимать только явно, не повторяя запись.
|
||||
- **Covers:** `frontend-user-feedback` — `Ошибки действий и препятствия доступа остаются понятными`; `frontend-loading-presentation` — `Контекстная индикация действий и фонового обновления`.
|
||||
- **Verification:** RTL/MSW: field errors, сохранённый ввод при network failure, version conflict → автоматическое чтение → явное принятие версии, доступная отмена при unavailable, отсутствие второго mutation request; `npm run test:run -- src/entities/bank-card/ui`.
|
||||
|
||||
- [x] 5.3 [frontend] Перевести чтение категорий, исходной покупки и версий OperationForm/DeleteOperation на автоматическое восстановление; сохранить intent ID, refund constraints, явное подтверждение версии и недопущение повторной финансовой записи.
|
||||
- **Covers:** `frontend-user-feedback` — `Ошибки действий и препятствия доступа остаются понятными`; `frontend-loading-presentation` — `Контекстная индикация действий и фонового обновления`.
|
||||
- **Verification:** RTL/MSW: сбой зависимого GET без потери ввода, отсутствие ложного empty для расходов, конфликт удаления/правки, network loss после submit без auto-resubmit, сохранность version/intent при ручной повторной отправке; `npm run test:run -- src/pages/card/ui/OperationForm src/pages/card/ui/DeleteOperation src/entities/bank-card/api/banking.test.tsx`.
|
||||
|
||||
- [x] 5.4 [frontend] Перевести чтение T-Bank connection status на тихое восстановление: не показывать форму до первого известного статуса, сохранять уже открытую секцию и token при временном GET failure, оставить явные локальные ошибки mutations.
|
||||
- **Covers:** `tbank-connection` — `Управление подключением в настройках`; `frontend-user-feedback` — `Ошибки действий и препятствия доступа остаются понятными`; `frontend-ui-resilience` — `Ожидаемые finance states не становятся route failures`.
|
||||
- **Verification:** RTL/MSW: initial unavailable → loaded, сохранность ввода при фоновом сбое, single mutation на submit/delete, ошибки token и удаления, новая cache boundary только после успешной mutation; `npm run test:run -- src/features/tbank-connection`.
|
||||
|
||||
## 6. Редкие подтверждения действий
|
||||
|
||||
- [x] 6.1 [frontend] Подключить один Mantine Notifications host с общей темой, styles и i18n: limit 2, autoClose 4000 ms, desktop/mobile positions, доступное закрытие, reduced motion и очистка очереди при смене/завершении сессии.
|
||||
- **Covers:** `frontend-user-feedback` — `Уведомления не мешают работе и не дублируются`.
|
||||
- **Verification:** Проверить application-owned cleanup при logout/смене сессии через RTL; выполнить `npm run typecheck`; конфигурацию third-party limit/timer не дублировать unit-тестами библиотеки, доступность и размещение проверить в задаче 7.3.
|
||||
|
||||
- [x] 6.2 [frontend] В action VMs карт, операций и категорий отделить успех записи от последующего чтения и реализовать однократное подтверждение по таблице Design: видимый результат без toast, неочевидный — краткое сообщение с action ID и ограниченным ожиданием подтверждения.
|
||||
- **Covers:** `frontend-user-feedback` — `Краткое уведомление подтверждает неочевидный результат действия`; `frontend-user-feedback` — `Уведомления не мешают работе и не дублируются`; `frontend-user-feedback` — `Ошибки действий и препятствия доступа остаются понятными`.
|
||||
- **Verification:** RTL/MSW: сохранённая запись внутри/вне видимой выборки, удаление видимой/невидимой записи, успешная запись с неуспешным GET, 10-секундный предел подтверждения, повторный callback того же action ID и отдельное следующее действие; `npm run test:run -- src/entities/bank-card/ui src/pages/card src/pages/cards`.
|
||||
|
||||
- [x] 6.3 [frontend] В VM подключения добавить одно подтверждение успешной замены token при неизменном видимом статусе; создание/удаление с очевидным изменением статуса и все фоновые чтения оставить без toast.
|
||||
- **Covers:** `tbank-connection` — `Управление подключением в настройках`; `frontend-user-feedback` — `Краткое уведомление подтверждает неочевидный результат действия`; `frontend-user-feedback` — `Чтение данных не требует внимания к техническим состояниям`.
|
||||
- **Verification:** RTL/MSW: create/delete без дублирующего подтверждения, replace с единственным toast, failure без success, background recovery без notifications, сообщение без token; `npm run test:run -- src/features/tbank-connection/ui`.
|
||||
|
||||
## 7. Завершение frontend и UI-проверки
|
||||
|
||||
- [x] 7.1 [frontend] После миграции consumers удалить InvestmentRefreshState, SourceSyncStatus, неиспользуемые props/exports/строки и их устаревшие stories; проверить оставшиеся технические actions во всём scope без удаления предметных form/access controls.
|
||||
- **Covers:** `frontend-user-feedback` — `Чтение данных не требует внимания к техническим состояниям`; `frontend-loading-presentation` — `Контекстная индикация действий и фонового обновления`.
|
||||
- **Verification:** Проверить callers и i18n keys, отсутствие видимых/иконных/tooltip/live-region замен удалённых статусов; `npm run knip`; `npm run deps`; `npm run test:run -- src/test/storybook.test.tsx`; убедиться, что внутренние metadata и серверный контракт не удалены.
|
||||
|
||||
- [x] 7.2 [frontend] Обновить UI/data/testing conventions и значимые presentation stories под UI package v1: тихий loaded, нейтральный unavailable, partial без ложного итога и редкое подтверждение; сохранить изоляцию Storybook от application API/auth/query.
|
||||
- **Covers:** `N/A` — conventions и каталог закрепляют уже утверждённую архитектуру и UI-пакет, не вводя отдельного поведения; необходимы для согласованной frontend delivery из Design.
|
||||
- **Verification:** Сверить локальные инструкции с новыми retry/focus policies, примеры и ссылки; `npm run deps:storybook`; `npm run build:catalog`; проверить достижимость stories и отсутствие устаревшего обещания ручного retry.
|
||||
|
||||
- [x] 7.3 [frontend] Выполнить implementation-owned browser review состояний и interactions UI package v1 на 320, 768 и 1440 px: страницы чтения, partial/unavailable, формы, notification host и восстановление контекста.
|
||||
- **Covers:** `frontend-user-feedback` — `Уведомления не мешают работе и не дублируются`; `frontend-user-feedback` — `Чтение данных не требует внимания к техническим состояниям`; `frontend-loading-presentation` — `Первичное ожидание не становится бесконечной загрузкой`; `frontend-data-resilience` — `Client Cached Snapshot остаётся доступным при обновлении Accounts и Portfolio`.
|
||||
- **Verification:** В браузере проверить Tab/focus, modal trap и доступное закрытие, busy/live announcements, reduced motion, длинный текст, safe area, отсутствие перекрытых controls/горизонтального скролла, сохранение scroll и раскрытий; presentation states воспроизвести synthetic stories, runtime lifecycle — приложением и RTL/MSW, без browser MSW worker.
|
||||
|
||||
- [x] 7.4 [frontend] Завершить Boy Scout review затронутого diff и выполнить полный canonical frontend gate после всех production, test, dependency и convention изменений.
|
||||
- **Covers:** `N/A` — финальная проверка frontend delivery по Design, без нового observable behavior; targeted tests не заменяют canonical gate.
|
||||
- **Verification:** `npm ci` и `npm run verify`; отдельно сверить сохранность чужого PortfolioStructure diff, отсутствие generated/OpenAPI drift, дублированных query owners и global toast interception; записать фактические результаты и ограничения.
|
||||
|
||||
## 8. As-built документация
|
||||
|
||||
- [x] 8.1 [docs] После frontend delivery оформить принятое решение о тихом чтении и редких уведомлениях в существующем журнале ADR, с причинами, альтернативами, последствиями и связью с предыдущим решением о background sync.
|
||||
- **Covers:** `N/A` — ADR фиксирует rationale уже утверждённого Design и выполненной delivery, а не создаёт новую behavioral spec; docs отмечен affected в Delivery Map.
|
||||
- **Verification:** Проверить номер по актуальному `adr/README.md`, самостоятельность текста, связь с ADR 017 и сохранность его исторического содержания; новый ADR описывает реализованный механизм и не обещает непоставленное поведение.
|
||||
|
||||
- [x] 8.2 [docs] Обновить объяснения UI resilience, data freshness и visual states, убрав описание обязательных предупреждений/ручного обновления и объяснив ограниченное автоматическое восстановление, первичную недоступность и сохранение ошибок действий.
|
||||
- **Covers:** `N/A` — as-built тематические страницы объясняют согласованные требования и технические решения; это documentation outcome Design, без самостоятельного нового поведения.
|
||||
- **Verification:** Сверить `architecture/cross-cutting/ui-resilience.md`, `architecture/cross-cutting/data-freshness.md` и `design/overview.md` с реализованным flow, исключить противоречия между freshness, completeness и UI; проверить локальные ссылки и предметные даты.
|
||||
|
||||
- [x] 8.3 [docs] Согласовать затронутые предметные описания счетов, портфеля, операций, дохода, событий, инструментов и карт с тихим UI; обновить только фрагменты, описывающие изменённые состояния и recovery.
|
||||
- **Covers:** `N/A` — предметная документация устраняет устаревшие инструкции пользователю после frontend delivery; область и поведение уже заданы Design и Delta Specs.
|
||||
- **Verification:** Пройти маршруты README к затронутым domains; сверить упоминания refresh/retry, времени получения и ошибок с приложением, сохранив полезные финансовые пояснения, ограничения прогнозов и входящие ссылки.
|
||||
|
||||
- [x] 8.4 [docs] Выполнить semantic/link review обновлённой документации и canonical docs gate с учётом новых файлов.
|
||||
- **Covers:** `N/A` — проверка документационной delivery из Design; не является новой пользовательской capability.
|
||||
- **Verification:** `npm ci` при отсутствии установленных dependencies, затем `npm run verify`; проверить ADR index, новые локальные anchors и пользовательские маршруты чтения; если затронуты диаграммы, отдельно проверить их рендер доступным средством.
|
||||
|
||||
## 9. Canonical specifications и итоговые gates
|
||||
|
||||
- [x] 9.1 [specs] После подтверждения реализации перенести согласованные quiet Delta Requirements в canonical specs через OpenSpec sync workflow и добавить `frontend-user-feedback` в AI_INDEX; учесть порядок пересечения с persist по Design, сохраняя несвязанные требования.
|
||||
- **Covers:** `N/A` — canonical publication не вводит поведения сверх утверждённых Delta Specs; реализует обязательный specs outcome Delivery Map и сохраняет единственный источник требований.
|
||||
- **Verification:** Сравнить перенесённые Requirement blocks со всеми десятью delta paths, проверить отсутствие восстановления старых UI/retry obligations и потери несвязанных scenarios; проверить новый target в AI_INDEX; при изменившейся базе вернуть затронутые artifacts на согласование до переноса.
|
||||
|
||||
- [x] 9.2 [specs] Выполнить canonical specs gate, native schema/change/spec validation и итоговую сверку трассировки с реально выполненными задачами.
|
||||
- **Covers:** `N/A` — проверка полноты и согласованности specs delivery, обязательная по Design и местным правилам; нового observable behavior нет.
|
||||
- **Verification:** `npm run verify`; `openspec schema validate wealth-ledger-sdd --verbose`; `openspec validate quiet-user-feedback --type change --strict --store wealth-ledger-specs`; `openspec validate --specs --strict --store wealth-ledger-specs`; проверить новые файлы и их diff отдельно, а checklist отмечать только по наблюдённым результатам.
|
||||
Reference in New Issue
Block a user