spec: archive loading skeleton change
This commit is contained in:
+2
@@ -0,0 +1,2 @@
|
||||
schema: wealth-ledger-sdd
|
||||
created: 2026-08-27
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
## Context
|
||||
|
||||
Frontend уже разделяет запросные экраны на container, ViewModel и отдельные
|
||||
представления состояний. ViewModel отличает начальную загрузку без данных от
|
||||
фонового обновления Client Cached Snapshot, а pending-состояния действий
|
||||
остаются у соответствующих форм и controls. Сейчас эти разные состояния
|
||||
визуально смешаны: top-level loading views и route fallback используют общий
|
||||
spinner, некоторые частичные блоки показывают loading-текст, а auth guards до
|
||||
завершения bootstrap не рендерят ничего.
|
||||
|
||||
Mantine уже является UI dependency и предоставляет Skeleton, layout и progress
|
||||
primitives. Поэтому изменение должно остаться в presentation boundaries,
|
||||
сохранить текущие ViewModel, query/cache и auth transitions и не вводить новый
|
||||
loading framework.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Сделать initial, partial, lazy-route и auth-bootstrap loading states
|
||||
структурно похожими на ожидаемую область содержимого.
|
||||
- Сохранить единое доступное обозначение занятой области и исключить skeleton
|
||||
placeholders из интерактивной и focus-модели.
|
||||
- Оставить уже показанные данные и controls основным контекстом для cached
|
||||
refresh и pending mutations.
|
||||
- Удержать ответственность за форму skeleton рядом с соответствующим app,
|
||||
page или section view.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Изменение запросов, cache timing, state selection, auth/route decisions,
|
||||
error/empty states или доменных данных.
|
||||
- Замена компактной индикации внутри pending controls и рядом с видимым Client
|
||||
Cached Snapshot.
|
||||
- Новый UI framework, глобальная orchestration загрузки или редизайн loaded
|
||||
screens.
|
||||
- Изменение backend, API contract или persistence.
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Loading presentation принадлежит существующим view boundaries
|
||||
|
||||
Top-level page loading остаётся отдельным state view, выбранным существующим
|
||||
container по состоянию ViewModel. Каждый такой view строит skeleton из тех же
|
||||
типов layout regions, что и соответствующий loaded screen: heading, cards,
|
||||
filters, summaries или rows. Частичный skeleton принадлежит section view и
|
||||
занимает только незагруженный блок, поэтому соседнее готовое содержимое не
|
||||
скрывается.
|
||||
|
||||
Это сохраняет установленную границу `container -> ViewModel -> view` и позволяет
|
||||
форме placeholder следовать реальной семантике экрана. Альтернатива — заменить
|
||||
общий loading StateView одним универсальным skeleton — отклонена: универсальная
|
||||
форма не может одновременно правдоподобно представлять список счетов,
|
||||
Portfolio, ledger и локальные sections и быстро разойдётся с loaded layouts.
|
||||
|
||||
Общий StateView сохраняется для error и empty outcomes, но перестаёт владеть
|
||||
initial loading. Его loading-вариант удаляется после перевода всех consumers,
|
||||
чтобы spinner-based primary state нельзя было случайно продолжать использовать.
|
||||
|
||||
### 2. Используются установленные Mantine primitives без новой абстракции
|
||||
|
||||
Skeleton shapes собираются из Mantine Skeleton и текущих layout primitives.
|
||||
Занятая область получает единый доступный status/busy contract и локализованное
|
||||
имя, а сами визуальные placeholders остаются декоративными, не содержат fake
|
||||
values и не создают focus targets. Custom animation не добавляется.
|
||||
|
||||
Низкоуровневая shared-обёртка допустима только для повторяющейся busy-семантики;
|
||||
структура cards, sections и rows остаётся в page/entity presentation layers.
|
||||
Альтернативы в виде собственной CSS skeleton-системы или новой dependency
|
||||
отклонены, потому что установленная библиотека уже покрывает нужные primitives
|
||||
и снижает surface поддержки.
|
||||
|
||||
### 3. Route loading и auth bootstrap остаются app-level concerns
|
||||
|
||||
Suspense boundaries в routing layer получают neutral structural fallbacks для
|
||||
ожидаемой области: auth-card shape для public auth routes, application-content
|
||||
shape внутри доступного shell и neutral shell shape там, где сам shell ещё не
|
||||
доступен. Уже отрисованный application shell не заменяется при загрузке
|
||||
дочернего route chunk.
|
||||
|
||||
Auth guards продолжают принимать решение только по существующему `ready` и
|
||||
`user`. Пока `ready` ложен, они показывают neutral shell skeleton без profile,
|
||||
navigation result или route content; после bootstrap текущий redirect/render
|
||||
flow продолжается без изменений. Перенос bootstrap в router data APIs или
|
||||
изменение auth ownership отклонены как несоразмерные визуальной задаче.
|
||||
|
||||
### 4. Contextual progress не переводится на skeleton
|
||||
|
||||
Button loading для submit/remove/update и компактные indicators фонового
|
||||
обновления сохраняются. Когда Client Cached Snapshot существует, loaded/empty
|
||||
view остаётся на экране, а текущая локальная refresh-индикация и recovery flow
|
||||
продолжают работать рядом с ним. Skeleton выбирается только для состояния без
|
||||
доступного результата либо для отдельно незагруженного content block.
|
||||
|
||||
Так state choice остаётся детерминированным: наличие usable content имеет
|
||||
приоритет над skeleton. Альтернатива показывать skeleton поверх cached data
|
||||
отклонена, потому что она скрывает полезный контекст и смешивает initial loading
|
||||
с background refresh.
|
||||
|
||||
### 5. Проверяется наблюдаемая граница, а не разметка библиотеки
|
||||
|
||||
Risk-based UI coverage фиксирует выбор skeleton для initial/partial и auth/route
|
||||
loading, сохранение loaded context для refresh/pending states и отсутствие
|
||||
интерактивности у placeholders. Тесты не закрепляют внутренний DOM Mantine или
|
||||
точные декоративные размеры; layout fidelity подтверждается scoped
|
||||
presentation review на существующих responsive boundaries.
|
||||
|
||||
## Delivery Map
|
||||
|
||||
| Area | Status | Architectural outcome or N/A reason |
|
||||
| --- | --- | --- |
|
||||
| frontend | `affected` | App, page и section presentation boundaries используют структурные skeletons для initial/partial loading, сохраняя contextual progress и текущие state machines. |
|
||||
| backend | `N/A` | Запросы, ответы, auth semantics, данные и server-side поведение не меняются. |
|
||||
| specs | `affected` | Новый capability contract `frontend-loading-presentation` остаётся canonical описанием loading invariants. |
|
||||
| docs | `affected` | После поставки as-built frontend и feature documentation отражает различие skeleton loading и contextual progress без копирования planning artifacts. |
|
||||
|
||||
### Contract Gate Decision
|
||||
|
||||
| Gate | Status | Reason |
|
||||
| --- | --- | --- |
|
||||
| runtime-openapi | `N/A` | Изменяется только frontend presentation; ни backend API, ни frontend contract snapshot не меняются. |
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Skeleton расходится с loaded layout.** Page- и section-owned shapes следуют
|
||||
тем же semantic regions и responsive containers; декоративные точные размеры
|
||||
не превращаются в отдельный общий contract.
|
||||
- **Skeleton ошибочно заменяет полезный cached context.** Существующая ViewModel
|
||||
граница initial loading versus `isFetching` сохраняется, а contextual
|
||||
indicators остаются отдельным presentation path.
|
||||
- **Auth bootstrap показывает неверный пользовательский flow.** Neutral shell
|
||||
не содержит profile или route outcome, а существующие guards остаются
|
||||
единственным владельцем решения после `ready`.
|
||||
- **Декоративные placeholders создают шум assistive technology.** Busy status
|
||||
назначается области целиком; внутренние shapes не интерактивны и не выдают
|
||||
fake content.
|
||||
- **Больше специализированных view components.** Дополнительные небольшие views
|
||||
приняты как цена точного соответствия разным экранам; общий generic skeleton
|
||||
оставил бы меньше файлов, но больше условностей и visual drift.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Ввести app-level neutral shell/content fallbacks и согласовать общую
|
||||
busy-семантику на существующих Mantine primitives.
|
||||
2. Перевести top-level page loading views и partial section states на
|
||||
принадлежащие им skeleton shapes, не меняя ViewModel и data flow.
|
||||
3. Подключить route и auth-bootstrap fallbacks, сохранив текущие redirect и
|
||||
session transitions.
|
||||
4. Удалить устаревший generic spinner-loading path после отсутствия consumers;
|
||||
compact mutation и cached-refresh indicators оставить без изменений.
|
||||
5. После подтверждения поведения обновить durable as-built documentation.
|
||||
|
||||
### Rollback Contract
|
||||
|
||||
| Field | Decision |
|
||||
| --- | --- |
|
||||
| Status | `required` |
|
||||
| Reason | Auth decision не меняется, но новый neutral fallback отображается до завершения session bootstrap и поэтому должен иметь явно безопасный путь отката. |
|
||||
| Trigger | Bootstrap fallback показывает public/protected route content до `ready`, нарушает redirect после определения сессии либо создаёт блокирующую accessibility regression. |
|
||||
| Actions | Вернуть auth guards к прежнему blank fallback, сохранив существующие `ready`/`user` decisions; остальные page и section skeletons можно оставить независимо. |
|
||||
| Data safety | Откат меняет только presentation; session tokens, query cache, backend data и persistence не преобразуются. |
|
||||
| Verification | Подтвердить отсутствие route content до `ready`, прежние public/protected redirects после bootstrap и успешный frontend canonical gate. |
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
## Why
|
||||
|
||||
Текущие крупные индикаторы загрузки прерывают визуальный контекст и не дают
|
||||
пользователю понять, какая часть интерфейса появится после завершения ожидания.
|
||||
Для основных переходов и первичной загрузки нужен единый подход, который
|
||||
сохраняет структуру экрана и делает ожидание менее резким.
|
||||
|
||||
## What Changes
|
||||
|
||||
- При первичной и частичной загрузке содержимого пользователь видит
|
||||
placeholders, повторяющие ожидаемую структуру соответствующего экрана или
|
||||
блока, вместо отдельного крупного индикатора процесса.
|
||||
- Такой же принцип применяется во время загрузки отложенно открываемых
|
||||
маршрутов и начального восстановления пользовательской сессии.
|
||||
- Компактные индикаторы сохраняются для выполняющихся пользовательских
|
||||
действий и фонового обновления уже показанных данных: в этих случаях
|
||||
существующее содержимое или элемент управления остаются основным контекстом.
|
||||
- Не меняются результаты загрузки, обработка ошибок и пустых состояний,
|
||||
навигационные правила, операции с данными или доменное поведение.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `frontend-loading-presentation`: Единое наблюдаемое представление первичной и
|
||||
частичной загрузки содержимого, загрузки маршрутов и восстановления сессии с
|
||||
сохранением компактной индикации для действий и фонового обновления.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
<!-- Existing capability requirements do not prescribe the loading presentation
|
||||
being replaced; their data, cache, navigation and error semantics remain
|
||||
unchanged. -->
|
||||
|
||||
## Impact
|
||||
|
||||
Изменение затрагивает пользователей frontend во время открытия экранов,
|
||||
ожидания отдельных блоков и запуска приложения. Оно охватывает клиентское
|
||||
представление loading-состояний, каноническое описание нового поведения и
|
||||
пользовательскую as-built документацию; backend, API и доменная модель не
|
||||
изменяются.
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
## Purpose
|
||||
|
||||
Определяет единое пользовательское представление ожидания во frontend, чтобы
|
||||
структура будущего содержимого оставалась понятной без потери уже доступного
|
||||
контекста.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Структурные placeholders для загрузки содержимого
|
||||
|
||||
Frontend SHALL при первичной загрузке содержимого без доступного результата
|
||||
показывать skeleton placeholders, которые передают расположение и визуальную
|
||||
иерархию основных ожидаемых блоков. При частичной загрузке frontend MUST
|
||||
ограничивать skeleton незагруженным блоком и сохранять уже доступные части
|
||||
экрана видимыми. В этих состояниях frontend MUST NOT использовать отдельный
|
||||
spinner как основное представление ожидаемого содержимого.
|
||||
|
||||
Skeleton placeholders MUST NOT содержать правдоподобные пользовательские
|
||||
данные, быть интерактивными или попадать в последовательность клавиатурного
|
||||
фокуса; область ожидания MUST оставаться доступно обозначенной как занятая.
|
||||
|
||||
#### Scenario: Экран впервые ожидает содержимое
|
||||
|
||||
- **WHEN** User открывает экран, для которого ещё нет доступного результата
|
||||
- **THEN** frontend показывает структурные skeleton placeholders основных блоков без отдельного spinner в роли основного loading-состояния
|
||||
|
||||
#### Scenario: Загружается отдельный блок экрана
|
||||
|
||||
- **WHEN** часть экрана уже доступна, а один из его содержательных блоков всё ещё ожидает результат
|
||||
- **THEN** frontend сохраняет доступные части видимыми и показывает skeleton только в границах ожидающего блока
|
||||
|
||||
#### Scenario: Пользователь перемещает клавиатурный фокус во время загрузки
|
||||
|
||||
- **WHEN** на экране видны skeleton placeholders и User перемещает клавиатурный фокус
|
||||
- **THEN** placeholders не получают фокус и не воспринимаются как доступные действия или реальные пользовательские данные
|
||||
|
||||
### Requirement: Представление загрузки маршрута и восстановления сессии
|
||||
|
||||
Frontend SHALL во время загрузки отложенно открываемого маршрута показывать
|
||||
skeleton ожидаемой области содержимого, сохраняя уже доступный каркас
|
||||
приложения. Во время начального восстановления пользовательской сессии
|
||||
frontend MUST показывать нейтральный skeleton каркаса до определения доступного
|
||||
пользователю потока и MUST NOT кратковременно показывать содержимое public или
|
||||
protected маршрута как результат загрузки. В обоих случаях отдельный spinner
|
||||
MUST NOT быть основным loading-состоянием.
|
||||
|
||||
#### Scenario: Загружается отложенно открываемый маршрут
|
||||
|
||||
- **WHEN** User переходит на маршрут, содержимое которого ещё загружается
|
||||
- **THEN** frontend сохраняет доступный каркас приложения и показывает skeleton в ожидаемой области содержимого без отдельного spinner в роли основного loading-состояния
|
||||
|
||||
#### Scenario: Приложение восстанавливает сессию при запуске
|
||||
|
||||
- **WHEN** frontend ещё не определил результат начального восстановления пользовательской сессии
|
||||
- **THEN** User видит нейтральный skeleton каркаса без преждевременного отображения public или protected содержимого
|
||||
|
||||
### Requirement: Контекстная индикация действий и фонового обновления
|
||||
|
||||
Frontend MUST сохранять уже доступное содержимое или элемент управления
|
||||
основным контекстом, когда выполняется пользовательское действие либо фоновое
|
||||
обновление Client Cached Snapshot. В этих состояниях frontend SHALL показывать
|
||||
компактный progress indicator рядом с соответствующим контекстом и MUST NOT
|
||||
заменять этот контекст skeleton placeholders.
|
||||
|
||||
#### Scenario: Выполняется пользовательское действие
|
||||
|
||||
- **WHEN** User запустил действие и его результат ещё ожидается
|
||||
- **THEN** frontend сохраняет соответствующий элемент управления или окружающий контекст видимым, показывает компактную индикацию процесса и не заменяет интерфейс skeleton placeholders
|
||||
|
||||
#### Scenario: Обновляется уже показанный snapshot
|
||||
|
||||
- **WHEN** frontend обновляет данные при наличии видимого Client Cached Snapshot
|
||||
- **THEN** frontend оставляет snapshot видимым, показывает компактную индикацию фонового обновления и не заменяет snapshot skeleton placeholders
|
||||
@@ -0,0 +1,53 @@
|
||||
## 1. App-level loading boundaries
|
||||
|
||||
- [x] 1.1 [frontend] Заменить route Suspense fallbacks на structural skeleton variants для public auth routes, ещё не загруженного application shell и content area внутри уже доступного shell.
|
||||
- **Covers:** `frontend-loading-presentation` — `Представление загрузки маршрута и восстановления сессии`
|
||||
- **Verification:** Route UI tests подтверждают подходящий skeleton fallback и сохранение уже отрисованного shell без проверки внутренней DOM-разметки Mantine.
|
||||
- [x] 1.2 [frontend] Показывать neutral shell skeleton в public и protected auth guards до `ready`, сохранив существующие session decisions и redirects после bootstrap.
|
||||
- **Covers:** `frontend-loading-presentation` — `Представление загрузки маршрута и восстановления сессии`
|
||||
- **Verification:** Auth/route tests подтверждают отсутствие public и protected route content до `ready` и прежние redirects для authenticated и unauthenticated outcomes.
|
||||
|
||||
## 2. Content loading presentation
|
||||
|
||||
- [x] 2.1 [frontend] Реализовать page-owned initial skeleton views для Accounts и Portfolio с layout regions, соответствующими их loaded headings, cards, summaries и sections.
|
||||
- **Covers:** `frontend-loading-presentation` — `Структурные placeholders для загрузки содержимого`
|
||||
- **Verification:** Page UI tests подтверждают accessible busy state, отсутствие primary spinner/fake content/focus targets и сохранение существующих error, empty и loaded outcomes.
|
||||
- [x] 2.2 [frontend] Реализовать page-owned initial skeleton views для Operations и Events с формой ожидаемых filters и rows, не меняя их ViewModel или request flow.
|
||||
- **Covers:** `frontend-loading-presentation` — `Структурные placeholders для загрузки содержимого`
|
||||
- **Verification:** Page UI tests подтверждают structural skeleton только при отсутствии результата и прежний переход к error, empty и loaded views.
|
||||
- [x] 2.3 [frontend] Заменить loading-текст в recent operations, recent events и T-Bank connection на section-owned skeletons, ограниченные незагруженным блоком.
|
||||
- **Covers:** `frontend-loading-presentation` — `Структурные placeholders для загрузки содержимого`
|
||||
- **Verification:** Section UI tests подтверждают, что skeleton занимает только ожидающий блок, соседний page context остаётся видимым, а placeholders не интерактивны.
|
||||
- [x] 2.4 [frontend] Удалить generic spinner-loading mode из StateView после миграции consumers и сохранить Button loading и локальные refresh indicators для pending mutations и Client Cached Snapshot.
|
||||
- **Covers:** `frontend-loading-presentation` — `Контекстная индикация действий и фонового обновления`
|
||||
- **Verification:** UI tests подтверждают сохранение controls или cached content вместе с compact progress и отсутствие skeleton replacement в mutation/refetch states.
|
||||
- [x] 2.5 [frontend] Обновить frontend UI pattern documentation в соответствии с реализованной границей initial/partial skeleton versus contextual progress.
|
||||
- **Covers:** `frontend-loading-presentation` — `Структурные placeholders для загрузки содержимого`; `frontend-loading-presentation` — `Контекстная индикация действий и фонового обновления`
|
||||
- **Verification:** `frontend/docs/UI-PATTERN.md` описывает фактическое ownership loading views и не меняет data/auth/router semantics.
|
||||
|
||||
## 3. Affected repository delivery
|
||||
|
||||
- [x] 3.1 [frontend] Выполнить canonical frontend gate и scoped responsive/accessibility review всех новых app, page и section skeleton states.
|
||||
- **Covers:** `N/A` — canonical gate и presentation review подтверждают delivery утверждённого frontend Design, но не вводят самостоятельное observable behavior.
|
||||
- **Verification:** Под Node 22 выполнить `npm run verify` в `frontend`; проверить narrow и wide layouts, busy semantics, keyboard focus и rollback-safe auth bootstrap fallback.
|
||||
- [x] 3.2 [docs] Обновить as-built system overview и затронутые authentication, accounts и Portfolio feature docs, описав поставленное различие structural skeleton и contextual progress.
|
||||
- **Covers:** `frontend-loading-presentation` — `Структурные placeholders для загрузки содержимого`; `frontend-loading-presentation` — `Представление загрузки маршрута и восстановления сессии`; `frontend-loading-presentation` — `Контекстная индикация действий и фонового обновления`
|
||||
- **Verification:** Affected docs описывают только фактический пользовательский flow, сохраняют локальные ссылки и не копируют Proposal, Delta Specs, Design или Tasks.
|
||||
- [x] 3.3 [docs] Выполнить canonical docs gate после as-built updates.
|
||||
- **Covers:** `N/A` — non-mutating docs gate проверяет affected delivery area и не добавляет новое product behavior.
|
||||
- **Verification:** Под Node 22 выполнить `npm run verify` в `docs`.
|
||||
- [x] 3.4 [specs] Сверить завершённую реализацию и отмеченные Tasks с каждым Requirement canonical delta, сохранив approved semantics либо маршрутизировав semantic mismatch обратно в Specification.
|
||||
- **Covers:** `frontend-loading-presentation` — `Структурные placeholders для загрузки содержимого`; `frontend-loading-presentation` — `Представление загрузки маршрута и восстановления сессии`; `frontend-loading-presentation` — `Контекстная индикация действий и фонового обновления`
|
||||
- **Verification:** Requirement-by-requirement comparison не оставляет unchecked implementation scope или behavior, отсутствующее в approved artifacts.
|
||||
- [x] 3.5 [specs] Выполнить native strict validation change и canonical main specs.
|
||||
- **Covers:** `N/A` — structural validation является canonical gate affected specs repository, а не самостоятельным пользовательским behavior.
|
||||
- **Verification:** Выполнить `openspec validate replace-loading-spinners-with-skeletons --type change --strict --store wealth-ledger-specs` и `openspec validate --specs --strict --store wealth-ledger-specs`.
|
||||
|
||||
## 4. Cross-area verification
|
||||
|
||||
- [x] 4.1 [workspace] Семантически проверить frontend и docs delivery против Proposal, Delta Specs, Design и полного Tasks checklist после успешных affected-area gates.
|
||||
- **Covers:** `N/A` — cross-repository semantic verification сопоставляет уже утверждённый scope и delivery, не владея implementation ни одной repository area.
|
||||
- **Verification:** Зафиксирована requirement-by-requirement проверка initial/partial, route/auth и contextual-progress behavior без незапланированных отклонений.
|
||||
- [x] 4.2 [workspace] Провести независимый read-only review завершённого change после semantic verification.
|
||||
- **Covers:** `N/A` — independent review является обязательной cross-area completion проверкой и не создаёт product behavior или repository-owned implementation.
|
||||
- **Verification:** Review охватывает implementation, canonical artifacts, accessibility, rollback boundary и affected docs; Critical и Important findings отсутствуют либо change возвращён в ответственную фазу.
|
||||
Reference in New Issue
Block a user