Files
MikrotikManager/docs/ux-ui-progress.md
T

514 lines
108 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UX/UI: журнал реализации
## Правила продолжения
- План: ReUI PRO, Frame, base-nova; KpiStatGrid, ServerTileRail и CodeExportSheet остаются источниками общих композиционных решений.
- Работа последовательно, без субагентов. После каждого завершённого этапа — локальный коммит на русском, без push.
- В начале этапа и после значимых проверок читать пятичасовой лимит. При остатке 25% и ниже не начинать следующий блок; закончить проверки и журнал.
- Этапы 2–4 выполнять в отдельной ветке codex/reui-v9-migration; не выпускать её до завершения миграции всех потребителей.
- Локально доступны моки. Проверки операций должны идти по обычному API-пути с подменой HTTP-ответов в тестах: встроенный mock-режим обходит запросы и не доказывает корректность live-ветки.
- Реальное применение RouterOS, сетевой опрос и экспорт сертификатов требуют отдельной приёмки в тестовом окружении. Не считать их проверенными по мокам.
- Перед продолжением читать git status и новые коммиты; сохранять изменения других задач.
## Состояние этапов
| Этап | Результат | Статус |
| --- | --- | --- |
| 0 | Исходная база, карта миграции, проверки и обзор экранов | Завершён 2026-09-13 |
| 1 | Достоверность сохранения, удаления, включения и опроса серверов | Завершён 2026-09-13 |
| 2 | Общая оболочка Data Grid/TanStack v9, Filters и эталон серверов | Завершён 2026-09-13, с перечисленными ограничениями приёмки |
| 3 | Основные таблицы и справочники | Завершён 2026-09-13, границы проверок ниже |
| 4 | Остальные таблицы, удаление старых API, общая регрессия | Завершён 2026-09-20; границы приёмки ниже |
| 5 | Состояния данных, свежесть, частичные сбои, гонки polling | Завершён 2026-09-20 для дашборда; границы проверок ниже |
| 6 | Навигация и дашборд | В работе: готовы переходы к серверам, проверкам и GRE; остаются WireGuard/VXLAN |
| 7 | Серверы и мониторинг: детали, формы, URL-контекст, права | Не начат |
| 8 | Трафик и статистика | Не начат |
| 9 | VPN, Firewall и Filters | Не начат |
| 10 | Маршрутизация, диагностика, карта и терминал | Не начат |
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Не начат |
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Не начат |
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат |
## Этап 0 — исходная база
Исходный HEAD: f8e743721007e809b8b148bb6ddcf9e6adb3f52d.
На старте уже изменены 25 файлов навыков ReUI, AGENTS.md и создан .codex/config.toml. Они относятся к предыдущей настройке и не входят в коммит этого этапа. .codegraph/daemon.pid — служебный файл, не коммитить.
Изменение: в lib/format-service-path-label.test.ts убрано расширение .ts из локального импорта. Это устраняет TS5097 без ослабления tsconfig и без изменения UI или backend.
### Проверки
| Проверка | Результат |
| --- | --- |
| tsc --noEmit --incremental false | Успешно после исправления импорта |
| tsx lib/format-service-path-label.test.ts | Успешно |
| tsx backend/src/services/ipsec-config.test.ts | Успешно |
| tsx backend/src/services/ros-file-contents.test.ts | Успешно |
| npm run build | Успешно: контракты, Next.js, TypeScript, 34 статические страницы |
| eslint . | Не проходит: 494 ошибки, 11015 предупреждений; включает артефакты .ci/docker |
| Тот же lint-отчёт без файлов .ci/docker | 81 ошибка, 134 предупреждения; это фильтрация отчёта, не отдельный запуск |
Lint hotspots: components/reui/sortable.tsx (8 ошибок), app/(main)/probes/page.tsx (8), components/reui/filters.tsx (6), app/(main)/network-map/page.tsx (5), app/(main)/firewall/page.tsx (4), components/reui/date-selector.tsx (3). Не исправлять их массово в этапе 1. Изоляцию generated-каталогов в ESLint вынести в отдельный небольшой блок перед миграцией; правила проверок не отключать.
В песочнице сборка не могла записать существующие dist-файлы (EPERM), а tsx падал в uv_os_get_passwd. Повторные запуски вне песочницы прошли; это ограничения окружения, не ошибки приложения.
### Исходные экраны
Просмотрены /servers и /dashboard через локальную production-сборку на 127.0.0.1:3000, без backend, в mock-режиме. Скриншоты показаны в истории задачи; отдельные PNG в репозиторий не добавлены.
- Серверы: 9 записей, 7 онлайн; Frame/KPI, вкладки типов, фильтры и поиск работают как исходная композиция. На viewport 1280×720 KPI занимают две строки; часть правых колонок таблицы за пределами видимой области. Проверку прокрутки и мобильной компоновки выполнить на этапе 7.
- Дашборд: KPI с подписями «Флот», «Оверлей», «BGP Rx», «Здоровье»; ниже крупные быстрые переходы, затем график задержки и события. Очередь проблем не доминирует на первом экране — целевая перестройка этапа 6.
- Это исходный визуальный обзор, не сквозная проверка операций. Проверки консоли, клавиатуры, других размеров и тем остаются в профильных этапах.
### Сохраняемые исправления IPsec
Последние коммиты исправляют общий IKEv2-peer с отдельными сертификатами клиентов, декодирование .p12 из RouterOS /file/get, отсутствие неподдерживаемых proposal-ключей в .sswan и горизонтальное переполнение шторки сертификата. Эти сценарии включить в регрессию этапов 4, 9, 11А. Ограничение passphrase брать из @mmapp/contracts (IPSEC_MIN_PASSPHRASE), не дублировать.
Тест ipsec-config проходит отдельно, но отсутствует в общей npm test-цепочке backend; подключить его при следующей правке тестовой инфраструктуры.
## Задание этапа 1 (выполнено)
Начать с app/(main)/servers/page.tsx, components/data-grids/servers-data-grid.tsx, shared/api/servers.ts и моделей серверов. Не начинать миграцию ReUI в этом этапе.
1. Сохранение: блокировать повторную отправку; закрывать форму и менять список только после успеха API. На 400/403/500/сетевой ошибке сохранять значения и показывать сообщение рядом с формой.
2. Редактирование: загрузить полную модель до разрешения сохранения; защитить от ответа другого сервера и закрытой формы. Не выводить enabled из online/offline. Изменение подключения сбрасывает прежний результат проверки.
3. Удаление: подтверждение конкретного объекта; не удалять строку при ошибке API; блокировать повтор.
4. Включение: обновлять enabled через API, фактическую доступность не подменять локальным переключением.
5. Опрос: результат по устройству; массово не более четырёх запросов одновременно, повтор только неуспешных. Ошибку перечитывания списка отличать от результата самого опроса.
6. Добавить проверки по HTTP-пути frontend с контролируемыми ответами, а не только встроенный mock. Успех, 403, 500, таймаут, двойное нажатие и смена объекта во время запроса обязательны.
7. Типизация, lint изменённых файлов, профильные тесты и браузерная проверка; обновить журнал и сделать отдельный локальный коммит без push.
Остановка после этапа 0: на последней проверке осталось 15% пятичасовой квоты (было 35% на старте). Этап 1 не начат по согласованному правилу резерва. При продолжении сначала проверить свежие лимиты.
## Этап 1 — достоверность операций
Исходный HEAD: 88326d9. Изменения предыдущей настройки ReUI и служебные файлы не входят в коммит этапа.
- В `app/(main)/servers/page.tsx` сохранение закрывает форму только после подтверждения API. Ошибки остаются рядом с формой, повторные отправки блокируются синхронным замком. Пока загружаются детали, сохранение недоступно; ошибку загрузки можно повторить. Поздний ответ другого объекта игнорируется.
- Удаление требует подтверждения имени и адреса, при ошибке строка и диалог остаются. Включение изменяет `enabled` через API и не подменяет измеренный online/offline. Изменение параметров подключения сбрасывает прежний результат проверки.
- `lib/server-operations.ts` содержит замок операций, понятные сообщения ошибок и очередь опроса максимум по четыре запроса. Результат показан по каждому серверу, повторяются только неудачные запросы. Ошибка последующего обновления списка отделена от результата опроса, прежние строки сохраняются.
- В `shared/api/servers.ts` ожидание ограничено 30 секундами, для опроса — 60. Таймаут явно означает неизвестный результат: клиентское прерывание не отменяет действие на backend. Контракты и маршруты API не изменены.
- В `components/data-grids/servers-data-grid.tsx` действия блокируются на время операции; выключенный сервер помечается отдельно от доступности.
- Добавлены Playwright, `playwright.config.ts`, `tests/e2e/servers.spec.ts` и команда `npm run test:servers`. Используется установленный Microsoft Edge, свободный порт 3100 и отдельный Next dev процесс. Запросы API и health перехвачены тестами; встроенный mock-режим не используется. Отчёты исключены из Git.
### Проверки этапа 1
| Проверка | Результат |
| --- | --- |
| `npm run test:servers` | 14 браузерных тестов прошли: создание, сохранение 400/403/500 и повтор, двойное нажатие, удаление с повтором, enabled, загрузка деталей и смена объекта, массовый опрос и повтор, таймаут, отдельная ошибка обновления списка, запрет включения, сброс проверки подключения |
| TypeScript | Проверка без emit прошла; production build также проверяет типы |
| `npm run build` | Успешно: contracts, Next.js, TypeScript, 34 статические страницы |
| ESLint изменённых TS/TSX-файлов | 0 ошибок, 1 известное предупреждение React Compiler о TanStack v8 `useReactTable` |
| ReUI `get_audit_checklist` | Получен; сохранены установленные компоненты, Frame, семантические токены и типизированный API. Требование TanStack v9 остаётся этапам 2–4; полная доступность и адаптивность — профильным этапам и приёмке |
Моки достаточны для этих клиентских сценариев. Реальный backend, права реальных пользователей и применение RouterOS не проверены. Общие исходные ошибки lint перечислены в этапе 0; они не устранены этим этапом. Полную регрессию повторять после миграции общей основы.
### Следующий шаг — этап 2
В новом окне проверить лимиты, git status и последние коммиты. Создать ветку `codex/reui-v9-migration`, перепроверить актуальный совместимый API ReUI Data Grid/TanStack и Filters. Мигрировать общую оболочку и одну таблицу серверов, закрепить зависимости и описать образец переноса. Сохранить проверки операций из этапа 1. Ветку не выпускать до завершения этапа 4. После этапа — отдельный локальный коммит без push.
## Этап 2 — промежуточное сохранение 2026-09-13
Ветка `codex/reui-v9-migration`, исходный HEAD `c30f817`. Этап **не завершён**. На старте оставалось 51% пятичасового лимита, после основного блока — 24%; новые блоки остановлены по правилу резерва.
### Выполнено
- Проверены MCP API и dry-run официального реестра base-nova; установлен Data Grid, новый Filters с Cascader и учебный `components/examples/c-data-grid-1.tsx`. Пример не подключён к маршрутам приложения. 13 общих примитивов при установке не перезаписывались; итоговых изменений глобальной темы нет.
- Закреплён `@tanstack/react-table` 9.2.4. Для поэтапной совместимости старые таблицы используют alias `@tanstack/react-table-v8` (8.21.3), `components/reui/legacy-data-grid/` и `shared/legacy-data-grid-*`. Это временная изоляция, не эмуляция API v8 поверх v9. Все старые импорты изменены механически; семантика старых экранов сохраняется. Удалить alias и legacy-файлы после последнего потребителя в этапе 4.
- Канонические `shared/data-grid-shell.tsx`, `data-grid-sort-header.tsx`, `data-grid-layout.ts` типизированы через `DataGridFeatures`. `servers-data-grid.tsx` использует `ColumnDef<DataGridFeatures, Server>` и `useTable({ features: dataGridFeatures, ... })`. Отдельные `get*RowModel` удалены. `manualPagination: true` сохраняет прежний полный список: bundle включает пагинацию по умолчанию, а экран серверов её пока не показывает. Стабильный `getRowId` и раскрытие сохранены.
- Новый Filters установлен в `components/reui/filters/`; старый `components/reui/filters.tsx` и его шесть потребителей пока работают. При переносе импортировать новый API явно из `@/components/reui/filters/filters`: одноимённый старый файл имеет приоритет над каталогом. Сохранить AND-семантику старых условий; не применять `flattenFilterConditions` как вычислитель вложенных OR-групп.
### Проверки и остаток
| Проверка | Результат |
| --- | --- |
| `tsc --noEmit --incremental false` | Прошла для всего проекта |
| 14 браузерных тестов операций | Прошли после миграции |
| Дополнительный тест v9 | Прошёл: 12 строк без потерь, сортировка в обе стороны, раскрытие и закрытие деталей |
| Lint адаптированной таблицы, общей оболочки и тестов | Прошёл без замечаний |
| `npm run build` | Прошла: contracts, типы и 34 статические страницы |
| Lint новых официальных Data Grid / Filters / Cascader | 23 ошибки и 27 предупреждений; [точный список](reui-migration-lint.md). Это незакрытая часть этапа, правила не отключались |
Следующий запуск: **продолжить этап 2**, не переходить к этапу 3. Проверить лимит и HEAD; разобрать замечания установленного реестра, подготовить и проверить переход старых Filters в FilterQuery на эталоне серверов, выполнить визуальную и клавиатурную проверку эталона и итоговый ReUI audit. Затем завершить этап отдельным локальным коммитом. Текущее сохранение — промежуточный коммит, без push; ветку не выпускать.
Референсы: [пример пагинации](https://reui.io/preview/base/components/c-data-grid-1?ref=mcp), [PRO-таблица](https://reui.io/preview/base/data-grid-filtering-2?ref=mcp), [Data Grid API](https://reui.io/docs/components/base/data-grid?ref=mcp), [Filters API](https://reui.io/docs/components/base/filters?ref=mcp).
```mermaid
flowchart LR
A[Серверы: реальные API] --> B[DataGridShell]
B --> C[ReUI Data Grid + TanStack v9]
D[Остальные таблицы] --> E[Временная оболочка legacy]
E --> F[TanStack v8 alias]
G[Старые Filters] -. этапы 2–4 .-> H[FilterQuery: AND / OR]
```
### Малый блок после промежуточного сохранения
По отдельному поручению пользователя выполнить работу в пределах 10% квоты; на старте фактически оставалось 9%. В `data-grid.tsx` сериализация `tableLayout` и `tableClassNames` вынесена в именованные ключи зависимостей useMemo. Сравнение по содержимому сохранено, два замечания React Compiler устранены, правила не отключались.
Проверки: TypeScript всего проекта прошёл; браузерный тест v9 (12 строк, сортировка, раскрытие) прошёл. Повторный lint группы: **21 ошибка, 27 предупреждений**, актуальный компактный отчёт — `docs/reui-migration-lint.md`. Production build не повторялся после этой локальной правки; предыдущая сборка этапа прошла. Этап 2 остаётся в работе; следующий объём прежний.
## Этап 2 — завершение
Продолжение от `f9a4a07` в `codex/reui-v9-migration`. Записи о промежуточных остановках выше — история; текущий следующий шаг — **этап 3**.
### Результат
- Серверы используют ReUI Data Grid/TanStack 9.2.4 и новый `FilterQuery`; таблица сохраняет все строки, сортировку, детали и подтверждённые операции этапа 1. Для раскрытия есть отдельная кнопка с `aria-expanded`, работающая с клавиатуры. При пустом результате фильтрации показывается «Нет совпадений», а не предложение добавить первый сервер.
- `ServerFilters` повторяет контракт официального `c-filters-1`. Поля `country/status/type` имеют `type: select`, потому что данные скалярные. Варианты множественного выбора задаются операторами. Добавлены русские подписи Filters, вычислитель дерева `applyFilterQuery` и временный `migrateFilterQuery` для AND-массива старых условий. Неизвестное условие не расширяет выборку, незавершённые условия не превращают OR в истинное выражение.
- `DataPageToolbar.filterControl` позволяет переносить потребителей по одному; старые props сохранены только на период этапов 3–4. Новые импорты Filters обязательно указывают `/filters/filters`, пока существует старый одноимённый файл.
- Выполнены локальные исправления совместимости исходников ReUI с React Compiler. Контекст Data Grid передаёт актуальный снимок данных; императивный координатор ширины получает только закоммиченные React значения. Кэш подписей Cascader обновляется без мутации ref во время рендера; измерения запускаются через ResizeObserver/кадр браузера, отложенная навигация отменяется при смене контекста. Draft и автооткрытие Filters синхронизируются до рендера дочерних редакторов. Убраны неиспользуемые импорты и лишние вычисления. Правила lint не отключались, публичные API и композиция сохранены. При будущем обновлении реестра эти исправления сравнить с upstream, не перезаписывать автоматически.
### Приёмка
| Проверка | Результат |
| --- | --- |
| `npm run test:servers` | 17 браузерных тестов прошли, включая операции, сортировку 12 строк, фильтрацию AND, изменение/сброс, Escape и клавиатурное раскрытие |
| `npx tsx --test lib/data-filters/apply-filter-query.test.ts` | 6 тестов прошли: AND/OR, отрицание, черновики, пустые/неизвестные условия, эквивалентность поддерживаемых старых операторов |
| TypeScript и `npm run build` | Успешно, 34 статические страницы |
| Lint изменённых участков и новой группы ReUI | 0 ошибок, 1 предупреждение `react-hooks/incompatible-library` для виртуализатора. React Compiler пропускает его оптимизацию; [отчёт](reui-migration-lint.md) |
| Визуальная проверка | Просмотрены экраны 1440×1000 и 390×844. Page overflow отсутствует; широкая таблица сохраняет горизонтальный скролл внутри своего контейнера |
| ReUI audit | Checklist получен; проверены композиция Frame/base-nova, API, типизированные HTTP-данные, подписи, состояния и фокус эталона. Правки internals ограничены описанной совместимостью, без смены дизайна |
Границы приёмки: моки HTTP не подтверждают реальные права или применение RouterOS. Общая мобильная шапка обрезает часть действий, KPI занимают много высоты — исходные задачи этапов 6–7. Некоторые служебные объявления вложенного Cascader ещё английские — завершить локализацию на этапе 7. Все режимы DnD/виртуализации/автоподбора ширины не были проверены браузером на этом этапе; их проверять при переносе использующих их экранов. Изменённый способ публикации контекста Data Grid требует профилирования больших таблиц на этапе 12. Это не полная приёмка доступности/производительности всего приложения.
### Образец следующего переноса
1. В выбранном доменном grid заменить `react-table-v8` на `react-table`, legacy-оболочку на `DataGridShell` и заголовки на новый `DataGridSortHeader`.
2. Тип колонок — `ColumnDef<DataGridFeatures, T>`, таблицы — `Table<DataGridFeatures, T>`. Использовать `useTable({ features: dataGridFeatures, ... })`; удалить старые `get*RowModel`. Сверить переименованные sorting/pinning/resize props с установленным API. Сохранить стабильный `getRowId`.
3. Явно определить пагинацию: для полного списка без панели — `manualPagination: true`; для серверной — передавать реальный `rowCount`; для клиентской — подключить панель и контролируемую страницу.
4. Для Filters перенести поля `key → id`, выбрать тип по данным, сохранить AND через `migrateFilterQuery`. Вычислитель сейчас поддерживает только операторы скалярных фильтров серверов и прежние текстовые contains; перед переносом дат, чисел или массивов расширить вычисление и тесты. Нельзя просто сплющить OR-дерево.
5. Проверить фильтрацию, сортировку, сброс/страницу, раскрытие и действия. Старую основу удалять только после последнего потребителя в этапе 4.
Следующий шаг: проверить квоту и состояние файлов; выполнить только этап 3 (обзор, мониторинг, трафик, статистика и справочники) в этой ветке, перечислить оставшихся legacy-потребителей и сделать локальный коммит без push. Перед миграцией виртуализированных таблиц добавить профильные проверки указанного выше риска контекста. Этапы 2–4 не выпускать до общей регрессии этапа 4.
## Этап 3 — основные таблицы и справочники
Перенесены десять прямых потребителей: `asns-data-grid`, `domains-data-grid`, `ip-ranges-data-grid`, `communities-data-grid`, `compact-data-grid`, `uptime-resources-data-grid`, `uptime-speed-history-data-grid`, `traffic-flows-data-grid`, `traffic/flow-analytics-panel`, `traffic/flow-traffic-map`. Через CompactDataGrid новую основу получили таблицы дашборда, статистики и снимков состояния. Серверы уже перенесены на этапе 2.
Используются `useTable`, `dataGridFeatures` и типы v9. Сохранены доменные props, стабильные идентификаторы, раскрытие и обработчики действий. Для списков без панели явно отключена клиентская пагинация; для каталогов EvoBGP она сохранена. Таблицы потоков сохраняют исходный порядок данных. Состояние выбора Communities приведено к типу v9. Общая оболочка получила русские служебные подписи через документированный `i18n`, без изменения установленных примитивов ReUI.
| Референс | Схема сохранённого сценария |
| --- | --- |
| [Data Grid Filtering 2](https://reui.io/preview/base/data-grid-filtering-2?ref=mcp) | Поиск → страница таблицы → сортировка / выбор строки → детали |
| [API Data Grid](https://reui.io/docs/components/base/data-grid?ref=mcp) | Типизированные данные → useTable v9 → DataGridShell → таблица и опциональная пагинация |
Проверки: TypeScript и целевой ESLint — без ошибок и предупреждений. 17 браузерных тестов серверов прошли. Добавлены 6 сценариев `tests/e2e/migrated-tables.spec.ts`: пагинация и поиск с последней страницы в трёх справочниках, числовая сортировка через границу страницы, выбор одной из 12 Communities, все 12 строк статистики и переход в URL-срез. Первый старт Next.js превысил 120 секунд; повторный старт успешен. Два теста сначала упали из-за неточных селекторов, исправлены и повторно прошли. Все запросы перехватывались HTTP-моками.
ReUI audit checklist получен. Сохранены Frame/base-nova, композиция и типизированные источники; новые props сверены с установленными файлами. Просмотрен экран Communities: таблица и выбранная строка отображаются, но правая панель деталей обрезается при ширине 1440 px. Исправить размещение таблицы/деталей при профильной переработке экранов; общую доступность и адаптивность не считать принятой. Существующие неработающие действия справочников также не входят в приёмку миграции. Браузерные проверки всех комбинаций мониторинга, traffic-map и snapshot-деталей остаются для общей регрессии этапа 4 и профильных этапов 7–10. Виртуализатор эти десять компонентов не используют; нагрузочный профиль общей основы остаётся этапу 12.
Production build прошёл: `npm run build`, 34 статические страницы. Последняя проверка квоты перед завершением блока: использовано 37% пятичасового окна и 50% недельного; это общий расход аккаунта, не оценка стоимости этапа.
### Остаток для этапа 4
- Доменные legacy-таблицы: backups, bgp-sessions, certificates, data-collection-scheduler, filters, firewall-rules, firewall-scenario-rules, gre-pools, gre-tunnels, ospf-bfd, ospf-neighbors, ospf-routes, probes-schedule, probes-speed-probes, recursive-routes, route-optimizer-comm-recs, route-optimizer-full-routes, route-optimizer-ospf-preview, route-optimizer-wan-matrix, subusers, users, vxlan, wireguard, wireguard-peers, ipsec-users-grid.
- Общая основа: `components/data-table.tsx`, `components/data-grids/shared/legacy-*`, `components/reui/legacy-data-grid/*`; ссылки на legacy layout в DataPageCard и DataPageToolbar. Проверить отсутствие потребителей перед удалением.
- Старые Filters: статистика, BGP, backups, backups-history и fallback DataPageToolbar; старые определения полей/вычислитель. Статистика передаёт в API только равенство скалярных срезов: при переносе нельзя принимать OR/отрицание и молча превращать их в равенство. Сохранять незавершённый черновик отдельно от URL.
Следующий шаг: проверить квоту и git status; начать этап 4 в `codex/reui-v9-migration`, переносить оставшихся потребителей ограниченными группами, затем удалить v8/старые Filters и выполнить общую регрессию. Ветку до этой проверки не выпускать. Коммит этапа 3 — только локальный, без push; настройки skills/AGENTS/.codex других работ не включать.
## Этап 4 — блок 1: BGP и OSPF
Перенесены `bgp-sessions-data-grid`, `ospf-neighbors-data-grid`, `ospf-routes-data-grid`, `ospf-bfd-data-grid`. Использована установленная основа ReUI v9 (`useTable`, `dataGridFeatures`, современная оболочка и заголовки); `manualPagination: true` сохраняет полный список. Стабильные row ID, сортировка, раскрытие BGP и управляемый выбор OSPF сохранены. Убрано неиспользуемое извлечение `onHighlight` из props, сам внешний контракт не менялся.
Проверки: `npx tsc --noEmit --incremental false` и целевой ESLint успешны. Три сценария `tests/e2e/routing-tables.spec.ts` подтвердили 12 строк во всех четырёх таблицах, числовую сортировку, выбор OSPF-соседа, раскрытие/сворачивание BGP после сортировки и пустые ответы API. Один тест сначала ожидал сокращённый текст пустого BFD; после уточнения селектора прошёл. Это HTTP-моки, не подтверждение реальной работы сетевых протоколов. Общая основа не менялась, повторная полная сборка/регрессия отложены до завершения этапа 4.
ReUI checklist получен: сохранены композиция, props установленного API, источники данных и оформление. Полная клавиатурная доступность действий строк, мобильная компоновка и сценарии графа остаются профильному этапу 10 и итоговой приёмке; текущие тесты их не подтверждают.
Квота: на старте использовано 49%, после блока 58% пятичасового окна (общий расход аккаунта). Этап 4 не завершён. BGP и три OSPF-таблицы исключить из списка оставшихся выше; остальные legacy-таблицы и все перечисленные старые Filters ещё остаются.
Точный следующий шаг: проверить git status и квоту, перенести VPN-группу (`wireguard-data-grid`, `wireguard-peers-grid`, `ipsec-users-grid`, `gre-pools-data-grid`, `gre-tunnels-data-grid`, `vxlan-data-grid`) с профильными проверками. Затем перейти к оставшимся таблицам управления/диагностики и миграции Filters. Старые зависимости удалять только после последнего потребителя. Этот блок сохранить отдельным локальным коммитом без push; ветку не выпускать.
## Этап 4 — блок 2: WireGuard
Перенесены `wireguard-data-grid` и `wireguard-peers-grid` на установленную основу ReUI v9. Сохранены внешние props, идентификаторы, обработчики операций и раскрытие интерфейса. Явное `manualPagination: true` сохраняет все строки без скрытого ограничения в десять записей. Общие примитивы и сетевые операции не менялись.
Проверки: TypeScript (`--incremental false`) и целевой ESLint прошли без замечаний. Два браузерных сценария `tests/e2e/wireguard-tables.spec.ts` прошли: 12 интерфейсов, сортировка по порту, раскрытие/сворачивание пиров выбранного интерфейса, 12 строк отдельной вкладки пиров, сортировка и пустые состояния обеих вкладок. Фикстура проверяется схемой `wgListResponseSchema`, ответы подменяются на HTTP-уровне. Реальные операции RouterOS и права этими тестами не проверены; их приёмка остаётся этапу 9. Применён уже полученный ReUI checklist: сохранены композиция, публичный API и оформление; полная доступность и адаптивность ещё не приняты.
Квота на старте: использовано 67% пятичасового окна; после браузерных проверок — 74% (остаток 26%). Новый блок не начинать на границе резерва. Полная сборка и общая регрессия остаются завершению этапа 4, общая основа в этом блоке не менялась.
Следующий шаг после проверки квоты: перенести оставшиеся четыре VPN-таблицы `ipsec-users-grid`, `gre-pools-data-grid`, `gre-tunnels-data-grid`, `vxlan-data-grid`. WireGuard, BGP и три OSPF-таблицы уже исключены из остатка. Остальные таблицы управления/диагностики и старые Filters ещё не перенесены; этап 4 не завершён, ветку не выпускать. Блок WireGuard сохранить локальным коммитом без push.
## Этап 4 — блок 3: VXLAN
По явному поручению пользователя продолжить ниже резервного порога выполнен один небольшой блок: `vxlan-data-grid` перенесён на установленную ReUI v9-оболочку. Использованы `useTable`, `dataGridFeatures`, типы v9 и `manualPagination: true`; сохранены идентификаторы, сортировка и обработчик экспорта. Общая основа не менялась.
TypeScript (`--incremental false`), целевой ESLint и браузерный сценарий `tests/e2e/vxlan-table.spec.ts` прошли. Проверены 12 строк, числовая сортировка VNI, открытие экспорта выбранного туннеля после сортировки, поиск, пустой результат и сброс. Использованы типизированные HTTP-моки; скачивание файла, применение RouterOS и существующие пункты редактирования/включения/удаления этим тестом не проверены. По ранее полученному ReUI checklist сохранены композиция, публичный API и оформление; профильная доступность и адаптивность остаются этапу 9. Полная регрессия остаётся завершению этапа 4.
После проверок осталось 8% пятичасового лимита (общая квота аккаунта). Блок сохранить локальным коммитом без push. Следующий шаг после сброса: `ipsec-users-grid`, `gre-pools-data-grid`, `gre-tunnels-data-grid`; затем оставшиеся таблицы управления/диагностики и старые Filters. VXLAN исключить из предыдущего списка остатка. Этап 4 не завершён, ветку не выпускать.
## Этап 4 — блок 4: IPsec и GRE (2026-09-14)
Перенесены `ipsec-users-grid`, `gre-tunnels-data-grid`, `gre-pools-data-grid`: `useTable`, `dataGridFeatures`, типы и общая оболочка ReUI v9. Явное `manualPagination: true` сохраняет полный список. Сохранены существующие идентификаторы и обработчики; ID GRE по-прежнему включает индекс исходных данных, его стабильность при переупорядочивании ответа API не улучшалась в этом блоке. Установленные примитивы и сетевые правила не менялись.
Проверки: TypeScript (`--incremental false`), целевой ESLint и три браузерных сценария `tests/e2e/ipsec-gre-tables.spec.ts` прошли. Проверены 12 клиентов IPsec, сортировка и подтверждение удаления выбранного клиента с отменой; 12 GRE-туннелей и производных пулов, числовая сортировка MTU, поиск, сортировка пулов и пустые ответы API. Селектор заголовка «Клиент» уточнён после совпадения с кнопкой панели. IPsec-фикстура валидируется контрактной схемой; GRE типизирован через доменную модель. Используются HTTP-моки: применение RouterOS, удаление клиента и экспорт сертификата этим прогоном не проверены.
ReUI checklist получен; сохранены публичный API, композиция и оформление. Полная доступность, адаптивность, работа операций и существующие декоративные действия GRE-пулов остаются профильному этапу 9 и итоговой приёмке. Общая основа не менялась; полная сборка и регрессия остаются завершению этапа 4.
Квота: в начале использовано 1%, после проверок 18% пятичасового окна (общий расход аккаунта). VPN-группа миграции завершена, этап 4 в целом ещё открыт. Следующий конкретный блок — `users-data-grid`, `subusers-data-grid`, `certificates-data-grid`, `backups-data-grid`; затем оставшиеся таблицы диагностики/управления и Filters. Только после последнего потребителя удалить legacy-основу и v8 и выполнить общую регрессию. Блок IPsec/GRE сохранить локальным коммитом без push; ветку до завершения этапа 4 не выпускать.
## Этап 4 — блок 5: таблицы администрирования (2026-09-14)
Перенесены `users-data-grid`, `subusers-data-grid`, `certificates-data-grid`, `backups-data-grid` на установленный API ReUI v9 с явным `manualPagination: true`. Сохранены идентификаторы, обработчики и раскрытие. `SubusersDataGrid` сейчас не имеет потребителей в app/components; для него выполнена статическая проверка, браузерная приёмка не заявляется.
Браузерная проверка выявила сортировку размера бэкапа по форматированной строке. В `Backup` добавлено необязательное `sizeBytes`; страница сохраняет число байтов из API, таблица сортирует по нему. Отображение размера не менялось. При отсутствии исходного числа запись считается неизвестной и располагается в конце (`sortUndefined: "last"`), строка не превращается в выдуманное число. Старые демонстрационные объекты без sizeBytes не дают числовой сортировки; текущая страница загружает бэкапы через API.
Три сценария `tests/e2e/admin-tables.spec.ts` прошли: 12 пользователей и сертификатов с сортировкой/раскрытием, SAN выбранного сертификата, 12 бэкапов с числовой сортировкой и правильным файлом в подтверждении удаления с отменой. Исправлены неполные HTTP-фикстуры настроек сертификатов, вызывавшие предупреждения controlled/uncontrolled. Реальные права, удаление, восстановление и операции сертификатов этим прогоном не проверены. Общая сборка/регрессия остаётся завершению этапа 4.
Применён полученный ReUI checklist: сохранены композиция и установленный API; полная доступность и адаптивность остаются профильной приёмке. Квота: использовано 26% на старте, 50% после браузерных проверок (общий расход аккаунта). Блок сохранить локальным коммитом без push.
Следующий блок: `filters-data-grid`, `firewall-rules-data-grid`, `firewall-scenario-rules-data-grid`. Затем `data-collection-scheduler-data-grid`, `probes-schedule-data-grid`, `probes-speed-probes-data-grid`, `recursive-routes-data-grid` и четыре `route-optimizer-*-data-grid`. Также остаются общая legacy-основа, `components/data-table.tsx`, старые Filters и зависимость v8. Этап 4 не завершён, ветку не выпускать.
Статические проверки блока 5: TypeScript прошёл; lint четырёх таблиц, фикстур и доменной модели — без замечаний. Расширенный lint `app/(main)/backups/page.tsx` обнаружил исходную ошибку `react-hooks/set-state-in-effect` на `void loadLive()` в эффекте (подтверждено в HEAD до правок). Эта ошибка не исправлялась в миграции; устранить при работе над состояниями данных на этапе 5. Не считать lint всей страницы успешным.
## Этап 4 — блок 6: таблица сетевых Filters (2026-09-14)
`filters-data-grid` перенесён на установленную ReUI v9-основу с полным списком (`manualPagination: true`). Сохранены props, ID, обработчики и действующее отключение сортировки в live-режиме. Кнопки поднятия/опускания правила получили доступные подписи с community. Это миграция таблицы сетевых правил; старый компонент построения Filters/FilterQuery этим блоком не затронут.
TypeScript и целевой ESLint прошли. Браузерный сценарий `tests/e2e/network-filters-table.spec.ts` подтвердил 12 строк, исходный порядок и отсутствие кнопки сортировки в live-режиме, блокировку перемещения за границы, поиск/пустой результат/сброс. Первый вариант теста ошибочно ожидал доступную сортировку; ожидание исправлено под существующий контракт страницы. HTTP-моки не подтверждают перестановку и применение правил к RouterOS. Применён ранее полученный ReUI checklist; профильная приёмка операций и доступности остаётся этапу 9.
Квота: на старте использовано 64%, после статических проверок — 78% (остаток 22%). Новые блоки не начинать в резерве. Firewall не переносился: `firewall-rules-data-grid` использует DnD и требует чтения нового API и профильного браузерного теста перетаскивания с проверкой порядка. Следующий блок после проверки квоты — обе Firewall-таблицы; далее восемь таблиц диагностики/сборщика, общие legacy-потребители, старые Filters и удаление v8. Этап 4 остаётся открытым, ветку не выпускать. Сохранить локальный коммит без push.
## Этап 4 — блок 7: Firewall и DnD
Перенесены `firewall-rules-data-grid` и `firewall-scenario-rules-data-grid`. Общая основа — установленный ReUI v9; полный список сохранён через `manualPagination: true`. В режиме DnD применён `manualSorting: reorderable`, в локальном сценарии — `manualSorting: true`. Сохранены row ID, обработчики, формат запросов и правила допустимости перемещения. Props `DataGridTableDndRows` сверены с установленным исходником: `dataIds`, `handleDragEnd`. Примитивы ReUI не менялись. Действия сценария получили подписи поднятия/опускания/удаления и видимость при фокусе.
Пять браузерных сценариев `tests/e2e/firewall-tables.spec.ts` прошли: клавиатурное и мышиное перемещение среди 12 правил, правильные rosId/destinationRosId в API, откат при ошибке 500, Escape без запроса, 12 правил локального сценария с перестановкой и удалением выбранной строки. Начальные падения объяснялись преждевременным завершением переноса в тесте; добавлено ожидание фактической смены цели DnD через live-region. Тесты используют HTTP-моки, не подтверждают применение RouterOS. Перенос между серверами/таблицами, touch, сортируемый режим общего списка и поведение на больших наборах остаются профильной и общей регрессии.
TypeScript и целевой ESLint прошли. ReUI checklist получен: сохранены композиция и публичный API; проверены мышь/клавиатура и отмена переноса. Служебные объявления DnD пока английские — локализовать при профильной доступности, не считать её полностью принятой. Остальные ограничения предыдущих блоков сохраняются.
Квота: использовано 0% на старте обновлённого окна, 34% после профильных тестов (общая квота аккаунта). Следующий блок — `data-collection-scheduler-data-grid`, `probes-schedule-data-grid`, `probes-speed-probes-data-grid`; затем `recursive-routes-data-grid` и четыре `route-optimizer-*-data-grid`. После них остаются общая legacy-основа/`data-table`, старые Filters и удаление v8 с полной регрессией. Этап 4 ещё не завершён; локальный коммит без push, ветку не выпускать.
Production build блока 7: `npm run build` прошёл, 34 статические страницы. Это проверка сборки, не замена общей регрессии этапа 4.
## Этап 4 — блок 8: сборщик и расписания проб (2026-09-14)
Перенесены `data-collection-scheduler-data-grid`, `probes-schedule-data-grid`, `probes-speed-probes-data-grid` на установленный ReUI Data Grid/TanStack v9: `useTable`, `dataGridFeatures`, общая современная оболочка и заголовки. Сохранены идентификаторы строк, обработчики и порядок задач сборщика; автоматическая пагинация отключена. Кнопка запуска задачи получила доступное имя с названием задачи.
Проверки: TypeScript без incremental и ESLint изменённых файлов; три сценария Playwright — 12 speed-probes и сортировка источника, все 11 задач сборщика и POST выбранного `alert_engine/run-now`, сортировка и удаление выбранного правила демонстрационного расписания. Финальный браузерный прогон: 3 passed. Исправлены неполные фоновые HTTP-фикстуры EvoBGP, GeoIP и Traffic Flow; повторный прогон без предупреждений controlled/uncontrolled. Live-ветки проверены с подменой HTTP, обычное расписание — отдельно во встроенном mock-режиме. Реальное исполнение задач и RouterOS не проверялись.
Первичная проверка TypeScript до браузерного запуска прошла. Финальная повторная проверка после Playwright заблокирована двумя TS2304 в сгенерированном `.next/dev/types/validator.ts` (строки 379 и 388: отсутствует `RouteHandlerConfig`). `next typegen` успешно обновил типы, но ошибку dev-кэша не устранил. Исходники и конфигурация TypeScript ради обхода ошибки не менялись. При продолжении проверить dev-кэш и повторить tsc; финальный TypeScript не считать пройденным. ESLint изменённых файлов прошёл.
Применены правила установленного ReUI и ранее полученный checklist: публичный API и существующая композиция сохранены, полная визуальная/адаптивная приёмка остаётся последующим этапам. Общая основа не менялась; полная регрессия — после завершения миграции.
Квота: использовано 46% на старте, 81% перед финальными проверками. Остаток ниже резерва 25%, новые блоки не начинать. Следующий шаг — `recursive-routes-data-grid` и четыре `route-optimizer-*-data-grid`; далее `data-table`, общие legacy-потребители, старые Filters и удаление v8 с общей регрессией. Этап 4 остаётся открытым; сохранить блок локальным коммитом без push, ветку не выпускать.
## Этап 4 — блок 9: рекурсивные маршруты (2026-09-14)
По прямому указанию пользователя продолжена работа ниже резерва квоты (89% использовано на старте). `recursive-routes-data-grid` переведён на установленную общую оболочку ReUI/TanStack v9, сохранены управляемое раскрытие по ключу группы, сортировка, обработчики редактирования/удаления и отсутствие пагинации.
Проверки: Playwright `recursive-routes-table.spec.ts` — 1 passed (12 групп через HTTP-мок, сортировка в обе стороны, раскрытие и закрытие деталей после сортировки, отсутствие pageerror); ESLint компонента и теста — успешно. TypeScript до и после браузерного прогона — успешно. Реальные сетевые изменения не проверялись.
Уточнение блока 8: в `.next/types/validator.ts` присутствовала полноценная декларация `RouteHandlerConfig` и проверки обоих API-маршрутов. Удалён только ошибочный сгенерированный `.next/dev/types/validator.ts`; исходники, tsconfig и production-validator сохранены. После этого tsc прошёл, в том числе после нового запуска Playwright. Ошибка предыдущей финальной проверки снята; правка кеша не входит в Git.
После блока использовано 95% пятичасовой квоты. Следующий блок — четыре `route-optimizer-*-data-grid`, затем общие legacy-потребители/`data-table`, старые Filters и удаление v8 с полной регрессией. Этап 4 не завершён. Этот блок сохранить локальным коммитом без push.
## Этап 4 — блок 10: оптимизатор маршрутов (2026-09-14)
Четыре `route-optimizer-*-data-grid` перенесены на установленный ReUI/TanStack v9. Сохранены динамические колонки WAN × JH, отсутствие сортировки матрицы, раскрытие списка комбинаций (5 → все), сортировка и ключи Community, состояния загрузки/ошибки/пустого OSPF preview. Удалены два неиспользуемых импорта.
Проверки: TypeScript до и после Playwright — успешно; ESLint всех четырёх компонентов и нового теста — без замечаний. `route-optimizer-tables.spec.ts`: 4 passed, включая 12 WAN, 12 комбинаций, сортировку и закрепление выбранной Community, 12 интерфейсов OSPF и числовую сортировку, пустой ответ и HTTP 500 preview. Первый прогон имел запрос при завершении теста к отключённому backend; добавлено ожидание загрузки основной таблицы, повторный прогон без этого сообщения. Проверки используют live-ветку с HTTP-моками; применение RouterOS не выполнялось.
Обнаруженный исходный долг для этапов 5/10: `applyRec` страницы имитирует успех через таймер даже в live-режиме; `buildLiveOptimizerData` рассчитывает вероятности Community из длины строки и использует оценочные ping/bandwidth при отсутствии замеров. Тест закрепления не подтверждает реальность применения или достоверность этих оценок. Алгоритмы в блоке миграции не менялись.
Квота: новое окно 0% на старте, 13% использовано после проверок, недельная — 91%. Остаток Data Grid: `components/data-table.tsx`, layout-импорты `DataPageCard`/`DataPageToolbar` и затем удаление общей legacy-основы/v8. Старые Filters остаются у BGP, statistics, backups, backups-history и в fallback DataPageToolbar. Этап 4 не завершён; сохранить блок локальным коммитом без push.
## Этап 4 — блок 11: удаление старого Data Grid (2026-09-14)
Мигрирована общая `DataTable` с сохранением опциональной клиентской пагинации (`manualPagination: !pagination`). `DataPageCard` и `DataPageToolbar` используют современный layout; значения CSS-классов совпадают с прежними. Удалены 14 файлов неиспользуемой legacy-копии и обёрток, а также alias `@tanstack/react-table-v8` и его вложенная зависимость из package.json/lockfile. Версия v9 остаётся 9.2.4. Поиск по app/components/lib/package.json/package-lock.json больше не находит импортов legacy Data Grid/v8.
Проверки после удаления общей основы: TypeScript и ESLint изменённых обёрток — успешно; вся браузерная регрессия — **49 passed**; production build — успешно, 34 статические страницы. У `DataTable` сейчас нет потребителей, её generic API проверен компилятором; пагинация рабочих таблиц покрыта регрессией справочников. Next.js сообщил о восстановлении SWC lockfile, итоговый diff lockfile содержит только удаление v8 и вложенного table-core. Старый Filters ещё не удалён: этап 4 остаётся открытым, ветку не выпускать.
Следующий блок — BGP и backups/backups-history на FilterQuery, затем statistics с явным ограничением на поддерживаемые API-срезы (AND равенств) и удаление старого Filters/fallback. Сохранить текущий блок отдельным локальным коммитом без push.
## Этап 4 — блок 12: Filters BGP и резервных копий (2026-09-14)
BGP, backups и BackupsHistory используют FilterQuery и современный `QueryFilters` через `filterControl`. Поля переведены на FilterField; scalar-поля с несколькими вариантами используют select и операторы выбора. Фильтрация выполняется общим рекурсивным evaluator с сохранением AND/OR и черновиков. Для текстового поля «Сервер» добавлены поддерживаемые ReUI операции starts_with/ends_with, включая отрицание и сравнение без учёта регистра.
Проверки: TypeScript — успешно; 8 профильных браузерных тестов BGP/OSPF/администрирования — passed, включая фильтрацию, изменение значения, AND и сброс; 7 unit-тестов evaluator — passed. Первый запуск tsx в sandbox завершился системной ошибкой uv_os_get_passwd ENOMEM; повтор вне sandbox прошёл. ESLint без ошибок в проверенных файлах, в BGP остаются 7 исходных предупреждений неиспользуемых импортов/StateBadge; известная исходная ошибка effect в backups/page не исправлялась этим блоком, lint всей этой страницы не заявляется чистым.
После блока использовано 42% пятичасовой и 96% недельной квоты. Остались statistics, старый fallback DataPageToolbar и старые Filters/helper; после их удаления повторить общие проверки. Сохранить блок локальным коммитом без push. Этап 4 остаётся открытым.
## Этап 4 — подготовка блока 13: адаптер статистики (2026-09-14)
Добавлен независимый `statistics-filter-query.ts`: преобразование URL-срезов в FilterQuery и строгий обратный адаптер с результатами valid/draft/invalid. Поддерживается только AND равенств скалярным строкам; OR, отрицание, массивы, неизвестные поля и конфликтующие значения не преобразуются молча. Незавершённый черновик не возвращает частичный набор срезов. Страна нормализуется в двухбуквенный код, ASN/serverId проверяются как положительные безопасные целые; недопустимые значения не обрезаются и не очищаются до другого числа.
Проверки: 5 unit-тестов адаптера, ESLint и TypeScript — успешно. Адаптер пока не подключён к экрану: это подготовительный checkpoint, а не завершение миграции statistics.
Точный следующий шаг: создать StatisticsFilters с отдельным локальным черновиком FilterQuery и отображением ошибки адаптера; обновлять URL/запрос только при valid, сохранять прежние срезы при draft/invalid; синхронизировать внешний переход/сброс URL с черновиком. Полям дать только оператор is, остальные неподдерживаемые выражения всё равно проверять адаптером. Затем удалить старые slicesToFilters/filtersToSlices страницы, fallback DataPageToolbar, `components/reui/filters.tsx` и неиспользуемый старый evaluator после проверки всех импортов. Unit-тест совместимости со старым evaluator заменить самостоятельными ожидаемыми результатами. Завершить браузерными тестами URL/черновика/ошибки и общей регрессией, только затем закрывать этап 4.
Квота после подготовки: 49% пятичасовой и 97% недельной использовано. Сохранить подготовку локальным коммитом без push. Чужие изменения skill/AGENTS/.codex не включать.
## Этап 4 — блок 13: подключение Filters статистики (2026-09-14)
StatisticsFilters подключён вместо старого массива Filter. Поля предлагают только is, адаптер дополнительно отклоняет неподдерживаемые выражения. Локальный черновик не изменяет URL до явного «Применить срез»; invalid/draft блокируют применение и сохраняют действующие срезы. Ключ редактора зависит от URL-срезов, поэтому внешний переход сбрасывает черновик. Старые функции slicesToFilters/filtersToSlices страницы удалены.
TypeScript до и после первого браузерного прогона и scoped ESLint прошли. Расширенный браузерный сценарий статистики прошёл: сортировка/переход в срез, восстановление фильтра из URL, редактирование без изменения URL, применение, сброс, отклонение RUS без обрезания до RU и внешняя смена страны на DE. В первом расширенном прогоне исправлен неоднозначный локатор alert; повтор — 1 passed. Проверки через HTTP-моки.
На последнем чтении использовано 62% пятичасовой и 99% недельной квоты. Старые Filters/fallback/helper ещё физически остаются, поэтому этап 4 НЕ завершён. Следующий шаг — удалить неиспользуемый fallback DataPageToolbar и старые Filters/evaluator после проверки импортов, обновить тест совместимости, затем выполнить полную регрессию и production build. Сохранить этот блок локальным коммитом без push.
## Этап 4 — завершение: удаление старого Filters (2026-09-20)
Удалены `components/reui/filters.tsx`, `apply-reui-filters.ts` и временный `migrate-filter-query.ts`. У DataPageToolbar удалены устаревшие props filters/onFiltersChange/filterFields и их fallback; все четыре действующих потребителя используют filterControl и FilterQuery. Props filters/onFiltersChange у BackupsHistory остаются внутренним контрактом нового FilterQuery, а не старого Filters. Unit-тест сравнения двух реализаций заменён независимыми ожидаемыми результатами для шести операторов с AND.
| Финальная проверка миграции | Результат |
| --- | --- |
| Поиск старых FilterFieldConfig/applyReuiFilters/migrateFilterQuery, legacy-data-grid и react-table-v8 в app/components/lib/tests/package.json/package-lock.json | Совпадений нет |
| Unit-тесты дерева FilterQuery | 7 passed |
| Unit-тесты адаптера статистики | 5 passed |
| TypeScript без incremental | Успешно |
| ESLint изменённых файлов | Успешно |
| Полная браузерная регрессия | 51 passed, 2.4 минуты |
| Production build | Успешно, 34 статические страницы |
| git diff --check изменённых файлов | Успешно |
Получен актуальный ReUI MCP `get_audit_checklist`: в этом блоке сохранены установленная композиция, публичный API и тема, новых данных/интерактивных элементов/секретов не добавлено. Миграция Data Grid/TanStack v9 и Filters завершена; старые временные API больше не нужны. Это завершение технической миграции, а не полной UX/UI-приёмки: остаются ранее записанные ограничения адаптивности/доступности, исходные lint-долги страниц, имитация Community apply и оценки в оптимизаторе. Реальные RouterOS, права на сетевые операции и все состояния backend по мокам не подтверждены. Полный repository lint не заявляется чистым; профильные проверки изменённых участков и production build прошли.
Квота: 2% пятичасовой/0% недельной использовано на старте; после регрессии — 32%/5%. Этап 4 сохранить локальным коммитом без push. Изменения skills/AGENTS/.codex другой работы не включать. Общую регрессию этапа 4 считать выполненной; автоматический merge, push или релиз не выполнялись.
**Следующий этап 5:** `hooks/use-dashboard-live.ts` и `app/(main)/dashboard/page.tsx`. Подтверждено чтением: fetchSnapshot уже использует Promise.allSettled, но при ошибках очищает probes/servers/BGP/traffic/overlay, а fetchRecentEvents очищает события; обновления запускаются из нескольких effects и таймеров без защиты результатов запросов. Ввести состояния/свежесть по источникам и сохранение последних успешных данных; отличать начальную ошибку от частичного сбоя и фонового обновления. Защитить смену backend/mode и перекрывающиеся запросы от поздних результатов, включая дополнительные запросы Internet Path. Проверить HTTP-моками первоначальную ошибку, частичный сбой, повторное восстановление и гонки; профильный UX показывать без подмены неизвестных данных нулями. Зафиксировать отдельным коммитом этапа 5.
## Этап 5 — состояния данных дашборда (2026-09-20)
`use-dashboard-live` сохраняет последние успешные servers/probes/BGP/traffic/events и отдельные GRE/WireGuard/VXLAN при сбоях. Состояние и время успешного получения ведутся по девяти источникам; Internet Path дополнительно показывает серверный sampledAt. Новый DashboardDataStatus использует существующие Alert/Button, сообщает частичный сбой, раскрывает время/ошибки источников и позволяет повторить загрузку. Неизвестные KPI отображаются как «—», пустые очереди проблем при недоступных источниках больше не утверждают «всё исправно». При фоновом обновлении сохранённые данные остаются видимыми.
Для snapshot и событий введены независимые счётчики запросов: применяются только результаты последнего запроса, включая дополнительные await Internet Path; cleanup инвалидирует незавершённые ответы при смене контекста или размонтировании. Убран дублирующий первоначальную загрузку effect по pathname. Смена backend/mode очищает данные предыдущего контекста. Это защита состояния, а не отмена уже отправленных HTTP-запросов.
Internet Path использует успешный серверный снимок независимо от сбоя списка серверов. Ошибка snapshot или обязательных данных fallback сохраняет старую модель; ошибки дополнительных lookup/wan-runtime/speed-probes не маскируются новым пустым снимком. Сами сетевые команды и правила выбора маршрутов не менялись.
Проверки: TypeScript и scoped ESLint — успешно; **5 браузерных сценариев passed**: начальная ошибка и восстановление, частичный сбой с сохранением данных/событий/времени, поздний основной ответ, ошибка дополнительной загрузки Internet Path, поздний дополнительный ответ. Для проверки поздних ответов тест дожидается завершения HTTP и двух кадров отрисовки. Сценарий частичного сбоя дополнительно повторён на ширине 390; скриншоты 1440/390 просмотрены, новый блок не обрезан, восстановление доступно. Получен ReUI checklist; используются существующая композиция, семантические цвета, role=status/alert и подписанная кнопка type=button. Общая основа ReUI не менялась, полная регрессия/production build не повторялись после успешной приёмки этапа 4 в этом окне.
Границы: браузерные проверки идут через HTTP-моки; реальная свежесть RouterOS не подтверждена. Время «Получено» обозначает успешный ответ API, не новый замер устройства; sampledAt показан отдельно, где доступен. Смена backend/mode защищена cleanup, но отдельный браузерный сценарий смены backend не запускался (в e2e backend фиксирован конфигурацией). Существующие доменные допущения вроде null server.status → offline и оценочных метрик оптимизатора относятся к следующим профильным этапам.
Квота: 41% пятичасовой на старте, 73% после профильных проверок (недельная 11%). Следующий этап 6 — общая оболочка и дашборд: показать одну схему ПК/телефона по App Shell 12/Stats 12, затем улучшать навигацию, контекст и очередь проблем с переходами к объектам. Этот этап сохранить отдельным локальным коммитом без push; настройки skills/AGENTS/.codex не включать.
## Этап 6 — блок 1: схема и адаптивная шапка (2026-09-20)
Схема ПК/телефона и таблица референсов сохранены в `docs/ux-ui-stage-6.md`. PageHeader сохраняет установленные SidebarTrigger/Breadcrumb/AppsMenu/SystemMonitorPopover: текущая страница видна на телефоне, действия переносятся, фиксированная высота снята. У Breadcrumb корректные прямые li внутри ol и русское доступное имя. Примитивы ReUI, зависимости и backend не менялись.
Проверки: TypeScript и scoped ESLint успешны. Четыре браузерных сценария шапки (320/390/1024/1440 px) прошли: геометрия заголовка/кнопок, меню с клавиатуры, Escape и возврат фокуса. Дополнительно прошли таблица рекурсивных маршрутов и мобильный сценарий серверов. Просмотрены скриншоты 320/1440. Первый запуск теста ошибочно искал banner: header внутри main этой роли не имеет; локатор исправлен без изменения семантики приложения. ReUI validate_usage/checklist получены ранее, используются установленные API и семантические цвета.
Предыдущая попытка запуска была остановлена ошибкой автоматической проверки разрешений из-за исчерпанной квоты; после сброса проверка запуска прошла. На старте этого продолжения использовано 2% пятичасовой и 16% недельной квоты. Этап 6 остаётся открытым. Следующий блок — единый список Sidebar/CommandPalette и стандартное управление фокусом поиска, затем дашборд и ссылки очереди. Локальный коммит без push; файлы настройки другой работы не включать.
## Этап 6 — блок 2: навигация и поиск (2026-09-20)
`lib/navigation.ts` — общий каталог 30 страниц, групп, иконок и поисковых синонимов. AppSidebar и CommandPalette используют его вместо двух независимых списков. Названия «Мониторинг» и «Communities» согласованы; поисковые синонимы uptime/BGP сохранены. NavMain получил именованный nav и aria-current; мобильная шторка закрывается при клиентском переходе, включая выбор текущей страницы.
CommandPalette использует установленные Command/cmdk и Base UI Dialog вместо самописного затемнённого окна и ручной навигации. Фокус при открытии переходит в поиск, Tab удерживается внутри, Escape возвращает фокус; ввод сбрасывается при новом открытии. Поддержаны поиск по синонимам, стрелки/Enter, объявление пустой выдачи, светлая/тёмная тема. Высота списка ограничена высотой диалога и viewport. Новые зависимости и примитивы не устанавливались; данные/API и схема прав не менялись.
Профильные проверки: 4 сценария navigation passed (один проверяет каталог по реальным файлам страниц), 4 сценария шапки passed. Проверены переход на серверы и возврат браузером, пустой поиск, фокус, мобильный выбор текущей страницы, размеры 390×600 в двух темах; скриншоты просмотрены. TypeScript успешен; ESLint новых navigation/CommandPalette/NavMain/test успешен. В AppSidebar сохранилась исходная ошибка react-hooks/set-state-in-effect на сбросе liveCounts при смене режима; её наличие подтверждено чтением HEAD до изменений, логика счётчиков не менялась. Не заявлять чистый lint всего проекта.
Блок готов для локального коммита без push. Этап 6 продолжается: иерархия дашборда, затем переходы из очереди к конкретным объектам.
## Этап 6 — блок 3: сводка и приоритет проблем (2026-09-20)
Дашборд сохраняет KpiStatGrid, AttentionQueue и Frame, но очередь «Требует внимания» теперь идёт сразу после сводки, до измерений. Быстрые переходы перенесены вниз. Подписи заменены на «Серверы онлайн», «Туннели работают», «Префиксы BGP», «Проверки с проблемами»; состояния в очереди названы по-русски. Длинные имена проблем переносятся вместо обрезки. Состояния пустого каталога серверов, отсутствия туннелей и активных проверок отделены от отсутствия ошибок.
useDashboardLive больше не обрезает массивы проблем до пяти до подсчёта. Счётчики учитывают все элементы; UI сначала показывает пять и раскрывает остальные через «Ещё N» с клавиатуры. Загрузка/частичные сбои/свежесть/защита от поздних ответов этапа 5 сохранены. Запросы и сетевые операции не менялись.
Приёмка текущей общей основы: **65 Playwright-тестов passed**, включая 6 сценариев дашборда, 4 навигации и 4 шапки; отдельные профильные запуски также успешны. Новый сценарий проверяет 7 проблем при 5 видимых строках, раскрытие клавиатурой, порядок очереди перед измерениями, пустые активные проверки и ширину 390. Просмотрены скриншоты дашборда 1440/390 и поиска в двух темах. **Production build успешен: контракты, TypeScript, 34 страницы.** Scoped ESLint нового кода успешен; исходная ошибка AppSidebar описана в блоке 2. ReUI checklist получен; vendored-примитивы, зависимости и тема не менялись. Проверки используют HTTP-моки, реальный RouterOS не проверен.
**Этап 6 ещё не завершён.** Ссылки AttentionRow пока открывают профильный раздел (/servers, /uptime, /gre, /wireguard, /vxlan), без выбора конкретного объекта. Следующий точный шаг: спроектировать единый URL-контракт идентификатора объекта и сервера, подключить принимающие страницы и только после этого заменить href очереди. Начать с сервера и проверить «дашборд → конкретный сервер → назад», прямое открытие URL, удалённый/недоступный объект, затем адаптировать мониторинг и туннели. Не считать query-параметр рабочим, пока целевая страница его не обрабатывает. Полные формы/права/фильтры URL этапа 7 остаются следующим этапом.
Границы: текущий mapper дашборда по-прежнему трактует null server.status как offline; неизвестные состояния устройств требуют отдельного исправления и проверки. Квота при завершении общей регрессии и запуске build: использовано 65% пятичасовой и 26% недельной. Сохранить блок локальным коммитом без push; изменения skills/AGENTS/.codex не включать.
## Этап 6 — блок 4: переход к серверу (2026-09-20)
Очередь серверов дашборда формирует `/servers?server=<encoded id>`. Страница серверов читает параметр в Suspense, ограничивает таблицу точным ID и показывает контекст перед общей сводкой. «Все серверы» снимает только параметр server, сохраняя остальные query-параметры; «К дашборду» и браузерный возврат доступны. Прямой URL и перезагрузка сохраняют выбор. При отсутствующем ID или 403/500 другой сервер не подставляется. Состояния загрузки, ошибки получения и отсутствующего объекта разделены.
При выбранном сервере верхняя кнопка «Проверить сервер» опрашивает только его через существующую очередь операций. Без параметра сохраняется прежний массовый опрос. API, права и правила мутаций не менялись. Контекст использует установленный ReUI Alert; состав общей сводки явно указан. Полная синхронизация поиска/Filters/форм с URL остаётся этапу 7.
Проверки: 27 профильных сценариев servers/dashboard passed; после переноса контекста над сводкой повторно пройден сценарий выбора/опроса/возврата и просмотрен мобильный экран 390. ESLint изменённых файлов успешен. Production build прошёл: контракты, TypeScript, 34 страницы. HTTP-фикстура дашборда дополнена и проверяется serverReadSchema: переход на сервер использует строгий API-контракт. Реальный RouterOS не проверялся.
Окружение: предыдущий запуск остановился из-за исчерпанной квоты автоматической проверки разрешений. После сброса обнаружен dev-кеш Next.js, возвращавший 404 для существующего маршрута; .next/dev сохранён как .next/dev-before-server-links, чистый кеш устранил ошибку, пять URL-сценариев и вся профильная регрессия прошли. Это игнорируемые сборочные артефакты, не исходники.
Квота нового окна: 1% использовано на старте, 26% при запуске итоговой сборки. Блок сохранить отдельным локальным коммитом без push. Этап 6 открыт: далее `/uptime?probe=<id>` и обработка выбранной проверки, затем туннели с идентификатором устройства и объекта.
## Этап 6 — блок 5: переход к проверке мониторинга (2026-09-20)
Очередь проверок формирует `/uptime?probe=<encoded id>`. UptimeRoute читает параметр в Suspense и сбрасывает локальный контекст страницы при выборе другого ID или полного списка. Фильтрация использует точный ID, поэтому одинаковые имена/назначения не смешиваются. Контекст показывает источник, назначение, состояния загрузки/ошибки/отсутствия; «Все проверки» удаляет только probe, остальные query-параметры сохраняются. Прямой URL, перезагрузка, браузерный возврат и ссылка на дашборд работают.
Ссылка и верхнее «Обновить данные» выполняют чтение, без collect-now. Ручной ping существующей кнопкой группы отправляет только ID выбранной проверки; формат API и механизм мутаций не менялись. При сбое фонового обновления прежние данные остаются, а контекст предупреждает об их возможном устаревании. В режиме полного списка верхнее обновление сохраняет прежнее поведение. Выбор не распространяется на отдельные вкладки ресурсов/speed; общий URL-контракт вкладок и фильтров относится к этапу 7.
Проверки: пять новых сценариев uptime-links passed, затем **18 связанных сценариев passed** (uptime-links/dashboard-data/collector-probes-tables/navigation). Проверены совпадающие имена, кодирование ID со слешем, отсутствие POST при переходе/чтении, payload ручного ping, сброс/возврат/reload, отсутствующий объект, 403/500, сохранение строки при 503 и восстановление. Мобильный блок контекста 390 просмотрен и не обрезан. У существующей широкой сетки строк Ping-проб на телефоне по-прежнему обрезается правая часть; исправление сетки и доступности её действий включить в этап 7, не считать весь экран мониторинга принятым по этой проверке.
Production build успешен: контракты, TypeScript, 34 страницы. Lint нового теста и изменённого hook успешен. У uptime/page.tsx исходные одна ошибка react-hooks/set-state-in-effect в эффекте speedDraft и шесть предупреждений unused-vars — тот же набор подтверждён запуском ESLint на содержимом HEAD до изменений. Новых lint-ошибок нет; не заявлять чистый lint проекта. ReUI checklist получен в этом продолжении, используются установленный Alert и существующая композиция. Проверки на HTTP-моках, реальная сеть не проверена.
Квота перед итоговой сборкой: использовано 55% пятичасовой и 40% недельной. Сохранить отдельный локальный коммит без push. **Следующий шаг этапа 6 — туннели GRE/WireGuard/VXLAN:** добавить сервер и ID объекта в href overlay, обработать оба значения на принимающих страницах, исключить подстановку другого устройства при отсутствующем ID. Проверить прямую ссылку, возврат, одинаковые имена/RouterOS ID на разных устройствах и отсутствие мутаций при открытии. Этап 6 остаётся открытым до этих переходов и приёмки.
## Этап 6 — блок 6: переход к GRE-туннелю (2026-09-20)
Очередь проблем открывает `/gre?server=<serverId>&tunnel=<id>`; оба значения кодируются. Ключ GRE в очереди включает сервер и ID, поэтому одинаковые RouterOS ID на разных устройствах не конфликтуют. Принимающая страница выбирает точную пару, показывает контекст через установленный ReUI Alert и сохраняет остальные query-параметры при снятии выбора. Ссылка «Все туннели сервера» оставляет сервер; ручная смена устройства снимает выбор туннеля и отражается в URL. Back/reload восстанавливают контекст. Открытие ссылки не отправляет POST.
Отдельно обработаны отсутствующая пара, ошибки 403/500, неуспешное обновление с сохранением последних данных и частичный сбой выбранного сервера. Такой сбой не выдаётся за отсутствие туннеля; действия таблицы блокируются. Полная достоверность сводки GRE при частичном опросе и сетевые формы остаются этапом 9.
Тесты обнаружили автоматический сброс URL-выбора серверной панелью до завершения загрузки. Добавлен opt-in `preserveSelection` в ServerRailLayout/ServerTileRail, включён для GRE; существующие потребители сохраняют прежнее поведение. При отсутствующем сервере панель не выбирает другой автоматически.
Проверки: 20 связанных E2E (GRE, IPsec, дашборд, навигация) passed, затем отдельный дополнительный сценарий ручной смены устройства passed — всего 21. Production build и TypeScript успешны, 34 статические страницы. Скриншот контекста ошибки просмотрен на 390 px. ESLint новых замечаний не добавил: у GRE прежние 14 unused warnings, у ServerRailLayout прежняя ошибка set-state-in-effect (оба baseline сверены с HEAD); остальные изменённые TS-файлы чистые. Проверки выполнены с HTTP-моками, реальный RouterOS не проверен.
Квота: на старте использовано 69% пятичасовой, перед заключительными проверками 86% (недельной 45%). Следующий блок в этом окне не начинать. Сохранить локальным коммитом без push. **Следующий шаг: WireGuard, затем VXLAN** — URL с сервером и точным ID, запрет fallback на другой сервер, переиспользовать preserveSelection; проверить одинаковые имена/ID, прямой URL, Back, ошибки и отсутствие автоматических мутаций. Этап 6 остаётся открытым до завершения этих переходов и приёмки.
## Карта миграции (исходная инвентаризация этапа 0)
Исходная связка на этапе 0: @tanstack/react-table ^8.21.3, @tanstack/react-virtual ^3.14.4, base-nova. По итогам этапа 4 используется @tanstack/react-table 9.2.4 с useTable/dataGridFeatures и дерево FilterQuery вместо старого filters/onChange. Список ниже сохранён как историческая инвентаризация.
Источники: [контракт UI](ui-design-contract.md), [таблицы PRO](https://reui.io/preview/base/data-grid-filtering-2?ref=mcp), [API Data Grid](https://reui.io/docs/components/base/data-grid?ref=mcp), [API Filters](https://reui.io/docs/components/base/filters?ref=mcp).
### Прямые потребители TanStack (47)
- `components/data-grids/asns-data-grid.tsx`
- `components/data-grids/backups-data-grid.tsx`
- `components/data-grids/bgp-sessions-data-grid.tsx`
- `components/data-grids/certificates-data-grid.tsx`
- `components/data-grids/communities-data-grid.tsx`
- `components/data-grids/compact-data-grid.tsx`
- `components/data-grids/data-collection-scheduler-data-grid.tsx`
- `components/data-grids/domains-data-grid.tsx`
- `components/data-grids/filters-data-grid.tsx`
- `components/data-grids/firewall-rules-data-grid.tsx`
- `components/data-grids/firewall-scenario-rules-data-grid.tsx`
- `components/data-grids/gre-pools-data-grid.tsx`
- `components/data-grids/gre-tunnels-data-grid.tsx`
- `components/data-grids/ip-ranges-data-grid.tsx`
- `components/data-grids/ospf-bfd-data-grid.tsx`
- `components/data-grids/ospf-neighbors-data-grid.tsx`
- `components/data-grids/ospf-routes-data-grid.tsx`
- `components/data-grids/probes-schedule-data-grid.tsx`
- `components/data-grids/probes-speed-probes-data-grid.tsx`
- `components/data-grids/recursive-routes-data-grid.tsx`
- `components/data-grids/route-optimizer-comm-recs-data-grid.tsx`
- `components/data-grids/route-optimizer-full-routes-data-grid.tsx`
- `components/data-grids/route-optimizer-ospf-preview-data-grid.tsx`
- `components/data-grids/route-optimizer-wan-matrix-data-grid.tsx`
- `components/data-grids/servers-data-grid.tsx`
- `components/data-grids/shared/data-grid-shell.tsx`
- `components/data-grids/shared/data-grid-sort-header.tsx`
- `components/data-grids/subusers-data-grid.tsx`
- `components/data-grids/traffic-flows-data-grid.tsx`
- `components/data-grids/uptime-resources-data-grid.tsx`
- `components/data-grids/uptime-speed-history-data-grid.tsx`
- `components/data-grids/users-data-grid.tsx`
- `components/data-grids/vxlan-data-grid.tsx`
- `components/data-grids/wireguard-data-grid.tsx`
- `components/data-grids/wireguard-peers-grid.tsx`
- `components/data-table.tsx`
- `components/ipsec/ipsec-users-grid.tsx`
- `components/reui/data-grid/data-grid-column-filter.tsx`
- `components/reui/data-grid/data-grid-column-header.tsx`
- `components/reui/data-grid/data-grid-column-visibility.tsx`
- `components/reui/data-grid/data-grid-table-dnd-rows.tsx`
- `components/reui/data-grid/data-grid-table-dnd.tsx`
- `components/reui/data-grid/data-grid-table-virtual.tsx`
- `components/reui/data-grid/data-grid-table.tsx`
- `components/reui/data-grid/data-grid.tsx`
- `components/traffic/flow-analytics-panel.tsx`
- `components/traffic/flow-traffic-map.tsx`
### Прямые потребители Filters (6)
- `app/(main)/backups/page.tsx`
- `app/(main)/bgp/page.tsx`
- `app/(main)/servers/page.tsx`
- `app/(main)/statistics/page.tsx`
- `components/backups/backups-history.tsx`
- `components/data-page-toolbar.tsx`