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

610 lines
137 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 | Навигация и дашборд | Завершён 2026-09-20 в границах локальной приёмки; ограничения ниже |
| 7 | Серверы и мониторинг: детали, формы, URL-контекст, права | В работе: 7.1–7.2, права серверов и ограничения действий мониторинга выполнены; формы проб сохраняются после API; переключатели подтверждаются API; удаление и звезда подтверждаются API; далее конкуренция измерений |
| 8 | Трафик и статистика | Не начат |
| 9 | VPN, Firewall и Filters | Не начат |
| 10 | Маршрутизация, диагностика, карта и терминал | Не начат |
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Не начат |
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Не начат |
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат |
## Блок 7.4Б2 — удаление и звезда (2026-09-21)
- После сброса пятичасовой квоты создан ранее отложенный коммит 4f23505. Предыдущий git add не выполнялся: auto-review не смог завершиться из-за лимита; повтор выполнен после восстановления квоты.
- Ping/Speed удаляются через существующий AlertDialog с названием/целью. Отмена не пишет данные; подтверждение выполняет PUT через общий gate. Во время запроса закрытие/повтор заблокированы; 403 оставляет диалог и строку, успех удаляет строку. Повторная проверка прав выполняется в обработчике.
- Звезда дашборда теперь применяет состояние после PATCH и сообщает ошибку, сохраняя прежнее значение. PATCH имеет timeout 30 секунд и использует тот же gate, что формы/переключатели/удаление; обновление mock-звёзд и событие дашборда отправляются после успеха.
- Проверки: все 30 E2E uptime-links прошли, включая 3 новых сценария удаления/звезды, прежние формы/права/mobile/ссылки. TypeScript прошёл; lint теста чистый, uptime — прежние 1 set-state-in-effect и 6 unused warnings. ReUI audit: переиспользован AlertDialog из серверного сценария, структура установленных компонентов и тема не изменены. Production build прошёл: 34 статические страницы.
- Осталось 7.4Б3: общий порядок записи и запущенных измерений (runSpeedTest, collect-now, collect-group), блокировка повторного запуска, поздние ответы после смены контекста. PUT/PATCH CRUD теперь сериализованы между собой, но запуск измерения пока не занимает общий gate. Далее URL-контекст мониторинга и приёмка 7.5.
- Проверки — HTTP-моки, реальные JWT/RouterOS не проверялись. Контроль квоты: 12% пятичасовой / 94% недельной израсходовано. Коммит локальный без push.
## Блок 7.4Б1 — подтверждённые переключатели (2026-09-21)
- По прямому поручению пользователя работа продолжена ниже резерва. Ping enabled и Speed update теперь используют общий saveProbeForm: синхронная блокировка, timeout, защита от устаревшего чтения, применение строки только после PUT. Ошибка видна вне Sheet и не сбрасывается фоновым GET.
- ID новой пробы создаётся при открытии формы и сохраняется при повторной отправке. Установленные компоненты ReUI и тема сохранены; checklist проверен.
- 14 профильных E2E прошли суммарно: 12 сценариев форм/прав, затем 2 сценария переключения (ожидание, 403, повтор). Первоначальные два сбоя были неоднозначным селектором alert из-за Next route-announcer; селектор уточнён. ESLint: прежние 1 ошибка/6 предупреждений uptime, тесты чистые. Финальный production build прошёл: 34 страницы, TypeScript без ошибок.
- Осталось: подтверждение удаления, применение удаления только после API, ошибка звезды и конкуренция ранее запущенных PATCH/Speed-test/PUT. Эти действия пока не переведены на общую блокировку. Локальные HTTP-моки не проверяют RouterOS/JWT.
- Квота на завершении блока: 96% пятичасовой и 92% недельной израсходовано. Следующий шаг — 7.4Б2 (удаление и звезда), затем URL мониторинга и приёмка. Коммит локальный, без push.
## Блок 7.3В/7.4А — подтверждённое сохранение форм (2026-09-21)
- Ping/Speed: формы ожидают PUT, обновляют локальный список и закрываются только после успешного ответа. При 400/403/500, сетевой ошибке или таймауте сохраняются поля и показывается ошибка в Sheet. Общий serverOperationError переиспользован для понятного 403 и неопределённого результата timeout.
- Синхронный ref блокирует повторное сохранение до React render; на время запроса заблокированы поля, действия, Отмена и Escape. PUT ограничен 30 секундами. Права повторно проверяются перед сохранением. API по-прежнему заменяет весь список; серверный контракт не менялся.
- GET overview/speed, начатые до сохранения, не применяются после него; чтение пропускается во время сохранения. Поздний ответ записи не применяется после смены mode/backend или размонтирования. Не утверждать полное устранение гонок ранее начатых PATCH/операций строк — это следующий блок.
- Проверки: 25 сценариев uptime-links прошли суммарно, включая 10 новых (400/403/500 и повтор для двух форм, двойное нажатие/Escape, таймаут). В первом прогоне 2 проверки ошибочно проверяли disabled у fieldset; заменены проверкой реального input, оба повторно прошли. TypeScript и production build прошли (34 страницы). ESLint теста чистый; у uptime прежние 1 ошибка set-state-in-effect и 6 предупреждений.
- ReUI audit: сохранены установленные Sheet/Alert, дизайн и тема; fieldset даёт нативную блокировку полей, aria-busy сообщает состояние. HTTP-моки не проверяют реальный JWT/RouterOS.
- Следующий шаг 7.4Б: подтверждение удаления, обновление строки только после API при переключении/удалении, обработка ошибки звезды и блокировка конкурирующих записей. Старые row-handlers пока оптимистичные; их PUT теперь сообщает ошибку вместо её поглощения, но rollback/подтверждение не реализованы. Затем полный URL-контекст мониторинга и приёмка 7.5.
- Контроль квоты: использовано 65% пятичасовой / 87% недельной; локальный коммит без push, сторонние файлы не включать.
## Блок 7.3Б — ограничения прав мониторинга (2026-09-21)
- Uptime использует can("mm:uptime:write") для Ping/Resources/Speed: создание, редактирование, удаление, переключение, звезда дашборда, групповой ping, collect-now и bandwidth-test. Дополнительное создание интерфейса из группы также ограничено. Backend-политика и auth disabled не изменены.
- Для читателя «Обновить данные» выполняет GET сохранённых результатов без POST collect-now. Фоновые GET, вкладки, фильтры, графики и CSV ресурсов доступны. Права servers:write и флаг is_admin не заменяют uptime:write; наследование uptime:admin сохранено.
- Обработчики повторно проверяют актуальные claims до изменения локального состояния/запроса. При локальном отзыве права форма Ping/Speed остаётся открытой, сообщение показано внутри неё. Кнопки пересчитываются на следующем render; подписка на изменения сессии не добавлена.
- Проверки: прежние 11 E2E прошли, новые 7 сценариев прав прошли; после проверки дополнительной кнопки повторно прошли 3 read-only сценария. TypeScript прошёл. ESLint теста чистый; uptime сохраняет исходные 1 set-state-in-effect (speedDraft) и 6 unused warnings. ReUI audit: установленные компоненты, композиция, тема сохранены; добавлены state/disabled и Alert. Финальный production build прошёл: 34 статические страницы.
- 7.3 ещё не принят целиком: persistProbes/persistSpeedProbes скрывают ошибки PUT, а формы оптимистично закрываются. Следующий блок 7.3В/7.4: подтверждённое сохранение Ping/Speed, сохранение формы на API 403/400/500, блокировка повторов, сообщения ошибок для переключения/звезды/удаления. Не считать локальный отзыв claims проверкой серверного 403.
- Реальный JWT/RouterOS не проверялся: использованы HTTP-моки и синтетические claims. На контрольной точке израсходовано 42% пятичасовой и 83% недельной квоты. Локальный коммит без push, исходные сторонние изменения не включать.
## Блок 7.3А — права действий серверов (2026-09-21)
- ServersPage использует существующий can("mm:servers:write") после AuthGuard. Backend относит создание/изменение/удаление, POST poll и test-connection к записи; политика API не изменена. Наследование write/admin и auth disabled сохранены, is_admin не даёт отдельного обхода.
- Режим «Только просмотр»: отключены добавление, редактирование, включение, удаление, опрос в заголовке/меню/деталях и повтор опроса. Детали, фильтры и WebFig доступны. Пустой список не предлагает читателю добавить сервер.
- Обработчики повторно проверяют актуальные claims перед действием; массовый опрос — перед каждым запросом. Отзыв права не закрывает форму и не отправляет запись. Проверка в UI не заменяет backend; неизвестный клиенту отзыв сервер возвращает как 403 с сохранением формы. Внешняя смена token отражается на следующем render/действии; подписка на изменения сессии не добавлена.
- Проверки: 32 E2E servers.spec.ts прошли (27 прежних и 5 прав), TypeScript и профильный ESLint прошли. Production build прошёл: 34 статические страницы. ReUI audit: композиция, установленный Data Grid и тема сохранены; добавлены только состояние прав, props прикладных компонентов и сообщение Alert.
- Границы: HTTP-моки и тестовые claims без реальной JWT-проверки; RouterOS не проверялся. Известная кнопка «Экспорт» серверов без обработчика остаётся задачей форм/действий 7.4.
- Следующий шаг: 7.3Б — инвентаризация write-действий uptime (Ping/Resources/Speed), сопоставление с backend и ограничения прав, затем профильные HTTP-мок тесты. 7.3 целиком пока не завершён.
- Лимит после основного блока: использовано 15% пятичасового, 79% недельного. Локальный коммит без push; исходные изменения навыков/AGENTS/.codex не включать.
## Этап 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 остаётся открытым до завершения этих переходов и приёмки.
## Этап 6 — блок 7: переход к интерфейсу WireGuard (2026-09-20)
Готов `/wireguard?server=<serverId>&interface=<id>` из очереди проблем, включая встроенные моки. Выбирается точная пара; таблица, пиры и сводка ограничены интерфейсом. Alert показывает сервер и адрес, ошибки и отсутствие объекта. Снятие выбора сохраняет сервер и остальные query; смена сервера снимает interface, Back/reload восстанавливают выбор. Используется preserveSelection без подстановки другого сервера.
При ошибке HTTP последние данные сохраняются с предупреждением; изменяющие действия для устаревших интерфейсов/пиров блокируются. Частичные failures выбранного сервера показаны отдельно. Полная достоверность KPI при частичных сбоях и UX форм остаются этапом 9. Ключи строк интерфейсов включают сервер, пиров — сервер/интерфейс; раскрытие строк и поиск интерфейса для действий пира используют тот же контекст.
Проверки: 9 новых сценариев wireguard-links и 2 прежних wireguard-tables passed; 6 сценариев dashboard-data passed. Проверены одинаковые ID/имена, отдельные строки и раскрытие, вкладка пиров, URL/Back/reload, 403/500/503, частичный сбой, восстановление и отсутствие запросов записи. Мобильный контекст 390 px просмотрен. ESLint всех изменённых TS-файлов успешен; production build/TypeScript, 34 страницы — успешно. Реальный RouterOS не проверен, используются HTTP-моки по схеме контрактов.
Квота: 0% использовано на старте нового окна, 25% перед сборкой (недельной 51%). Блок сохранить локальным коммитом без push. Следующий шаг — VXLAN и итоговая приёмка переходов этапа 6.
## Этап 6 — блок 8: VXLAN и итоговая приёмка (2026-09-20)
Готов переход `/vxlan?server=<serverId>&tunnel=<id>`. Выбор точной пары ограничивает список и сводку, сохраняется после reload/Back, снимается при ручной смене сервера. Остальные query сохраняются. Использованы установленный ReUI Alert, ServerRail preserveSelection и существующая Frame-композиция. Ключи очереди и строк таблицы учитывают сервер. Проверен экспорт .rsc выбранного объекта, без автоматических запросов записи при навигации.
Ошибка HTTP сохраняет последние данные и показывает предупреждение. Backend VXLAN сейчас возвращает пустой список при неуспешном опросе устройства: listVxlanTunnels и listVxlanTunnelsForServer подавляют исключения. Поэтому сообщение по ссылке говорит «туннель не получен» и перечисляет возможные причины, не утверждая, что объект удалён. Исправление этого API, полной достоверности сетевых KPI, форм и существующих незавершённых действий VXLAN — этап 9; сетевые алгоритмы в этом блоке не менялись.
Проверки блока: 8 новых vxlan-links, прежний vxlan-table и 6 dashboard-data — 15 passed. Итоговая приёмка этапа 6: **68 E2E passed** (page-header, navigation, dashboard-data, servers, uptime-links, gre-links, wireguard-links, vxlan-links, wireguard-tables, vxlan-table). Покрыты клавиатура/Escape, темы навигации, шапка 320/390/1024/1440 px, все пять типов объектов очереди, точный контекст, совпадающие ID, возврат, HTTP-ошибки и повтор операций. Контекст VXLAN просмотрен на 390 и 1440 px, WireGuard — на 390 px. Все изменённые TS-файлы WireGuard/VXLAN проходят ESLint. Финальные production build, TypeScript и генерация 34 страниц успешны; для сборки уточнён локальный тип существующего serverId в ответе VXLAN. ReUI audit checklist сверён: компоненты переиспользованы, ссылки настоящие, состояния объявляются role=status, vendored-компоненты/тема/зависимости не менялись.
**Этап 6 завершён в границах UI и HTTP-моков.** Реальные устройства, серверная авторизация и сетевые операции требуют отдельной приёмки. Это не итоговая приёмка всего продукта: известная обрезка мобильных строк Ping-проб остаётся этапом 7, прежние lint-ошибки общих компонентов перечислены выше. Квота перед фиксацией: использовано 70% пятичасовой и 58% недельной. Сохранить локальным коммитом без push; следующий крупный этап начать отдельным блоком.
**Следующий точный шаг — этап 7:** составить схему серверов/мониторинга по Data Grid Filtering 2 и Sheet 8, инвентаризировать права действий и текущие формы. Первый ограниченный блок — URL-контекст поиска/типа/Filters на /servers (с сохранением server), проверка прямого URL/Back/reload и возврата к списку. Затем мобильные строки Ping-проб и формы/права по журналу. Не менять сетевые правила в рамках визуальной унификации.
## Этап 7 — подготовка (2026-09-20)
На старте продолжения использовано 80% пятичасовой квоты (остаток 20%, ниже резерва). Код приложения не менялся: выполнена ограниченная подготовка по текущим файлам. Создан `docs/ux-ui-stage-7.md` с таблицей референсов, схемами ПК/телефона, инвентаризацией форм/прав и разбивкой 7.1–7.5.
Подтверждено: серверы сохраняют в URL только server; поиск/тип/Filters локальные. Ping-пробы используют фиксированную сетку внутри overflow-hidden. В lib/auth.ts уже есть can/hasPermission, а на двух страницах проверки прав действий не подключены; backend-политика разделяет servers/uptime read/write. В плане записан конкретный контракт q/type/filters, ограничения parser и сценарии проверки Back/reload/черновиков/ошибочного URL.
Проверка подготовки — сверка путей, типов, обработчиков и правил backend по исходникам, git diff --check. Тесты приложения повторно не запускались: изменения только документационные. Сохранить локальным коммитом без push. **Следующий шаг: блок 7.1 по ux-ui-stage-7.md — codec URL серверов с unit-тестами, затем подключение к странице и профильные E2E.** Этап 7 не считать реализованным.
## Этап 7 — блок 7.1: URL-фильтры серверов (2026-09-21)
Реализован codec `lib/data-filters/server-url-state.ts`, hook `hooks/use-server-url-state.ts` и подключение к ServersPageContent. Параметры q/type/filters восстанавливают поиск, тип и дерево Filters; server остаётся точным контекстом из этапа 6. JSON имеет версию v=1, сохраняет AND/OR, вложенность и negated. Проверяются поля/значения текущих SERVER_FILTER_FIELDS, операторы select, уникальные ID, размер 8 KiB, глубина 4, число узлов 50. Неизвестный тип, повторные параметры или повреждённые фильтры показывают предупреждение и не расширяют выдачу до всех серверов.
Черновик условия не сериализуется. Сброс q/type/filters сохраняет server и чужие query-параметры. Дискретные изменения используют native pushState; поиск — replaceState без debounce. Это уточнение плана обосновано текущим Next App Router: useSearchParams обновляется без навигационного запроса, быстрый ввод не создаёт записи на каждый символ и не имеет отложенного обновления, затирающего соседние параметры. Тест проверяет неизменное число GET списка при вводе. Back/Forward очищают локальный черновик и восстанавливают URL-состояние; формы и API операций не менялись.
Проверки: 4 unit-теста codec passed; 21 прежний сценарий servers passed; 6 новых URL E2E passed после уточнения селектора alert (в странице также есть Next route announcer). Отдельно повторён сценарий с мобильной проверкой на 390 px, скриншот просмотрен. Проверены прямой URL, Back/Forward/reload, OR/negated, завершение условия, быстрый ввод, отсутствие запросов записи, сброс с server/keep, повреждённый/слишком большой JSON. TypeScript, ESLint всех изменённых TS-файлов и production build (34 страницы) успешны. HTTP-моки, реальные устройства не проверены.
Квота: использовано 1% на старте, 30% перед заключительной сборкой (недельной 66%). Сохранить локальным коммитом без push. **Следующий шаг — 7.2:** мобильные строки Ping-проб в uptime/page.tsx, доступность имени/цели/состояния и меню без обрезки на 320/390 px с сохранением действий, выбора и раскрытия; затем права по блоку 7.3. Этап 7 в целом остаётся открытым.
## Этап 7 — блок 7.2: мобильные строки Ping-проб (2026-09-21)
Устранена отмеченная на этапе 6 обрезка правой части строк Ping-проб. В uptime/page.tsx сохранён один набор контролов и прежние обработчики. Frame-контейнер и локальный probe-rows.module.css переключают строку между двумя колонками с подписями и широкой сеткой при доступной ширине от 1100 px. Длинные имя, фильтр и цель переносятся, серверный заголовок и действия группы тоже допускают перенос. FormToggle получил label, кнопки звезды/редактирования/удаления — aria-label, заголовок группы сервера — aria-expanded. Вендорные ReUI-компоненты, тема, данные и сетевые правила не менялись.
Проверки: **11 E2E passed** (uptime-links и collector-probes-tables). Новые сценарии 320/390/1440 px проверяют длинные подписи, доступность кнопок в пределах экрана, отсутствие горизонтального переполнения страницы, широкую/узкую композицию, переключатель, раскрытие графика Enter, редактирование/Escape, PATCH звезды и PUT включения. Скриншоты 320/1440 просмотрены после исправлений переноса. TypeScript и production build успешны, 34 страницы. Новый тест проходит lint; у uptime/page.tsx прежние 1 ошибка set-state-in-effect и 6 предупреждений, тот же baseline подтверждён на HEAD. Не заявлять чистый lint всего проекта.
Ограничения: проверены HTTP-моки. Общая адаптивность вкладок Resources/Speed, подтверждение удаления и обработка ошибок существующего persistProbes не входили в этот блок; относятся к дальнейшей работе с формами и операциями. Этап 7 остаётся открытым. Квота: использовано 47% на старте, 81% перед фиксацией (недельной 74%). Новые блоки в этом окне не начинать; сохранить локальным коммитом без push.
**Следующий шаг — 7.3:** подключить разрешения действий серверов и мониторинга по can/hasPermission и действующим backend-маршрутам; проверить read-only, write/admin, auth disabled и серверный 403 без потери формы. Начать с серверов, затем мониторинг; UI-проверки не заменяют backend.
## Карта миграции (исходная инвентаризация этапа 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`