diff --git a/app/(main)/ospf/page.tsx b/app/(main)/ospf/page.tsx
index 8c75409a..71082590 100644
--- a/app/(main)/ospf/page.tsx
+++ b/app/(main)/ospf/page.tsx
@@ -15,7 +15,7 @@ import { DataPageToolbar } from "@/components/data-page-toolbar"
import { PageHeader } from "@/components/page-header"
import { DataPageCard } from "@/components/data-page-card"
import { OspfNeighborsDataGrid } from "@/components/data-grids/ospf-neighbors-data-grid"
-import { OspfRoutesDataGrid, routeTypeClass } from "@/components/data-grids/ospf-routes-data-grid"
+import { OspfRoutesDataGrid, OspfRouteTypeBadge } from "@/components/data-grids/ospf-routes-data-grid"
import { OspfBfdDataGrid } from "@/components/data-grids/ospf-bfd-data-grid"
import { Frame, FramePanel } from "@/components/reui/frame"
import { KpiStatGrid } from "@/components/reui-kit/kpi-stat-grid"
@@ -1026,9 +1026,7 @@ function RoutesTab({ routes, hasSearch }: { routes: OspfRoute[]; hasSearch: bool
{routes.some(r => r.dataUnknown) ? "Последний снимок: " : ""}{routes.length} маршрутов
{typeCount.map(([type, count]) => (
-
- {type === "Unknown" ? "Тип неизвестен" : type}: {count}
-
+
))}
@@ -1043,7 +1041,7 @@ function RoutesTab({ routes, hasSearch }: { routes: OspfRoute[]; hasSearch: bool
{ type: "O E2", desc: "External Type 2" },
] as const).map(({ type, desc }) => (
- {type}
+
{desc}
))}
diff --git a/components/data-grids/bgp-sessions-data-grid.tsx b/components/data-grids/bgp-sessions-data-grid.tsx
index 747e1c13..24d07894 100644
--- a/components/data-grids/bgp-sessions-data-grid.tsx
+++ b/components/data-grids/bgp-sessions-data-grid.tsx
@@ -8,6 +8,7 @@ import {
useTable,
} from "@tanstack/react-table"
import { cn } from "@/lib/utils"
+import { Badge } from "@/components/reui/badge"
import { DataGridShell } from "@/components/data-grids/shared/data-grid-shell"
import {
DATA_GRID_CELL_PAD,
@@ -22,83 +23,20 @@ import { BGP_AS_NAMES } from "@/lib/bgp/types"
import { fmtBgpNum } from "@/lib/bgp/helpers"
import { ChevronDownIcon, ChevronRightIcon, NetworkIcon } from "lucide-react"
-const STATE_STYLE: Record = {
- Unknown: { bg: "bg-muted", text: "text-muted-foreground", dot: "bg-muted-foreground", label: "Неизвестно" },
- Established: {
- bg: "bg-emerald-500/10",
- text: "text-emerald-600 dark:text-emerald-400",
- dot: "bg-emerald-500",
- label: "Established",
- },
- Active: {
- bg: "bg-amber-500/10",
- text: "text-amber-600 dark:text-amber-400",
- dot: "bg-amber-500",
- label: "Active",
- },
- Idle: {
- bg: "bg-slate-500/10",
- text: "text-slate-500 dark:text-slate-400",
- dot: "bg-slate-500",
- label: "Idle",
- },
- Connect: {
- bg: "bg-blue-500/10",
- text: "text-blue-600 dark:text-blue-400",
- dot: "bg-blue-500",
- label: "Connect",
- },
- OpenSent: {
- bg: "bg-violet-500/10",
- text: "text-violet-600 dark:text-violet-400",
- dot: "bg-violet-500",
- label: "OpenSent",
- },
- OpenConfirm: {
- bg: "bg-violet-500/10",
- text: "text-violet-600 dark:text-violet-400",
- dot: "bg-violet-500",
- label: "OpenConfirm",
- },
-}
-
-const TYPE_STYLE: Record = {
- Unknown: { bg: "bg-muted", text: "text-muted-foreground" },
- eBGP: { bg: "bg-blue-500/10", text: "text-blue-600 dark:text-blue-400" },
- iBGP: { bg: "bg-purple-500/10", text: "text-purple-600 dark:text-purple-400" },
+const STATE_DOT: Record = {
+ Unknown: "bg-muted-foreground", Established: "bg-success", Active: "bg-warning",
+ Idle: "bg-muted-foreground", Connect: "bg-info", OpenSent: "bg-info", OpenConfirm: "bg-info",
}
function StateBadge({ state }: { state: BgpState }) {
- const s = STATE_STYLE[state]
- return (
-
-
- {s.label}
-
- )
+ return
+
+ {state === "Unknown" ? "Неизвестно" : state}
+
}
function TypeBadge({ type }: { type: BgpType }) {
- const s = TYPE_STYLE[type]
- return (
-
- {type === "Unknown" ? "Тип неизвестен" : type}
-
- )
+ return {type === "Unknown" ? "Тип неизвестен" : type}
}
interface BgpSessionsDataGridProps {
@@ -227,7 +165,7 @@ function BgpSessionsDataGrid({ sessions, emptyTitle, selectedSessionId, onSelect
cell: ({ row }) => {
const n = row.original.prefixesRx
return n !== null ? (
-
+
{fmtBgpNum(n)}
) : (
diff --git a/components/data-grids/ospf-routes-data-grid.tsx b/components/data-grids/ospf-routes-data-grid.tsx
index febc98ea..c5cda34c 100644
--- a/components/data-grids/ospf-routes-data-grid.tsx
+++ b/components/data-grids/ospf-routes-data-grid.tsx
@@ -8,6 +8,7 @@ import {
useTable,
} from "@tanstack/react-table"
import { cn } from "@/lib/utils"
+import { Badge } from "@/components/reui/badge"
import { DataGridShell } from "@/components/data-grids/shared/data-grid-shell"
import {
DATA_GRID_CELL_PAD,
@@ -31,12 +32,10 @@ export interface OspfRouteRow {
area: string
}
-function routeTypeClass(type: OspfRouteRow["type"]) {
- if (type === "Unknown") return "bg-muted text-muted-foreground border-border"
- if (type === "O") return "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/25"
- if (type === "O IA") return "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/25"
- if (type === "O E1") return "bg-purple-500/10 text-purple-600 dark:text-purple-400 border-purple-500/25"
- return "bg-orange-500/10 text-orange-600 dark:text-orange-400 border-orange-500/25"
+function OspfRouteTypeBadge({ type, count }: { type: OspfRouteRow["type"]; count?: number }) {
+ return
+ {type === "Unknown" ? "Тип неизвестен" : type}{count === undefined ? "" : ": " + count}
+
}
interface OspfRoutesDataGridProps {
@@ -67,14 +66,7 @@ function OspfRoutesDataGrid({ routes, emptyTitle }: OspfRoutesDataGridProps) {
accessorKey: "type",
header: ({ column }) => ,
cell: ({ row }) => (
-
- {row.original.type === "Unknown" ? "Тип неизвестен" : row.original.type}
-
+
),
meta: { headerTitle: "Тип", headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD },
},
@@ -189,4 +181,4 @@ function OspfRoutesDataGrid({ routes, emptyTitle }: OspfRoutesDataGridProps) {
)
}
-export { OspfRoutesDataGrid, type OspfRoutesDataGridProps, routeTypeClass }
+export { OspfRoutesDataGrid, type OspfRoutesDataGridProps, OspfRouteTypeBadge }
diff --git a/docs/ux-ui-external-acceptance.md b/docs/ux-ui-external-acceptance.md
index e1fa7e12..c783867b 100644
--- a/docs/ux-ui-external-acceptance.md
+++ b/docs/ux-ui-external-acceptance.md
@@ -1,6 +1,6 @@
# UX/UI: протокол внешней приёмки 12.5
-Актуальное продолжение 2026-10-14: блок217 завершён. Legacy WAN runtime больше не скрывает ошибки DHCP/addresses/routes и не опрашивает выключенные Home Router/Exit Node. Empty success сохранён. Production15, профиль snapshot/build, live API2/native6/anonymous2/dashboard smoke1440/320 прошли. Backend ux-wan217/compiled1052/source484 matched, frontend ux-gre214; own DB/roles удалены, пароли не менялись. Следующий218 — доступность заполненных таблиц маршрутизации. Этап12.5 открыт.
+Актуальное продолжение 2026-10-14: блок218 завершён. Заполненные BGP state/type/prefix RX и OSPF route type переведены на читаемые ReUI Badge/основной цвет чисел. Baseline22 violations; fixed104 scoped cells0, E2E22, TypeScript/ESLint/build прошли. Own GRE/OSPF/BGP поднялись, UI8 прошли, own objects удалены и static projection10×2 совпал. Frontend ux-routing218/source527 matched, backend ux-wan217. Следующий219 — скрытые ошибки GRE source snapshots для оповещений. Этап12.5 открыт.
IPsec78: реальный PSK responder established, mode-config198.18.78.10 и online=true подтверждены API; точные собственные объекты удалены. Сертификатный/DN online, late failure и history restore остаются открытыми. [Отчёт](ux-ui/acceptance/ipsec-mode-config-2026-10-09.json), [журнал](ux-ui-progress.md).
@@ -2595,3 +2595,7 @@ Production compiled SQL/API29 на собственной ограниченно
## 12.5.217 — WAN runtime (2026-10-14)
Read failures/malformed collections дают502, выключенные устройства409 без native poll, empty success сохранён. Production15 и реальная сверка API/native прошли. Подробности и границы — [журнал](ux-ui-progress.md). Backend ux-wan217, frontend ux-gre214; следующий218 — routing table accessibility.
+
+## 12.5.218 — заполненные routing tables (2026-10-14)
+
+BGP/OSPF contrast: baseline22/fixed104 cells0 violations; E2E22 и real UI8 passed. Own GRE/OSPF/BGP objects удалены; статическая сверка10×2 прошла. Подробности и границы — [журнал](ux-ui-progress.md). Frontend ux-routing218, backend ux-wan217; следующий219 — GRE snapshot source errors.
diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md
index 992b7d76..76ba8be1 100644
--- a/docs/ux-ui-plan.md
+++ b/docs/ux-ui-plan.md
@@ -1,6 +1,6 @@
# План реализации UX/UI по пятичасовым окнам
-Актуальное продолжение 2026-10-14: блок217 завершён. Legacy WAN runtime больше не скрывает ошибки DHCP/addresses/routes и не опрашивает выключенные Home Router/Exit Node. Empty success сохранён. Production15, профиль snapshot/build, live API2/native6/anonymous2/dashboard smoke1440/320 прошли. Backend ux-wan217/compiled1052/source484 matched, frontend ux-gre214; own DB/roles удалены, пароли не менялись. Следующий218 — доступность заполненных таблиц маршрутизации. Этап12.5 открыт.
+Актуальное продолжение 2026-10-14: блок218 завершён. Заполненные BGP state/type/prefix RX и OSPF route type переведены на читаемые ReUI Badge/основной цвет чисел. Baseline22 violations; fixed104 scoped cells0, E2E22, TypeScript/ESLint/build прошли. Own GRE/OSPF/BGP поднялись, UI8 прошли, own objects удалены и static projection10×2 совпал. Frontend ux-routing218/source527 matched, backend ux-wan217. Следующий219 — скрытые ошибки GRE source snapshots для оповещений. Этап12.5 открыт.
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-13. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md
index f72ac053..a51a2910 100644
--- a/docs/ux-ui-progress.md
+++ b/docs/ux-ui-progress.md
@@ -1,6 +1,6 @@
# UX/UI: журнал реализации
-Актуальное продолжение 2026-10-14: блок217 завершён. Legacy WAN runtime больше не скрывает ошибки DHCP/addresses/routes и не опрашивает выключенные Home Router/Exit Node. Empty success сохранён. Production15, профиль snapshot/build, live API2/native6/anonymous2/dashboard smoke1440/320 прошли. Backend ux-wan217/compiled1052/source484 matched, frontend ux-gre214; own DB/roles удалены, пароли не менялись. Следующий218 — доступность заполненных таблиц маршрутизации. Этап12.5 открыт.
+Актуальное продолжение 2026-10-14: блок218 завершён. Заполненные BGP state/type/prefix RX и OSPF route type переведены на читаемые ReUI Badge/основной цвет чисел. Baseline22 violations; fixed104 scoped cells0, E2E22, TypeScript/ESLint/build прошли. Own GRE/OSPF/BGP поднялись, UI8 прошли, own objects удалены и static projection10×2 совпал. Frontend ux-routing218/source527 matched, backend ux-wan217. Следующий219 — скрытые ошибки GRE source snapshots для оповещений. Этап12.5 открыт.
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
@@ -38,6 +38,18 @@
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–196 и ограничения ниже. Runtime backend/frontend ux-peak196. |
+## 12.5.218 — контраст заполненных BGP и OSPF таблиц (2026-10-14)
+
+Baseline104 explicitly scrolled cell checks:22 нарушений в light на1440/320. BGP Established3,31:1, Active2,96:1, Idle4,21:1, prefix RX3,65:1; OSPF O E2 3,23:1. Ручные state/type spans заменены штатным ReUI Badge invert-light; BGP состояния дополнительно различаются семантическим индикатором и прежним текстом. Protocol type — текстовая категория без оценки здоровья. Prefix RX читает основной цвет. OSPF type Badge общий для строк, counts и легенды; vendor components/theme не менялись.
+
+Fixed104 scoped cells0 violations, E2E22 (8 contrast fixtures + BGP data13 + OSPF values1), ESLint0, TypeScript и production build прошли. Первый локальный tsc исчерпал4GB heap: include захватывал игнорируемые tmp source copies. tmp/temp/test-results/playwright-report исключены из TypeScript, исходники и тесты остались включены; повтор прошёл без увеличения heap. Это scoped colour audit, не полная WCAG приёмка.
+
+На Debian свободное место упало до157MB. Удалён неиспользуемый build-cache старше15m и51 старый неиспользуемый тег mmapp-test/frontend:ux-* без force, с защитой image IDs всех контейнеров. ux-gre214 и предыдущий ux-gre213 сохранены; свободно5,5GB перед build. Backend/Portal/Postgres/volumes не удалялись. Frontend ux-routing218/source527 matched, backend ux-wan217 healthy сохранён.
+
+Созданы собственные GRE2, OSPF instances/areas/templates и BGP instances/connections со scoped prefix filters: OSPF Full, BGP Established с received prefixes и два own remote routes active на каждом CHR. Восемь live UI BGP/OSPF ×light/dark ×1440/320 проверили16 scrolled cells без violations/page errors/mutation requests. Все собственные объекты удалены; hash projection перечисленных статических полей10 collections×2 совпал с baseline, dynamic state исключён. Пароли не менялись. Packet-loss SLA/полный протоколный regression этим UI-блоком не принимаются.
+
+Протоколы: [contrast](ux-ui/acceptance/routing218-contrast-2026-10-14.json), [source](ux-ui/acceptance/routing218-source-proof-2026-10-14.json), [deployment](ux-ui/acceptance/routing218-deployment-2026-10-14.json), [storage cleanup](ux-ui/acceptance/routing218-storage-cleanup.json), [native/UI/cleanup](ux-ui/acceptance/routing218-native-live-2026-10-14.json). Следующий219 — GRE источник snapshots: сейчас fetchGreTunnelLiveRows подавляет ошибки устройств и сообщает успешный пустой результат.
+
## 12.5.217 — достоверный WAN runtime и disabled no-poll (2026-10-14)
/api/servers/:id/wan-runtime подавлял ошибки DHCP, addresses и routes через [] и опрашивал выключенные устройства. Добавлены409 перед native client (с повторной проверкой актуальной DB row) и строгая валидация трёх коллекций тем же readInternetPathRows, что используется Internet path collector. Read failure/malformed row возвращает502 с WAN error; успешный envelope defaultGateway/immediateGateway/defaultInterface/uplinks и корректный empty200 сохранены. Методы/права/алгоритм выбора default route не менялись. Активный dashboard читает сохранённый Internet path, поэтому legacy WAN endpoint не объявляется его единственным источником.
diff --git a/docs/ux-ui-remaining-acceptance.md b/docs/ux-ui-remaining-acceptance.md
index 26029bf5..5ec89819 100644
--- a/docs/ux-ui-remaining-acceptance.md
+++ b/docs/ux-ui-remaining-acceptance.md
@@ -1,10 +1,10 @@
# Оставшаяся внешняя приёмка — 2026-10-13
-Актуальное продолжение 2026-10-14: блок217 завершён. Legacy WAN runtime больше не скрывает ошибки DHCP/addresses/routes и не опрашивает выключенные Home Router/Exit Node. Empty success сохранён. Production15, профиль snapshot/build, live API2/native6/anonymous2/dashboard smoke1440/320 прошли. Backend ux-wan217/compiled1052/source484 matched, frontend ux-gre214; own DB/roles удалены, пароли не менялись. Следующий218 — доступность заполненных таблиц маршрутизации. Этап12.5 открыт.
+Актуальное продолжение 2026-10-14: блок218 завершён. Заполненные BGP state/type/prefix RX и OSPF route type переведены на читаемые ReUI Badge/основной цвет чисел. Baseline22 violations; fixed104 scoped cells0, E2E22, TypeScript/ESLint/build прошли. Own GRE/OSPF/BGP поднялись, UI8 прошли, own objects удалены и static projection10×2 совпал. Frontend ux-routing218/source527 matched, backend ux-wan217. Следующий219 — скрытые ошибки GRE source snapshots для оповещений. Этап12.5 открыт.
-Это актуальная матрица после 12.5.217. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
+Это актуальная матрица после 12.5.218. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
-Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-wan217`, frontend `ux-gre214`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
+Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-wan217`, frontend `ux-routing218`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
| Раздел | Подтверждено | Осталось / условие завершения |
| --- | --- | --- |
@@ -1513,3 +1513,7 @@ Production compiled SQL/API29 на собственной ограниченно
## 12.5.217 — WAN runtime (2026-10-14)
Read failures/malformed collections дают502, выключенные устройства409 без native poll, empty success сохранён. Production15 и реальная сверка API/native прошли. Подробности и границы — [журнал](ux-ui-progress.md). Backend ux-wan217, frontend ux-gre214; следующий218 — routing table accessibility.
+
+## 12.5.218 — заполненные routing tables (2026-10-14)
+
+BGP/OSPF contrast: baseline22/fixed104 cells0 violations; E2E22 и real UI8 passed. Own GRE/OSPF/BGP objects удалены; статическая сверка10×2 прошла. Подробности и границы — [журнал](ux-ui-progress.md). Frontend ux-routing218, backend ux-wan217; следующий219 — GRE snapshot source errors.
diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md
index dfb14228..264b4e02 100644
--- a/docs/ux-ui-stage-12.md
+++ b/docs/ux-ui-stage-12.md
@@ -1,6 +1,6 @@
# Этап 12: итоговая приёмка UX/UI
-Актуальное продолжение 2026-10-14: блок217 завершён. Legacy WAN runtime больше не скрывает ошибки DHCP/addresses/routes и не опрашивает выключенные Home Router/Exit Node. Empty success сохранён. Production15, профиль snapshot/build, live API2/native6/anonymous2/dashboard smoke1440/320 прошли. Backend ux-wan217/compiled1052/source484 matched, frontend ux-gre214; own DB/roles удалены, пароли не менялись. Следующий218 — доступность заполненных таблиц маршрутизации. Этап12.5 открыт.
+Актуальное продолжение 2026-10-14: блок218 завершён. Заполненные BGP state/type/prefix RX и OSPF route type переведены на читаемые ReUI Badge/основной цвет чисел. Baseline22 violations; fixed104 scoped cells0, E2E22, TypeScript/ESLint/build прошли. Own GRE/OSPF/BGP поднялись, UI8 прошли, own objects удалены и static projection10×2 совпал. Frontend ux-routing218/source527 matched, backend ux-wan217. Следующий219 — скрытые ошибки GRE source snapshots для оповещений. Этап12.5 открыт.
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
@@ -3954,3 +3954,16 @@ Compiled1052/source484 matched; backend healthy, frontend ux-gre214 сохран
Протоколы: [baseline](ux-ui/acceptance/wan217-baseline.json), [fixed](ux-ui/acceptance/wan217-fixed.json), [build](ux-ui/acceptance/wan217-build-proof-2026-10-14.json), [deployment](ux-ui/acceptance/wan217-deployment-2026-10-14.json), [live](ux-ui/acceptance/wan217-live-2026-10-14.json). Следующий218 — accessibility заполненных таблиц маршрутизации: контраст статусов и метрик в light/dark на ПК/телефоне.
+
+## 12.5.218 — контраст заполненных BGP и OSPF таблиц (2026-10-14)
+
+Baseline104 explicitly scrolled cell checks:22 нарушений в light на1440/320. BGP Established3,31:1, Active2,96:1, Idle4,21:1, prefix RX3,65:1; OSPF O E2 3,23:1. Ручные state/type spans заменены штатным ReUI Badge invert-light; BGP состояния дополнительно различаются семантическим индикатором и прежним текстом. Protocol type — текстовая категория без оценки здоровья. Prefix RX читает основной цвет. OSPF type Badge общий для строк, counts и легенды; vendor components/theme не менялись.
+
+Fixed104 scoped cells0 violations, E2E22 (8 contrast fixtures + BGP data13 + OSPF values1), ESLint0, TypeScript и production build прошли. Первый локальный tsc исчерпал4GB heap: include захватывал игнорируемые tmp source copies. tmp/temp/test-results/playwright-report исключены из TypeScript, исходники и тесты остались включены; повтор прошёл без увеличения heap. Это scoped colour audit, не полная WCAG приёмка.
+
+На Debian свободное место упало до157MB. Удалён неиспользуемый build-cache старше15m и51 старый неиспользуемый тег mmapp-test/frontend:ux-* без force, с защитой image IDs всех контейнеров. ux-gre214 и предыдущий ux-gre213 сохранены; свободно5,5GB перед build. Backend/Portal/Postgres/volumes не удалялись. Frontend ux-routing218/source527 matched, backend ux-wan217 healthy сохранён.
+
+Созданы собственные GRE2, OSPF instances/areas/templates и BGP instances/connections со scoped prefix filters: OSPF Full, BGP Established с received prefixes и два own remote routes active на каждом CHR. Восемь live UI BGP/OSPF ×light/dark ×1440/320 проверили16 scrolled cells без violations/page errors/mutation requests. Все собственные объекты удалены; hash projection перечисленных статических полей10 collections×2 совпал с baseline, dynamic state исключён. Пароли не менялись. Packet-loss SLA/полный протоколный regression этим UI-блоком не принимаются.
+
+Протоколы: [contrast](ux-ui/acceptance/routing218-contrast-2026-10-14.json), [source](ux-ui/acceptance/routing218-source-proof-2026-10-14.json), [deployment](ux-ui/acceptance/routing218-deployment-2026-10-14.json), [storage cleanup](ux-ui/acceptance/routing218-storage-cleanup.json), [native/UI/cleanup](ux-ui/acceptance/routing218-native-live-2026-10-14.json). Следующий219 — GRE источник snapshots: сейчас fetchGreTunnelLiveRows подавляет ошибки устройств и сообщает успешный пустой результат.
+
diff --git a/docs/ux-ui-test-server.md b/docs/ux-ui-test-server.md
index 068ea261..5a512b8c 100644
--- a/docs/ux-ui-test-server.md
+++ b/docs/ux-ui-test-server.md
@@ -1,6 +1,6 @@
# Тестовый сервер UX/UI — 2026-10-10
-Актуальное продолжение 2026-10-14: блок217 завершён. Legacy WAN runtime больше не скрывает ошибки DHCP/addresses/routes и не опрашивает выключенные Home Router/Exit Node. Empty success сохранён. Production15, профиль snapshot/build, live API2/native6/anonymous2/dashboard smoke1440/320 прошли. Backend ux-wan217/compiled1052/source484 matched, frontend ux-gre214; own DB/roles удалены, пароли не менялись. Следующий218 — доступность заполненных таблиц маршрутизации. Этап12.5 открыт.
+Актуальное продолжение 2026-10-14: блок218 завершён. Заполненные BGP state/type/prefix RX и OSPF route type переведены на читаемые ReUI Badge/основной цвет чисел. Baseline22 violations; fixed104 scoped cells0, E2E22, TypeScript/ESLint/build прошли. Own GRE/OSPF/BGP поднялись, UI8 прошли, own objects удалены и static projection10×2 совпал. Frontend ux-routing218/source527 matched, backend ux-wan217. Следующий219 — скрытые ошибки GRE source snapshots для оповещений. Этап12.5 открыт.
| Параметр | Проверенное состояние |
| --- | --- |
@@ -2326,3 +2326,7 @@ Production compiled SQL/API29 на собственной ограниченно
## 12.5.217 — WAN runtime (2026-10-14)
Read failures/malformed collections дают502, выключенные устройства409 без native poll, empty success сохранён. Production15 и реальная сверка API/native прошли. Подробности и границы — [журнал](ux-ui-progress.md). Backend ux-wan217, frontend ux-gre214; следующий218 — routing table accessibility.
+
+## 12.5.218 — заполненные routing tables (2026-10-14)
+
+BGP/OSPF contrast: baseline22/fixed104 cells0 violations; E2E22 и real UI8 passed. Own GRE/OSPF/BGP objects удалены; статическая сверка10×2 прошла. Подробности и границы — [журнал](ux-ui-progress.md). Frontend ux-routing218, backend ux-wan217; следующий219 — GRE snapshot source errors.
diff --git a/docs/ux-ui/acceptance/routing218-contrast-2026-10-14.json b/docs/ux-ui/acceptance/routing218-contrast-2026-10-14.json
new file mode 100644
index 00000000..3ef22d2e
--- /dev/null
+++ b/docs/ux-ui/acceptance/routing218-contrast-2026-10-14.json
@@ -0,0 +1,988 @@
+{
+ "date": "2026-10-14",
+ "block": "12.5.218",
+ "baseline": {
+ "cases": 8,
+ "cellChecks": 104,
+ "failedCells": 22,
+ "casesDetail": [
+ {
+ "date": "2026-10-14",
+ "section": "bgp",
+ "theme": "light",
+ "width": 1440,
+ "scope": "Explicitly scrolled BGP type/state/prefix RX and OSPF route type cells",
+ "findings": [
+ {
+ "row": 0,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 0,
+ "cell": 5,
+ "text": "Established",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ ".bg-emerald-500\\/10"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.31 (foreground color: #009966, background color: #e6f8f2, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 0,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 1,
+ "cell": 4,
+ "text": "iBGP",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 5,
+ "text": "Active",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ ".text-amber-600"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 2.96 (foreground color: #e17100, background color: #fff5e6, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 1,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 2,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 5,
+ "text": "Idle",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ ".bg-slate-500\\/10"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 4.21 (foreground color: #62748e, background color: #eff1f4, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 2,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 3,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 5,
+ "text": "Connect",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 4,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 5,
+ "text": "OpenSent",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 5,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 5,
+ "cell": 5,
+ "text": "OpenConfirm",
+ "violations": []
+ },
+ {
+ "row": 5,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 6,
+ "cell": 4,
+ "text": "Тип неизвестен",
+ "violations": []
+ },
+ {
+ "row": 6,
+ "cell": 5,
+ "text": "Неизвестно",
+ "violations": []
+ },
+ {
+ "row": 6,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "date": "2026-10-14",
+ "section": "bgp",
+ "theme": "light",
+ "width": 320,
+ "scope": "Explicitly scrolled BGP type/state/prefix RX and OSPF route type cells",
+ "findings": [
+ {
+ "row": 0,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 0,
+ "cell": 5,
+ "text": "Established",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ ".bg-emerald-500\\/10"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.31 (foreground color: #009966, background color: #e6f8f2, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 0,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 1,
+ "cell": 4,
+ "text": "iBGP",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 5,
+ "text": "Active",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ ".text-amber-600"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 2.96 (foreground color: #e17100, background color: #fff5e6, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 1,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 2,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 5,
+ "text": "Idle",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ ".bg-slate-500\\/10"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 4.21 (foreground color: #62748e, background color: #eff1f4, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 2,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 3,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 5,
+ "text": "Connect",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 4,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 5,
+ "text": "OpenSent",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 5,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 5,
+ "cell": 5,
+ "text": "OpenConfirm",
+ "violations": []
+ },
+ {
+ "row": 5,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 6,
+ "cell": 4,
+ "text": "Тип неизвестен",
+ "violations": []
+ },
+ {
+ "row": 6,
+ "cell": 5,
+ "text": "Неизвестно",
+ "violations": []
+ },
+ {
+ "row": 6,
+ "cell": 7,
+ "text": "12",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .text-emerald-600.dark\\:text-emerald-400.text-right"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.65 (foreground color: #009966, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "date": "2026-10-14",
+ "section": "bgp",
+ "theme": "dark",
+ "width": 1440,
+ "scope": "Explicitly scrolled BGP type/state/prefix RX and OSPF route type cells",
+ "findings": [
+ {
+ "row": 0,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 0,
+ "cell": 5,
+ "text": "Established",
+ "violations": []
+ },
+ {
+ "row": 0,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 4,
+ "text": "iBGP",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 5,
+ "text": "Active",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 5,
+ "text": "Idle",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 5,
+ "text": "Connect",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 5,
+ "text": "OpenSent",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 5,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 5,
+ "cell": 5,
+ "text": "OpenConfirm",
+ "violations": []
+ },
+ {
+ "row": 5,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 6,
+ "cell": 4,
+ "text": "Тип неизвестен",
+ "violations": []
+ },
+ {
+ "row": 6,
+ "cell": 5,
+ "text": "Неизвестно",
+ "violations": []
+ },
+ {
+ "row": 6,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ }
+ ]
+ },
+ {
+ "date": "2026-10-14",
+ "section": "bgp",
+ "theme": "dark",
+ "width": 320,
+ "scope": "Explicitly scrolled BGP type/state/prefix RX and OSPF route type cells",
+ "findings": [
+ {
+ "row": 0,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 0,
+ "cell": 5,
+ "text": "Established",
+ "violations": []
+ },
+ {
+ "row": 0,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 4,
+ "text": "iBGP",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 5,
+ "text": "Active",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 5,
+ "text": "Idle",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 5,
+ "text": "Connect",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 5,
+ "text": "OpenSent",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 5,
+ "cell": 4,
+ "text": "eBGP",
+ "violations": []
+ },
+ {
+ "row": 5,
+ "cell": 5,
+ "text": "OpenConfirm",
+ "violations": []
+ },
+ {
+ "row": 5,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ },
+ {
+ "row": 6,
+ "cell": 4,
+ "text": "Тип неизвестен",
+ "violations": []
+ },
+ {
+ "row": 6,
+ "cell": 5,
+ "text": "Неизвестно",
+ "violations": []
+ },
+ {
+ "row": 6,
+ "cell": 7,
+ "text": "12",
+ "violations": []
+ }
+ ]
+ },
+ {
+ "date": "2026-10-14",
+ "section": "ospf",
+ "theme": "light",
+ "width": 1440,
+ "scope": "Explicitly scrolled BGP type/state/prefix RX and OSPF route type cells",
+ "findings": [
+ {
+ "row": 0,
+ "cell": 1,
+ "text": "O",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 1,
+ "text": "O IA",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 1,
+ "text": "O E1",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 1,
+ "text": "O E2",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .bg-orange-500\\/10.text-orange-600.dark\\:text-orange-400"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.23 (foreground color: #f54900, background color: #fff0e6, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 4,
+ "cell": 1,
+ "text": "Тип неизвестен",
+ "violations": []
+ }
+ ]
+ },
+ {
+ "date": "2026-10-14",
+ "section": "ospf",
+ "theme": "light",
+ "width": 320,
+ "scope": "Explicitly scrolled BGP type/state/prefix RX and OSPF route type cells",
+ "findings": [
+ {
+ "row": 0,
+ "cell": 1,
+ "text": "O",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 1,
+ "text": "O IA",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 1,
+ "text": "O E1",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 1,
+ "text": "O E2",
+ "violations": [
+ {
+ "id": "color-contrast",
+ "nodes": [
+ {
+ "target": [
+ "#own218-audit > .bg-orange-500\\/10.text-orange-600.dark\\:text-orange-400"
+ ],
+ "summary": "Fix any of the following:\n Element has insufficient color contrast of 3.23 (foreground color: #f54900, background color: #fff0e6, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "row": 4,
+ "cell": 1,
+ "text": "Тип неизвестен",
+ "violations": []
+ }
+ ]
+ },
+ {
+ "date": "2026-10-14",
+ "section": "ospf",
+ "theme": "dark",
+ "width": 1440,
+ "scope": "Explicitly scrolled BGP type/state/prefix RX and OSPF route type cells",
+ "findings": [
+ {
+ "row": 0,
+ "cell": 1,
+ "text": "O",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 1,
+ "text": "O IA",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 1,
+ "text": "O E1",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 1,
+ "text": "O E2",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 1,
+ "text": "Тип неизвестен",
+ "violations": []
+ }
+ ]
+ },
+ {
+ "date": "2026-10-14",
+ "section": "ospf",
+ "theme": "dark",
+ "width": 320,
+ "scope": "Explicitly scrolled BGP type/state/prefix RX and OSPF route type cells",
+ "findings": [
+ {
+ "row": 0,
+ "cell": 1,
+ "text": "O",
+ "violations": []
+ },
+ {
+ "row": 1,
+ "cell": 1,
+ "text": "O IA",
+ "violations": []
+ },
+ {
+ "row": 2,
+ "cell": 1,
+ "text": "O E1",
+ "violations": []
+ },
+ {
+ "row": 3,
+ "cell": 1,
+ "text": "O E2",
+ "violations": []
+ },
+ {
+ "row": 4,
+ "cell": 1,
+ "text": "Тип неизвестен",
+ "violations": []
+ }
+ ]
+ }
+ ]
+ },
+ "fixed": {
+ "cases": 8,
+ "cellChecks": 104,
+ "failedCells": 0
+ },
+ "regression": {
+ "startTime": "2026-10-13T17:51:31.476Z",
+ "duration": 63706.111,
+ "expected": 22,
+ "skipped": 0,
+ "unexpected": 0,
+ "flaky": 0
+ },
+ "scope": "Scrolled cells only: BGP type/state/prefixRX and OSPF type. No blanket WCAG certification.",
+ "typeScriptPassed": true,
+ "eslintErrors": 0,
+ "typeScriptInitialFailure": "Local default include compiled ignored temporary source copies; exhausted4GB heap. Generated directories now excluded."
+}
diff --git a/docs/ux-ui/acceptance/routing218-deployment-2026-10-14.json b/docs/ux-ui/acceptance/routing218-deployment-2026-10-14.json
new file mode 100644
index 00000000..2d049f8b
--- /dev/null
+++ b/docs/ux-ui/acceptance/routing218-deployment-2026-10-14.json
@@ -0,0 +1,20 @@
+{
+ "date": "2026-10-14",
+ "block": "12.5.218",
+ "frontendSourceMatched": 527,
+ "backendChanged": false,
+ "services": {
+ "backend": {
+ "image": "mmapp-test/backend:ux-wan217",
+ "imageId": "sha256:f5fac6167453e180e7aacb3b88de14e49860479dfcf46bc7a9ffd34db682e42b",
+ "running": true,
+ "health": "healthy"
+ },
+ "frontend": {
+ "image": "mmapp-test/frontend:ux-routing218",
+ "imageId": "sha256:ec658b9da88c19d60987be5bfc0c15fb339a0310d7a42dc761b40a70a3428c5a",
+ "running": true,
+ "health": null
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/ux-ui/acceptance/routing218-native-live-2026-10-14.json b/docs/ux-ui/acceptance/routing218-native-live-2026-10-14.json
new file mode 100644
index 00000000..15ae1657
--- /dev/null
+++ b/docs/ux-ui/acceptance/routing218-native-live-2026-10-14.json
@@ -0,0 +1,127 @@
+{
+ "date": "2026-10-14",
+ "block": "12.5.218",
+ "passed": true,
+ "backend": "ux-wan217",
+ "frontend": "ux-routing218",
+ "devices": [
+ {
+ "host": "194.226.121.150",
+ "serverId": 1,
+ "greOwned": true,
+ "bgpEstablished": true,
+ "bgpPrefixesRx": 1,
+ "ospfFull": true,
+ "remotePrefixesActive": 2
+ },
+ {
+ "host": "104.165.244.7",
+ "serverId": 2,
+ "greOwned": true,
+ "bgpEstablished": true,
+ "bgpPrefixesRx": 1,
+ "ospfFull": true,
+ "remotePrefixesActive": 2
+ }
+ ],
+ "ui": [
+ {
+ "section": "bgp",
+ "theme": "light",
+ "width": 1440,
+ "scrolledCellChecks": 3,
+ "contrastViolations": 0,
+ "nativeData": true,
+ "pageErrors": 0,
+ "mutationRequests": 0
+ },
+ {
+ "section": "bgp",
+ "theme": "light",
+ "width": 320,
+ "scrolledCellChecks": 3,
+ "contrastViolations": 0,
+ "nativeData": true,
+ "pageErrors": 0,
+ "mutationRequests": 0
+ },
+ {
+ "section": "bgp",
+ "theme": "dark",
+ "width": 1440,
+ "scrolledCellChecks": 3,
+ "contrastViolations": 0,
+ "nativeData": true,
+ "pageErrors": 0,
+ "mutationRequests": 0
+ },
+ {
+ "section": "bgp",
+ "theme": "dark",
+ "width": 320,
+ "scrolledCellChecks": 3,
+ "contrastViolations": 0,
+ "nativeData": true,
+ "pageErrors": 0,
+ "mutationRequests": 0
+ },
+ {
+ "section": "ospf",
+ "theme": "light",
+ "width": 1440,
+ "scrolledCellChecks": 1,
+ "contrastViolations": 0,
+ "nativeData": true,
+ "pageErrors": 0,
+ "mutationRequests": 0
+ },
+ {
+ "section": "ospf",
+ "theme": "light",
+ "width": 320,
+ "scrolledCellChecks": 1,
+ "contrastViolations": 0,
+ "nativeData": true,
+ "pageErrors": 0,
+ "mutationRequests": 0
+ },
+ {
+ "section": "ospf",
+ "theme": "dark",
+ "width": 1440,
+ "scrolledCellChecks": 1,
+ "contrastViolations": 0,
+ "nativeData": true,
+ "pageErrors": 0,
+ "mutationRequests": 0
+ },
+ {
+ "section": "ospf",
+ "theme": "dark",
+ "width": 320,
+ "scrolledCellChecks": 1,
+ "contrastViolations": 0,
+ "nativeData": true,
+ "pageErrors": 0,
+ "mutationRequests": 0
+ }
+ ],
+ "cleanup": [
+ {
+ "host": "194.226.121.150",
+ "ownedObjectsRemoved": 15,
+ "collectionsMatched": 10,
+ "scope": "Listed static configuration fields; dynamic state excluded"
+ },
+ {
+ "host": "104.165.244.7",
+ "ownedObjectsRemoved": 15,
+ "collectionsMatched": 10,
+ "scope": "Listed static configuration fields; dynamic state excluded"
+ }
+ ],
+ "realPortalJWT": true,
+ "apiMocks": false,
+ "passwordsUnchanged": true,
+ "scope": "Owned GRE/BGP/OSPF convergence and filled-table scoped contrast; no packet-loss SLA or complete WCAG claim"
+}
diff --git a/docs/ux-ui/acceptance/routing218-source-proof-2026-10-14.json b/docs/ux-ui/acceptance/routing218-source-proof-2026-10-14.json
new file mode 100644
index 00000000..c60cd4e1
--- /dev/null
+++ b/docs/ux-ui/acceptance/routing218-source-proof-2026-10-14.json
@@ -0,0 +1,10 @@
+{
+ "date": "2026-10-14",
+ "block": "12.5.218",
+ "kinds": [
+ {
+ "kind": "frontend",
+ "matched": 527
+ }
+ ]
+}
diff --git a/docs/ux-ui/acceptance/routing218-storage-cleanup.json b/docs/ux-ui/acceptance/routing218-storage-cleanup.json
new file mode 100644
index 00000000..cacb5bfd
--- /dev/null
+++ b/docs/ux-ui/acceptance/routing218-storage-cleanup.json
@@ -0,0 +1,63 @@
+{
+ "date": "2026-10-14",
+ "repository": "mmapp-test/frontend",
+ "preservedTags": [
+ "mmapp-test/frontend:ux-gre213",
+ "mmapp-test/frontend:ux-gre214"
+ ],
+ "inUseImagesProtected": true,
+ "force": false,
+ "removedTags": [
+ "mmapp-test/frontend:ux-gre212",
+ "mmapp-test/frontend:ux-gre210",
+ "mmapp-test/frontend:ux-gre209",
+ "mmapp-test/frontend:ux-native208",
+ "mmapp-test/frontend:ux-history207",
+ "mmapp-test/frontend:ux-accessibility205",
+ "mmapp-test/frontend:ux-portal199",
+ "mmapp-test/frontend:ux-peak196",
+ "mmapp-test/frontend:ux-peak191",
+ "mmapp-test/frontend:ux-warning183",
+ "mmapp-test/frontend:ux-status181",
+ "mmapp-test/frontend:ux-volume180",
+ "mmapp-test/frontend:ux-live179",
+ "mmapp-test/frontend:ux-history178",
+ "mmapp-test/frontend:ux-ping173",
+ "mmapp-test/frontend:ux-targets171",
+ "mmapp-test/frontend:ux-bfd158",
+ "mmapp-test/frontend:ux-ospf153",
+ "mmapp-test/frontend:ux-wg152",
+ "mmapp-test/frontend:ux-gre151",
+ "mmapp-test/frontend:ux-sessions150",
+ "mmapp-test/frontend:ux-scope148",
+ "mmapp-test/frontend:ux-http147",
+ "mmapp-test/frontend:ux-labels146",
+ "mmapp-test/frontend:ux-traffic141",
+ "mmapp-test/frontend:ux-log133",
+ "mmapp-test/frontend:ux-ike124",
+ "mmapp-test/frontend:ux-bound116",
+ "mmapp-test/frontend:ux-keepcert115",
+ "mmapp-test/frontend:ux-alias112-version",
+ "mmapp-test/frontend:ux-alias112",
+ "mmapp-test/frontend:ux-a11y101",
+ "mmapp-test/frontend:ux-ospf91",
+ "mmapp-test/frontend:ux-optimizer87",
+ "mmapp-test/frontend:ux-ipsec77",
+ "mmapp-test/frontend:ux-vxlan76",
+ "mmapp-test/frontend:ux-479f2ad",
+ "mmapp-test/frontend:ux-c16fc0c",
+ "mmapp-test/frontend:ux-c82e8fa",
+ "mmapp-test/frontend:ux-bf43263",
+ "mmapp-test/frontend:ux-6a46fb1",
+ "mmapp-test/frontend:ux-3722fad",
+ "mmapp-test/frontend:ux-fd521f6",
+ "mmapp-test/frontend:ux-731803b",
+ "mmapp-test/frontend:ux-bd3f996",
+ "mmapp-test/frontend:ux-b55588f",
+ "mmapp-test/frontend:ux-f194e72",
+ "mmapp-test/frontend:ux-aa7dbdf",
+ "mmapp-test/frontend:ux-8782054",
+ "mmapp-test/frontend:ux-673e5ba",
+ "mmapp-test/frontend:ux-4d88b97"
+ ]
+}
\ No newline at end of file
diff --git a/tests/e2e/routing-contrast.spec.ts b/tests/e2e/routing-contrast.spec.ts
new file mode 100644
index 00000000..9b3d3f72
--- /dev/null
+++ b/tests/e2e/routing-contrast.spec.ts
@@ -0,0 +1,17 @@
+import {test,expect} from '@playwright/test'
+import axe from 'axe-core'
+import {writeFileSync} from 'node:fs'
+const states=['Established','Active','Idle','Connect','OpenSent','OpenConfirm','Unknown']
+const server={id:1,name:'Own218 router',host:'192.0.2.1',enabled:true,country:'RU',status:null}
+const host={serverId:1,serverName:server.name,serverSite:'',serverCountry:'RU'}
+for(const theme of ['light','dark'])for(const width of [1440,320])for(const section of ['bgp','ospf'])test(`Routing contrast ${section} ${theme} ${width}`,async({page})=>{
+ test.setTimeout(90000);await page.setViewportSize({width,height:1000});await page.addInitScript(t=>localStorage.setItem('rl-theme',t),theme)
+ let writes=0;const sessions=states.map((state,index)=>({...host,id:'*'+(index+1),name:'own218-'+state,peerIp:'192.0.2.2',remoteAs:64513,localAs:64512,localId:'192.0.2.1',remoteId:'192.0.2.2',state,type:index===1?'iBGP':index===6?'Unknown':'eBGP',uptime:'1h',holdTime:90,keepalive:30,prefixesRx:12,prefixesTx:2,inputMessages:20,outputMessages:10,capabilities:[],lastError:null}))
+ const catalog={neighbors:[],interfaces:[],instances:[],bfdSessions:[],routes:['O','O IA','O E1','O E2','Unknown'].map((type,index)=>({...host,id:'r'+index,destination:'192.0.'+(index+2)+'.0/24',type,cost:10,nextHop:'192.0.2.2',via:'ether1',area:'backbone'})),failures:[]}
+ await page.route('**/health',r=>r.fulfill({json:{ok:true}}));await page.route('**/api/**',r=>{if(r.request().method()!=='GET')writes++;const p=new URL(r.request().url()).pathname;return r.fulfill({json:p==='/api/auth/config'?{required:false}:p==='/api/servers'?[server]:p==='/api/bgp/catalog'?{sessions,failures:[]}:p==='/api/ospf/catalog'?catalog:{}})})
+ await page.goto('/'+section);if(section==='ospf')await page.getByRole('button',{name:'Маршруты',exact:true}).click()
+ const rows=page.locator('tr[data-row-id]');await expect(rows).toHaveCount(section==='bgp'?7:5);await page.evaluate(()=>document.fonts.ready);await page.waitForTimeout(900);await page.addScriptTag({content:axe.source});const findings=[]
+ for(let index=0;indexel.id='own218-audit');const result=await page.evaluate(async()=>(window as unknown as {axe:typeof axe}).axe.run({include:['#own218-audit']},{runOnly:{type:'rule',values:['color-contrast']}}));findings.push({row:index,cell:cellIndex,text:(await cell.innerText()).trim(),violations:result.violations.map(v=>({id:v.id,nodes:v.nodes.map(n=>({target:n.target,summary:n.failureSummary}))}))});await cell.evaluate(el=>el.removeAttribute('id'))}
+ writeFileSync(`tmp/routing218-${section}-${theme}-${width}.json`,JSON.stringify({date:'2026-10-14',section,theme,width,scope:'Explicitly scrolled BGP type/state/prefix RX and OSPF route type cells',findings},null,2))
+ if(process.env.MMAPP_A11Y_BASELINE!=='true')expect(findings.filter(f=>f.violations.length)).toEqual([]);expect(writes).toBe(0);expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth+1)).toBe(true)
+})
diff --git a/tsconfig.json b/tsconfig.json
index 34d254dd..638708d4 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -30,5 +30,5 @@
".next/dev/types/**/*.ts",
"**/*.mts"
],
- "exclude": ["node_modules", "backend", "packages"]
+ "exclude": ["node_modules", "backend", "packages", "tmp", "temp", "test-results", "playwright-report"]
}