docs(specs): archive consume-mantine-components change
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
schema: wealth-ledger-sdd
|
||||||
|
created: 2026-08-27
|
||||||
|
skip_specs: true
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
## Context
|
||||||
|
|
||||||
|
В `frontend` UI почти полностью построен на `@mantine/core` (v9.5.1). Единственное место с собственной ручной реализацией хлебных крошек — `src/app/ui/AppBreadcrumbs.tsx`: внутренний `BreadcrumbTrail` вручную рендерит `<nav aria-label>` → `<span class="breadcrumbs-*">` → сепаратор «→», плюс кастомные классы `breadcrumbs`, `breadcrumbs-entry`, `breadcrumbs-separator-N`, `breadcrumbs-item-N` и медиа-правило `data-depth="3"` в `src/app/styles.css`. Тест `src/app/ui/AppBreadcrumbs.test.tsx` проверяет `data-depth`, `aria-current`, `aria-label`, navigation role и ссылки.
|
||||||
|
|
||||||
|
Mantine `Breadcrumbs` (v9.5.1) — штатный аналог: рендерит children с сепаратором между ними, поддерживает `separator`, `separatorMargin` и произвольные children (элементы или текст). Рендерится как `<Box>` (`<div>` по умолчанию), принимает `component` и `data-*`/aria-атрибуты. Нативно НЕ скрывает промежуточные крошки по глубине и НЕ выставляет `aria-current` — адаптивность и текущую крошку нужно сохранить на уровне представления/CSS.
|
||||||
|
|
||||||
|
Мобильная навигация `AppShell` (`bottom-nav`) выходит из scope по решению Change Owner — остаётся кастомной.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:**
|
||||||
|
- Заменить ручную разметку `BreadcrumbTrail` на штатный `Mantine Breadcrumbs`.
|
||||||
|
- Сохранить идентичное пользовательское и доступное поведение: те же цепочки, ссылки только у предшествующих крошек, текущая страница — текст, `aria-label`, navigation role, `data-depth`, адаптивное скрытие промежуточных крошек на узком экране.
|
||||||
|
|
||||||
|
**Non-Goals:**
|
||||||
|
- Не менять маршруты, иерархию, тексты, API и данные.
|
||||||
|
- Не трогать другую кодовую базу (уже на Mantine) и мобильную навигацию `AppShell`.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
### D1. Использовать `Mantine Breadcrumbs` с `component="nav"` и `aria-label`
|
||||||
|
|
||||||
|
Ручной `<nav>` заменяется на `<Breadcrumbs component="nav" aria-label={t("breadcrumbs.label")} data-depth={items.length} separator="→">`. `data-depth` и `aria-label` передаются в root через BoxProps/aria-атрибуты — сохраняет тесты navigation role, label и `data-depth`.
|
||||||
|
|
||||||
|
Rationale: `Breadcrumbs` — это и есть штатная замена собственной разметки сепаратора/навигации. Альтернатива (оставить кастом) отвергнута — цель change удалить дублирующий кастом.
|
||||||
|
|
||||||
|
### D2. Ссылки и текущая крошка рендерятся как children
|
||||||
|
|
||||||
|
Каждая крошка — элемент в children: предшествующие — `<Link to={...}>{label}</Link>` (получает Mantine `breadcrumb` class), текущая — `<span aria-current="page">{label}</span>`. Сепаратор «→» задаётся через `separator="→"` вместо ручных `breadcrumbs-separator-N` spans.
|
||||||
|
|
||||||
|
Rationale: сохраняет поведение «ссылки только предшествующие, текущая — текст с aria-current». Mantine автоматически ставит сепаратор между children.
|
||||||
|
|
||||||
|
### D3. Убрать теперь неиспользуемые кастомные CSS-классы, оставить только адаптивное правило
|
||||||
|
|
||||||
|
Удаляются `breadcrumbs`, `breadcrumbs-entry`, `breadcrumbs-separator-N`, `breadcrumbs-item-N`, а также селекторы и правило `data-depth="3"` из `src/app/styles.css`. Стиль оставшихся крошек даёт Mantine (accent-цвет ссылки можно сохранить через минимальную настройку `Breadcrumbs`/`.mantine`-класс или переиспользовать существующий `--ft-color-accent`).
|
||||||
|
|
||||||
|
Адаптивное скрытие промежуточных крошек на узком экране НЕ переносится на компонент: Mantine `Breadcrumbs` не умеет прятать по глубине. Сохраняется как кастомное CSS-правило по `data-depth` на root. Рассматривалась альтернатива — рендерить сокращённую цепочку на mobile через props — отвергнута как избыточная переделка поведения ради минимизации CSS.
|
||||||
|
|
||||||
|
## Delivery Map
|
||||||
|
|
||||||
|
| Area | Status | Architectural outcome or N/A reason |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| frontend | `affected` | Замена `AppBreadcrumbs.tsx` на `Mantine Breadcrumbs`, обновление `AppBreadcrumbs.test.tsx`, чистка кастомных `breadcrumbs-*` стилей в `src/app/styles.css`. |
|
||||||
|
| backend | `N/A` | Нет изменений backend API, данных или контрактов. |
|
||||||
|
| specs | `affected` | Change объявляет `skip_specs: true` — чистый рефакторинг, observable behavior и требования `app-breadcrumb-navigation` не меняются. |
|
||||||
|
| docs | `N/A` | Ни один as-built документ в `docs/` не описывает эти компоненты; правки не требуются. |
|
||||||
|
|
||||||
|
### Contract Gate Decision
|
||||||
|
|
||||||
|
| Gate | Status | Reason |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| runtime-openapi | `N/A` | API contract не меняется ни с одной стороны; рефакторинг UI-представления только во frontend. |
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- **Разметка сепаратора/классов меняется** — Mantine оборачивает children и рендерит сепаратор иначе. Митигируется тем, что тест проверяет поведение (roles, `aria-current`, `data-depth`, ссылки), а не внутренние классы.
|
||||||
|
- **Адаптивное скрытие остаётся кастомным CSS** — не «чистая» замена, но это осознанный trade-off: Mantine не предоставляет скрытия по глубине, а переписывание поведения на props добавило бы риск регрессий.
|
||||||
|
- **`aria-current` и navigation role** — должны быть явно построены, т.к. Mantine не добавляет их сам. Покрыто тестом.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
Один атомарный шаг во frontend: заменить реализацию `BreadcrumbTrail` на `Mantine Breadcrumbs` (с сохранением логики `AccountBreadcrumbs`/`AppBreadcrumbs` нетронутой), обновить тест, почистить кастомные `breadcrumbs-*` стили, прогнать `npm run verify`.
|
||||||
|
|
||||||
|
### Rollback Contract
|
||||||
|
|
||||||
|
`N/A` — безопасный обратимый рефакторинг UI-представления без auth/security, data migration, публичного контракта или труднообратимых решений. Откат — возврат прежней реализации `BreadcrumbTrail` и стилей.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
## Why
|
||||||
|
|
||||||
|
Почти весь UI frontend уже построен на `@mantine/core`. Два места по-прежнему используют собственную ручную реализацию: `AppBreadcrumbs` (собственный `BreadcrumbTrail` с ручными `<nav>`/`span`/сепаратором и классами `breadcrumbs-*` + CSS в `styles.css`) и мобильный `bottom-nav` в `AppShell` (собственный `<nav class="bottom-nav">` + CSS). Это технический долг: кастомный код и стили дублируют возможности поддерживаемых компонентов Mantine. Цель — заменить собственные реализации штатными компонентами Mantine там, где готовый аналог существует, без изменения пользовательского поведения.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
Заменить собственную UI-реализацию хлебных крошек (`AppBreadcrumbs`) на штатный компонент `Mantine Breadcrumbs`. Внешнее поведение не меняется: те же цепочки крошек на тех же защищённых маршрутах, ссылками остаются только предшествующие крошки, текущая страница — текст, сохраняется `aria-label`, адаптивное скрытие промежуточных крошек на узком экране и `data-depth`. Адаптивное скрытие по глубине остаётся на кастомном CSS, так как Mantine `Breadcrumbs` не прячет промежуточные крошки по глубине автоматически.
|
||||||
|
|
||||||
|
Scope утверждён Change Owner: заменяем только `AppBreadcrumbs`. Мобильная навигация `AppShell` (`bottom-nav`) выходит из scope и остаётся кастомной (wont-fix): готового штатного fixed bottom-nav в Mantine нет, миграция на мобильную навигацию Mantine AppShell не входит в этот change.
|
||||||
|
|
||||||
|
Non-goals:
|
||||||
|
- Не менять маршруты, иерархию крошек, тексты, поведение навигации и API.
|
||||||
|
- Не трогать остальной код, уже стоящий на Mantine.
|
||||||
|
- Не трогать мобильную навигацию `AppShell` (`bottom-nav`).
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
_Нет._
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
_Нет._ Заменяемая реализация не меняет внешнее (spec-level) поведение: требование `app-breadcrumb-navigation` уже описывает цепочки, ссылки, текущую крошку-текст и адаптивность. Чистый рефакторинг → `skip_specs: true`.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
Затрагивает только `frontend`: `app/ui/AppBreadcrumbs` (+ его тест) и кастомные стили `app/styles.css` для хлебных крошек. Backend API, данные, зависимости и мобильная навигация `AppShell` не изменяются. Пользовательское поведение идентично текущему.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
## 1. Миграция AppBreadcrumbs на Mantine Breadcrumbs
|
||||||
|
|
||||||
|
- [x] 1.1 [frontend] Заменить ручную разметку `BreadcrumbTrail` в `src/app/ui/AppBreadcrumbs.tsx` на `Mantine Breadcrumbs`: `<Breadcrumbs component="nav" aria-label={t("breadcrumbs.label")} data-depth={items.length} separator="→">`, предшествующие крошки — `<Link>` children, текущая — `<span aria-current="page">`; логика `AccountBreadcrumbs`/`AppBreadcrumbs` (маршруты и построение цепочек) остается без изменений
|
||||||
|
- **Covers:** `N/A` — замена внутренней реализации без изменения observable behavior спеку `app-breadcrumb-navigation` (skip_specs) и соответствует решениям D1–D2 Design.
|
||||||
|
- **Verification:** Прочитать `src/app/ui/AppBreadcrumbs.tsx`, убедиться, что используется `Mantine Breadcrumbs` с `component="nav"`, `aria-label`, `data-depth` и `separator="→"`, а цепочки/пропсы `AccountBreadcrumbs` не изменены.
|
||||||
|
|
||||||
|
- [x] 1.2 [frontend] Удалить кастомные классы хлебных крошек (`breadcrumbs`, `breadcrumbs-entry`, `breadcrumbs-separator-N`, `breadcrumbs-item-N`) и их CSS из `src/app/styles.css`, сохранив только адаптивное правило скрытия промежуточных крошек по `data-depth` на root и базовое оформление ссылок
|
||||||
|
- **Covers:** `N/A` — удаление дублирующего стиля по D3 Design; адаптивность по `data-depth` остаётся кастомной, т.к. Mantine Breadcrumbs не скрывает крошки по глубине.
|
||||||
|
- **Verification:** Прочитать `src/app/styles.css`, проверить отсутствие unused `breadcrumbs-*` селекторов и наличие медиа-правила `data-depth="3"`/эквивалента для узкого экрана.
|
||||||
|
|
||||||
|
- [x] 1.3 [frontend] Обновить `src/app/ui/AppBreadcrumbs.test.tsx` под новую DOM-разметку Mantine: сохранить проверки navigation role с `aria-label`, `aria-current="page"` у текущей крошки, `data-depth`, ссылок и fallback «Счет» без identifier
|
||||||
|
- **Covers:** `N/A` — тест проверяет неизменное поведение (roles, `aria-current`, `data-depth`, ссылки) и не создаёт новый Requirement; требует адаптации к структуре Mantine.
|
||||||
|
- **Verification:** Запустить таргетированно тест (например `npm run test:unit -- AppBreadcrumbs` или эквивалент из `package.json`), убедиться в зелёном проходе.
|
||||||
|
|
||||||
|
- [x] 1.4 [frontend] Выполнить полный local-verify-гейт frontend для подтверждения строгой корректности после рефакторинга
|
||||||
|
- **Covers:** `N/A` — canonical gate проверяет affected frontend delivery (D1–D3), а не вводит самостоятельное observable behavior.
|
||||||
|
- **Verification:** Выполнить `npm ci` (при необходимости) и `npm run verify` в `frontend` на Node 22, проверить отсутствие ошибок strict TypeScript, Biome, тестов/coverage, Knip, dependency boundaries и build.
|
||||||
|
|
||||||
|
## 2. Affected-area gates и итоговая проверка
|
||||||
|
|
||||||
|
- [x] 2.1 [specs] Выполнить structural validation change-артефактов canonical store
|
||||||
|
- **Covers:** `N/A` — задача проверяет specs-delivery уже покрытых артефактов (skip_specs) и не вводит новый Requirement.
|
||||||
|
- **Verification:** Запустить `openspec validate consume-mantine-components --type change --strict --store wealth-ledger-specs` и `git diff --check` в `specs`.
|
||||||
|
|
||||||
|
- [ ] 2.2 [workspace] Провести semantic verification реализации против Proposal, Design и Tasks и independent read-only review без Critical/Important findings
|
||||||
|
- **Covers:** `N/A` — terminal semantic verification и independent review проверяют change целиком и не реализуют отдельный Requirement.
|
||||||
|
- **Verification:** Сверить фактическое поведение крошек и чистку стилей с Proposal/Design/Tasks, подтвердить отсутствие blocking findings перед archive eligibility.
|
||||||
Reference in New Issue
Block a user