diff --git a/app/(main)/bgp/page.tsx b/app/(main)/bgp/page.tsx index 3e845db6..2a377b1f 100644 --- a/app/(main)/bgp/page.tsx +++ b/app/(main)/bgp/page.tsx @@ -761,11 +761,11 @@ function BgpPageContent({ selectedServerId, selectedSessionId, activeTab, onSele } banner={
-
+
{TABS.map(t => ( - ))} diff --git a/components/server-tile-rail.tsx b/components/server-tile-rail.tsx index 7e5fde08..1c07b816 100644 --- a/components/server-tile-rail.tsx +++ b/components/server-tile-rail.tsx @@ -35,6 +35,10 @@ import { LayersIcon, SearchIcon, ServerIcon } from "lucide-react" /** Preview: https://reui.io/preview/base/list-9 · https://reui.io/docs/components/base/icon-tile · https://reui.io/docs/components/base/frame · https://reui.io/docs/components/base/badge */ export const ALL_SERVERS_ID = "all" +// Device URL changes can remount the page. Keep only the current keyboard intent, +// and never restore it after the user has focused another control. +let pendingKeyboardFocus: { path: string; id: string; source: Element | null; until: number } | null = null + export interface ServerTileItem { id: string name: string @@ -185,7 +189,15 @@ function ServerTileRail({ useEffect(() => { const viewport = listRef.current?.closest('[data-slot="scroll-area-viewport"]') viewport?.scrollTo({ top: 0 }) - listRef.current?.querySelector('[aria-selected="true"]')?.scrollIntoView({ block: "nearest" }) + const selected = listRef.current?.querySelector('[aria-selected="true"]') + selected?.scrollIntoView({ block: "nearest" }) + const pending = pendingKeyboardFocus + if (pending && (pending.until < performance.now() || pending.path !== window.location.pathname)) { + pendingKeyboardFocus = null + } else if (pending && pending.id === resolvedId && selected?.getClientRects().length) { + if (document.activeElement === document.body || document.activeElement === pending.source) selected.focus({ preventScroll: true }) + pendingKeyboardFocus = null + } }, [pageIndex, resolvedId, q, pageSize]) const visibleIds = new Set(table.getRowModel().rows.map(row => row.id)) const visibleGroups = groups.map(group => ({ ...group, items: group.items.filter(item => visibleIds.has(item.id)) })).filter(group => group.items.length > 0) @@ -200,6 +212,19 @@ function ServerTileRail({ return ids }, [filtered, showAllTile]) + const tabStopId = activeId && selectableIds.includes(activeId) + ? activeId + : showAllTile ? ALL_SERVERS_ID : table.getRowModel().rows.find(row => isSelectable(row.original))?.id + + function selectWithKeyboard(id: string) { + if (id === resolvedId) { + listRef.current?.querySelector('[aria-selected="true"]')?.focus() + return + } + pendingKeyboardFocus = { path: window.location.pathname, id, source: document.activeElement, until: performance.now() + 10000 } + onSelect(id) + } + function moveSelection(delta: number) { if (selectableIds.length === 0) return const idx = selectableIds.indexOf(resolvedId) @@ -208,7 +233,7 @@ function ServerTileRail({ ? delta > 0 ? 0 : selectableIds.length - 1 : Math.min(selectableIds.length - 1, Math.max(0, idx + delta)) const nextId = selectableIds[nextIdx] - if (nextId) onSelect(nextId) + if (nextId) selectWithKeyboard(nextId) } function handleListKeyDown(event: KeyboardEvent) { @@ -225,13 +250,13 @@ function ServerTileRail({ } if (event.key === "Home") { event.preventDefault() - if (selectableIds[0]) onSelect(selectableIds[0]) + if (selectableIds[0]) selectWithKeyboard(selectableIds[0]) return } if (event.key === "End") { event.preventDefault() const last = selectableIds[selectableIds.length - 1] - if (last) onSelect(last) + if (last) selectWithKeyboard(last) } } @@ -283,8 +308,6 @@ function ServerTileRail({ className="flex flex-col gap-0.5 p-1.5" role="listbox" aria-label="Серверы" - aria-activedescendant={activeId ? `${railId}-server-tile-${activeId}` : undefined} - tabIndex={0} onKeyDown={handleListKeyDown} > {showAllTile ? ( @@ -292,6 +315,7 @@ function ServerTileRail({ id={`${railId}-server-tile-${ALL_SERVERS_ID}`} item={allItem} selected={resolvedId === ALL_SERVERS_ID} + tabStop={tabStopId === ALL_SERVERS_ID} onSelect={() => onSelect(ALL_SERVERS_ID)} showCount={showCount} showType={false} @@ -315,6 +339,7 @@ function ServerTileRail({ id={`${railId}-server-tile-${item.id}`} item={item} selected={resolvedId === item.id} + tabStop={tabStopId === item.id} onSelect={() => onSelect(item.id)} showCount={showCount} showType={showType} @@ -354,6 +379,7 @@ function ServerTileButton({ id, item, selected, + tabStop, onSelect, showCount, showType, @@ -363,6 +389,7 @@ function ServerTileButton({ id: string item: ServerTileItem selected: boolean + tabStop: boolean onSelect: () => void showCount: boolean showType: boolean @@ -385,6 +412,7 @@ function ServerTileButton({ type="button" role="option" aria-selected={selected} + tabIndex={tabStop ? 0 : -1} title={item.title ?? hostname} disabled={!selectable} onClick={onSelect} diff --git a/docs/ux-ui-external-acceptance.md b/docs/ux-ui-external-acceptance.md index a508c84b..c7321332 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-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт. +Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап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). @@ -18,6 +18,27 @@ OSPF84: real GRE Full adjacency/private routes/Ping3/3 и4 live PC/320px URL/bro BFD85: Up/Full/own route/Ping,4 live UI и loss/withdrawal/recovery приняты; exact own cleanup8 коллекций×2 passed. Withdrawal5,245ms не подтверждает sub-second BFD timing; этот вопрос открыт. Runtime routing83 без новой сборки. [Отчёт](ux-ui/acceptance/ospf-bfd-loss-2026-10-09.json). +## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15) + +Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json). + +ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены. + +TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах. + +Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены. + +Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json). + +Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe. + +| Референс | Схема и экран | +| --- | --- | +| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) | +| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) | + +Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными. + ## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15) По сохранённому разрешению254 выполнен тот же exact mmt253-8e62a941.pngs.top на104.165.244.7. Перед началом снят свежий приватный native/export/SSL/files/SQL baseline; own certificate/files/jobs/DNS0, Cloudflare token/zone/account проверены. Renewal временно выключен. Подготовленный backend258 активирован; frontend251/Portal/DB сохранены, backend healthy. [Развёртывание](ux-ui/acceptance/acme259-deployment.json). diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md index b35a8861..5cf697f8 100644 --- a/docs/ux-ui-plan.md +++ b/docs/ux-ui-plan.md @@ -1,8 +1,29 @@ # План реализации UX/UI по пятичасовым окнам -Актуальное продолжение 2026-10-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт. +Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап12.5 открыт. -Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-14. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат. +Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-15. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат. + +## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15) + +Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json). + +ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены. + +TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах. + +Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены. + +Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json). + +Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe. + +| Референс | Схема и экран | +| --- | --- | +| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) | +| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) | + +Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными. ## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15) @@ -314,27 +335,27 @@ Production backend/frontend ux-backups237 собраны и развёрнуты Следующий238 фиксирует уже выполненное длительное REST-наблюдение. Затем — S3 restore через реальный UI в пределах собственного bridge и retention. Полная конфигурация CHR, arbitrary PKI, другие ОС, public IPv6, multi-instance и внешние jobs не приняты этим блоком. -## Актуальная точка продолжения — 2026-10-14 +## Актуальная точка продолжения — 2026-10-15 Этапы0–11 завершены в локальных границах журнала. История выполненных внешних блоков и доказательства сохранены в [журнале](ux-ui-stage-12.md) и [протоколе](ux-ui-external-acceptance.md). Реальная Telegram доставка201 и Portal SSO195–199 приняты в описанных границах. Multi-instance, внешние аккаунты/клиентские ОС и оставшиеся профильные сетевые ограничения открыты. По поручению2026-10-09 работать укрупнёнными разделами, последовательно на текущей модели: карта возможностей → исправления → профильные тесты → build/deploy → собственные реальные сценарии → cleanup → один локальный коммит без push. Проверять квоту после завершённого раздела; незавершённые проверки явно переносить в журнал. -## Остаток приёмки по группам — 2026-10-14 +## Остаток приёмки по группам — 2026-10-15 -Этапы0–11 завершены в локальных границах. Этап12.5 открыт: ниже7 групп проверок, а не7 обещанных окон или точное число тестов. Детали и исторические ограничения сохраняются в [матрице](ux-ui-remaining-acceptance.md). +Этапы0–11 завершены в локальных границах. Этап12.5 открыт. Ниже семь групп охвата; это не семь обязательных новых этапов и не обещание числа окон. Принятые сценарии не запускать заново без изменения кода или найденного риска. Для расширения стенда отдельно нужны перечисленные условия. -| Группа | Что уже подтверждено | Оставшийся результат / что требуется | +| Группа | Подтверждено | Открытый объём / условие | | --- | --- | --- | -| REST и достоверность данных | Ошибки OSPF/BFD/BGP/GRE/WAN215–221 явные; latest API smoke222 прошёл | Причина единичных native401/500 неизвестна. Короткое независимое наблюдение возможно сейчас; оно не даст SLA | -| Клиентские ОС и внешняя сеть | CHR↔CHR/Linux, private IPv4/ULA и synthetic IPv6/IPFIX; Android full-tunnel/reconnect227 подтверждены пользователем | Windows/macOS/iOS VPN, расширенная Android приёмка, независимые WAN/multi-WAN и public IPv6; public IPv6 на текущем стенде отсутствует | -| Масштаб и длительная устойчивость | Контролируемые lock/dedupe/loss/recovery и короткие native проверки | Несколько backend/worker/writers, длительный rekey/REST/traffic, physical disconnect и maximum throughput | -| Внешние хранилища и jobs | Локальные API/errors/RBAC/owned SQL; реальные Portal1.3.1/Telegram195–201 и Selectel S3/client/API/ownSQL228–229 | Native S3 backup/restore/live UI/retention; provider/container/Gitea/MaxMind jobs с соответствующими endpoints/аккаунтами; private/paid credentials не выдумывать | -| Расширенные сетевые конфигурации | GRE/WG/VXLAN/IPsec/OSPF/BFD/BGP сценарии и точные границы записаны | from-pool/EUI-64/multiple-address import/export, outer IPv6, произвольная PKI/OID/policy/path-length и длительные переходы; BFD-only gate гарантией не стал | -| Общий UX, доступность и производительность | Полная UI192 исторически; latest backend25/UI52, scoped contrast/live218 и keyboard206 | Финальная свежая UI-регрессия перед выпуском, профильный performance и ручная accessibility/screen-reader приёмка; scoped axe не полный WCAG | -| Границы выпуска | План/доказательства/датированные блоки сохранены; коммиты локальные | Определить требуемые конфигурации и принять результат в этих границах; невыполненные варианты остаются явными ограничениями | +| REST и достоверность данных | Нативные наблюдения236/238; ограничение shared REST pools257,12 параллельных чтений двух CHR и25 backend-профилей прошли | Длительное наблюдение после transport fix; историческая причинность всех401/500 и сравнение версий RouterOS полностью не доказаны | +| Клиентские ОС и внешняя сеть | CHR↔CHR/Linux; Android StrongSwan full-tunnel и reconnect227 подтверждены пользователем | Windows/macOS/iOS и другие клиенты; независимые WAN/multi-WAN. Публичного IPv6 на предоставленном стенде нет — проверка требует другого подключения | +| Масштаб и устойчивость | Контролируемые PG lock/dedupe/loss/recovery, отдельные multi-process сценарии194/200/201 | Несколько полноценных backend/worker со всеми writers, длительный rekey/traffic, физические потери WAN/DB и maximum throughput | +| Хранилища и внешние jobs | Реальные Portal/Telegram195–201, Selectel S3/limited bridge restore/retention228–240, GeoIP mirror242; контейнеры248; production ACME250 и реальное renewal/rotation259 с trusted TLS/cleanup | Large native export/migration buckets/multi-instance S3; CA revocation отдельно не принят. Gitea publishing CI не запускался: действует no push. Direct licensed MaxMind отсутствует в текущей реализации | +| Расширенные сетевые конфигурации | GRE/WG/VXLAN/IPsec/OSPF/BFD/BGP в записанных сценариях, включая private IPv6 | from-pool/EUI-64/multiple-address import/export, outer IPv6, произвольная PKI/OID/path-length, независимые внешние peers и длительные переходы. BFD-only gate не гарантирован | +| UX, доступность и производительность | UI226, ARIA мастеров251, новая проверка260 текущего runtime описана выше | Исправления260 и72 live состояния приняты; оставшиеся12 экранов и ручная screen-reader приёмка отдельно. Synthetic timing не field CWV, scoped axe не полная WCAG сертификация | +| Границы выпуска | Датированный журнал, протоколы и локальные коммиты без push | Подготовить итоговый перечень принятых конфигураций/исключений для решения о выпуске; лабораторный успех не объявлять приёмкой всех конфигураций | -Portal offline JWT действует доexp после logout/role change; это принятая граница198, а не мгновенная revocation. Controlled Telegram unknown/retry201 не доказывает physical WAN lost ACK. User screenshots и vendor ReUI/theme сохранены. +Portal offline JWT действует доexp после logout/role change; это записанная граница198, не мгновенная revocation. Controlled Telegram unknown/retry201 не доказывает физическую потерю ACK в WAN. Архивные записи ниже отражают состояние на свою дату; актуальный статус задают эта таблица и верхняя точка продолжения. ## Цель и обязательные требования diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md index bc79ae33..1a7d79d9 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -1,6 +1,6 @@ # UX/UI: журнал реализации -Актуальное продолжение 2026-10-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт. +Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап12.5 открыт. Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md). @@ -36,7 +36,28 @@ | 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 | | 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 | | 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 | -| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная12.3.83; внешняя12.5 открыта, блоки1–259 в записанных границах. Реальная ротация/renewal259 и полный откат приняты. Runtime backend ux-certificates258/frontend ux-certificates251. Общая backend-регрессия257 и профиль сертификатов258 прошли. Следующий260 — актуальный остаток и профильная итоговая приёмка. | +| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная12.3.83; внешняя12.5 открыта, блоки1–260 в записанных границах. Ротация/renewal259 и откат приняты; общий UI260:72 live states passed. Runtime backend258/frontend260. Следующий261 — оставшиеся12 экранов и итоговые границы. | + +## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15) + +Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json). + +ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены. + +TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах. + +Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены. + +Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json). + +Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe. + +| Референс | Схема и экран | +| --- | --- | +| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) | +| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) | + +Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными. ## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15) diff --git a/docs/ux-ui-remaining-acceptance.md b/docs/ux-ui-remaining-acceptance.md index 596ef729..ae3a89b6 100644 --- a/docs/ux-ui-remaining-acceptance.md +++ b/docs/ux-ui-remaining-acceptance.md @@ -1,10 +1,31 @@ -# Оставшаяся внешняя приёмка — 2026-10-14 +# Оставшаяся внешняя приёмка — 2026-10-15 -Актуальное продолжение 2026-10-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт. +Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап12.5 открыт. -Это актуальная матрица после 12.5.259. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. +Это актуальная матрица после 12.5.260. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. -Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-certificates258`, frontend `ux-certificates251`; 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-certificates258`, frontend `ux-accessibility260-final`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись. + +## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15) + +Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json). + +ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены. + +TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах. + +Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены. + +Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json). + +Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe. + +| Референс | Схема и экран | +| --- | --- | +| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) | +| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) | + +Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными. ## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15) @@ -316,21 +337,21 @@ Production backend/frontend ux-backups237 собраны и развёрнуты Следующий238 фиксирует уже выполненное длительное REST-наблюдение. Затем — S3 restore через реальный UI в пределах собственного bridge и retention. Полная конфигурация CHR, arbitrary PKI, другие ОС, public IPv6, multi-instance и внешние jobs не приняты этим блоком. -## Остаток приёмки по группам — 2026-10-14 +## Остаток приёмки по группам — 2026-10-15 -Этапы0–11 завершены в локальных границах. Этап12.5 открыт: ниже7 групп проверок, а не7 обещанных окон или точное число тестов. Детали и исторические ограничения сохраняются в [матрице](ux-ui-remaining-acceptance.md). +Этапы0–11 завершены в локальных границах. Этап12.5 открыт. Ниже семь групп охвата; это не семь обязательных новых этапов и не обещание числа окон. Принятые сценарии не запускать заново без изменения кода или найденного риска. Для расширения стенда отдельно нужны перечисленные условия. -| Группа | Что уже подтверждено | Оставшийся результат / что требуется | +| Группа | Подтверждено | Открытый объём / условие | | --- | --- | --- | -| REST и достоверность данных | Ошибки OSPF/BFD/BGP/GRE/WAN215–221 явные; latest API smoke222 прошёл | Correlated236:384 samples/40 diagnostics без отказов; current policies/address/load исправны.828/304 retained REST sessions и vendor inactive-session fix — гипотеза; 238:763.844s/624samples/24diagnostics все200, API records изменились без отказа; причинность401/500 и version control остаются открытыми | -| Клиентские ОС и внешняя сеть | CHR↔CHR/Linux, private IPv4/ULA и synthetic IPv6/IPFIX; Android full-tunnel/reconnect227 подтверждены пользователем | Windows/macOS/iOS VPN, расширенная Android приёмка, независимые WAN/multi-WAN и public IPv6; public IPv6 на текущем стенде отсутствует | -| Масштаб и длительная устойчивость | Контролируемые lock/dedupe/loss/recovery и короткие native проверки | Несколько backend/worker/writers, длительный rekey/REST/traffic, physical disconnect и maximum throughput | -| Внешние хранилища и jobs | Portal/Telegram195–201; native S3230, persistence235, missing UX237, bridge-only live restore239, isolated retention240; real GeoIP mirror242; availability243 | Large native export только HTTP fixture; migration buckets/multi-instance. Containers:feature=true, package не обнаружен,/container400 — нужны package/lifecycle. ACME:no token/zone, нужен test-domain/scoped Cloudflare/staging. Gitea:local dry-run принят; publishing CI не запускался (no push). Direct licensed MaxMind не реализован | -| Расширенные сетевые конфигурации | GRE/WG/VXLAN/IPsec/OSPF/BFD/BGP сценарии и точные границы записаны | from-pool/EUI-64/multiple-address import/export, outer IPv6, произвольная PKI/OID/policy/path-length и длительные переходы; BFD-only gate гарантией не стал | -| Общий UX, доступность и производительность | Полная UI192 исторически; latest backend25/UI52, scoped contrast/live218 и keyboard206 | Свежая UI226 принята в записанных границах; профильный performance и ручная accessibility/screen-reader приёмка; scoped axe не полный WCAG | -| Границы выпуска | План/доказательства/датированные блоки сохранены; коммиты локальные | Определить требуемые конфигурации и принять результат в этих границах; невыполненные варианты остаются явными ограничениями | +| REST и достоверность данных | Нативные наблюдения236/238; ограничение shared REST pools257,12 параллельных чтений двух CHR и25 backend-профилей прошли | Длительное наблюдение после transport fix; историческая причинность всех401/500 и сравнение версий RouterOS полностью не доказаны | +| Клиентские ОС и внешняя сеть | CHR↔CHR/Linux; Android StrongSwan full-tunnel и reconnect227 подтверждены пользователем | Windows/macOS/iOS и другие клиенты; независимые WAN/multi-WAN. Публичного IPv6 на предоставленном стенде нет — проверка требует другого подключения | +| Масштаб и устойчивость | Контролируемые PG lock/dedupe/loss/recovery, отдельные multi-process сценарии194/200/201 | Несколько полноценных backend/worker со всеми writers, длительный rekey/traffic, физические потери WAN/DB и maximum throughput | +| Хранилища и внешние jobs | Реальные Portal/Telegram195–201, Selectel S3/limited bridge restore/retention228–240, GeoIP mirror242; контейнеры248; production ACME250 и реальное renewal/rotation259 с trusted TLS/cleanup | Large native export/migration buckets/multi-instance S3; CA revocation отдельно не принят. Gitea publishing CI не запускался: действует no push. Direct licensed MaxMind отсутствует в текущей реализации | +| Расширенные сетевые конфигурации | GRE/WG/VXLAN/IPsec/OSPF/BFD/BGP в записанных сценариях, включая private IPv6 | from-pool/EUI-64/multiple-address import/export, outer IPv6, произвольная PKI/OID/path-length, независимые внешние peers и длительные переходы. BFD-only gate не гарантирован | +| UX, доступность и производительность | UI226, ARIA мастеров251, новая проверка260 текущего runtime описана выше | Исправления260 и72 live состояния приняты; оставшиеся12 экранов и ручная screen-reader приёмка отдельно. Synthetic timing не field CWV, scoped axe не полная WCAG сертификация | +| Границы выпуска | Датированный журнал, протоколы и локальные коммиты без push | Подготовить итоговый перечень принятых конфигураций/исключений для решения о выпуске; лабораторный успех не объявлять приёмкой всех конфигураций | -Portal offline JWT действует доexp после logout/role change; это принятая граница198, а не мгновенная revocation. Controlled Telegram unknown/retry201 не доказывает physical WAN lost ACK. User screenshots и vendor ReUI/theme сохранены. +Portal offline JWT действует доexp после logout/role change; это записанная граница198, не мгновенная revocation. Controlled Telegram unknown/retry201 не доказывает физическую потерю ACK в WAN. Архивные записи ниже отражают состояние на свою дату; актуальный статус задают эта таблица и верхняя точка продолжения. | Раздел | Подтверждено | Осталось / условие завершения | | --- | --- | --- | @@ -345,14 +366,14 @@ Portal offline JWT действует доexp после logout/role change; э | BGP | Established83, exact /32 exchange/API/UI/administrative withdrawal92, BFD primary GRE loss/alternate/recovery93; basic native IPv6 eBGP/AFI/NLRI/Ping/withdrawal/API/UI155; native IPv6 BGP/BFD scoped withdrawal/recovery160; private4-byte ASN/as4/API/UI162 | Hold timer/TCP loss безBFD принят104; packet loss в переходе, разные внешние peers/WAN policy, basic BFDv6 принят160; IPv6 outer/public WAN/scale. Два логических пути используют тот же public WAN; physical redundancy не принята | | Recursive routes | Production PUT/change/own history restore88, disabled guards, partial/unknown/applied outcomes, live формы/контекст | Автоматический rollback/retry, durable receipts и несколько экземпляров отсутствуют; PUT не является idempotent noop. Полная произвольная legacy catalog миграция не принята | | Diagnostics/terminal | Allowlist/native reads, Ping/trace/route/MTU/DNS, actual avg-rtt, disabled guards, JWT/live PC/mobile89; real default memory tail/log limits/API/UI133 | DNS использует backend resolver; MTU — ограниченная оценка, не гарантированный максимум. Stop ожидания не доказывает остановку RouterOS команды; arbitrary CLI и physical disconnect не приняты | -| Portal/интеграции | Portal1.3.1/issuer/login/RBAC/expiry/context195–199, actual Telegram201; Selectel S3 native/API/live limited restore/retention228–240; real GeoIP mirror242 | Offline JWT сохраняетдоступ доexp; S3 large-native/migration/multi-instance отдельно; containers lifecycle248 принят в записанных границах; ACME issuance250 принят; automatic renewal/CA revoke и remote Gitea publishing не приняты; direct licensed MaxMind не реализован. ClientOS/scale/WAN отдельно | +| Portal/интеграции | Portal1.3.1/issuer/login/RBAC/expiry/context195–199, actual Telegram201; Selectel S3 native/API/live limited restore/retention228–240; real GeoIP mirror242 | Offline JWT сохраняетдоступ доexp; S3 large-native/migration/multi-instance отдельно; containers lifecycle248 принят в записанных границах; ACME issuance250 принят; automatic renewal/rotation259 приняты в конкретном сценарии; CA revoke и remote Gitea publishing не приняты; direct licensed MaxMind не реализован. ClientOS/scale/WAN отдельно | | Общая приёмка | Локальная12.3.83; UI1244/context221/compiled984 parity139; профильные build/E2E/live проверки последующих блоков | Traffic initial live mock-id gate passed141; IPv6 special ranges/cache passed142; unknown RIPE coordinates passed143; IPv6 synthetic UDP/worker/SQL passed144; native CHR ULA/IPFIX/API/UI passed145; ICMPv6/IPv6 endpoint labels passed146; HTTP DTO validation passed147; scoped flow catalog/serverId guards passed148; strict monthly calendar passed149; Sessions labels passed150; application GRE static IPv6 lifecycle151 passed; WireGuard static IPv6 lifecycle152 и disabled SQL/API gates passed; OSPF/BFD boundary/warning153 passed; OSPFv3 routes154 accepted in recorded bounds; BGP IPv6 basic155 passed; bigint ID/SQL guards156 passed; общая UI1260/context221/backend24 regression157 passed; IPv6 BFD correlation158 profile/deployed passed; native OSPFv3/BFDv6 boundary/recovery159 passed; BGP IPv6/BFD withdrawal/recovery160 passed; selected BGP/alert unknown guards161 replay/profiles/deployed passed (SQL stability skipped); private4-byte ASN162 native/API/UI passed; duplicate BGP alert keys163 replay/profiles/deployed passed; nullable selected BGP164 replay/profiles/API/UI passed; collector165 parallel/current guards/read-only PG passed; positive partial/error SQL166/full24 with PG passed (optional network SQL not repeated); GRE/BGP freshness167/SQL8 passed; other source audit168 выявил identity/disabled/freshness gaps; локальный server/traffic identity guard169 passed без SQL/deploy, source/per-probe/freshness170/ownSQL16/compiled1008/deployed passed; source diagnostics171/42E2E/ownSQL metadata/live2 passed; monitoring history172/ownSQL26/compiled1020/deployed passed; Ping known/error173/ownHTTP-SQL8/85uniqueE2E/native4/4/deployed passed; traffic confidence174 baseline reproduced/local guard passed; owned SQL9/9358ms/deploy175/1024compiled/live smoke passed; legacy history/provenance open; compound RTT176 local/native replay2 passed, SQL10/deploy177/1024compiled/live smoke passed; history178/legacy provenance/current TTL/manual truth/SQL9+14/35unique E2E/native GET/deployed passed; live179 complete/disable-config/bounded fallback/ownSQL10/nativeSSE2/liveUI2 passed; volume180 exact/unit marker/ownSQL6/18E2E/API/liveUI/deployed passed; binding status181 native/ownSQL9/8E2E/deployed passed; peer atomicity182 ownSQL3/native/API/UI passed; peer failure warning183 ownSQL2/4E2E/deployed passed; inactive history184 ownSQL2/deployed passed; gap185/identity186/restore187/wave188/sourceguard189/confirmed peak190 passed; aggregate peaks191, request dedupe193, cross-process lock194/200, full regression192 (1286 unique E2E и25-й профиль privacy196 scoped), Portal195–199/Telegram201 passed; последующие изменения проверять отдельно; scoped axe не означает полную WCAG сертификацию | Доказательства и точные ограничения: [полная история этапа12](ux-ui-stage-12.md), [внешний протокол](ux-ui-external-acceptance.md), [BGP93](ux-ui/acceptance/bgp-failover-2026-10-10.json), [паспорт стенда](ux-ui-test-server.md). Невыполненные пункты не являются все блокерами данного лабораторного сценария: для выпуска необходимо явно определить требуемые конфигурации и принять их либо согласовать ограничение. -## Следующая последовательность — 2026-10-14 +## Следующая последовательность — 2026-10-15 -1. **Приёмка260:** обновить актуальный остаток без устаревших blockers; профильный UX/a11y/performance контроль текущего runtime. Реальная ротация и откат259 приняты. +1. **Приёмка261:**12 оставшихся default screens с real Portal/API и точным read-only guard; профильный audit/layout/performance. Сертификаты259 и UI260 приняты в записанных границах. 2. **Профильный контроль:** для новых изменений выполнять затронутые SQL/API/UI проверки. Общая регрессия192 выполнена; не повторять её без изменений общей основы или нового основания. 3. **Внешние зависимости:** Portal issuer/login/revoke, Telegram bot/chat/разрешённое сообщение, клиентские ОС, независимые WAN/public IPv6 и scale/multi-instance — отдельные группы по таблице выше. Ограничения текущего стенда не превращать в обещание приёмки всех конфигураций. diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md index a4cabb6f..3868ea1a 100644 --- a/docs/ux-ui-stage-12.md +++ b/docs/ux-ui-stage-12.md @@ -1,6 +1,6 @@ # Этап 12: итоговая приёмка UX/UI -Актуальное продолжение 2026-10-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт. +Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап12.5 открыт. План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push. @@ -4152,6 +4152,27 @@ Real Portal/browser API, provider local verified: создан один job ро Runtime backend ux-backup234/frontend ux-routing218 healthy; новое application image для volume-only fix не требовалось. Пароли CHR не менялись, S3 mutations0 в235, configs body/JWT не опубликованы, private local manifest удалён. Исходные10 missing local files не восстановлены и records не удалены. Force-recreate — не host reboot/power loss/fsync/DR приёмка. Далее236 — UX недоступных local files, затем version retention границы и оставшиеся7 группы матрицы; multi-instance guard не принимается. Этап12.5 открыт. +## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15) + +Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json). + +ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены. + +TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах. + +Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены. + +Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json). + +Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe. + +| Референс | Схема и экран | +| --- | --- | +| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) | +| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) | + +Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными. + ## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15) По сохранённому разрешению254 выполнен тот же exact mmt253-8e62a941.pngs.top на104.165.244.7. Перед началом снят свежий приватный native/export/SSL/files/SQL baseline; own certificate/files/jobs/DNS0, Cloudflare token/zone/account проверены. Renewal временно выключен. Подготовленный backend258 активирован; frontend251/Portal/DB сохранены, backend healthy. [Развёртывание](ux-ui/acceptance/acme259-deployment.json). diff --git a/docs/ux-ui-test-server.md b/docs/ux-ui-test-server.md index 24936032..dd636915 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-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт. +Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап12.5 открыт. | Параметр | Проверенное состояние | | --- | --- | @@ -14,7 +14,7 @@ | PostgreSQL | postgres:18-alpine,18.6;mmapp-test-postgres healthy;SELECT version() passed | | Данные | Отдельный Compose project mmapp-test,том mmapp-test_mmapp-pgdata и каталог ./data | | Доступ | PostgreSQL не публикуется;frontend3000/backend8000 привязаны к127.0.0.1 после запуска | -| Образы приложения | mmapp-test/backend:ux-certificates258 и mmapp-test/frontend:ux-certificates251; backend healthy, проверено2026-10-15 в259 | +| Образы приложения | mmapp-test/backend:ux-certificates258 и mmapp-test/frontend:ux-accessibility260-final; backend healthy, проверено2026-10-15 в260 | Compose SHA256:3052bd6ab5d9865f97605cebfb5ff27a4410cd3357b81153a65a87fb7c92582f. OptionalBACKEND_IMAGE_TAG позволяетобновлятьbackendотдельно. Локальный и удалённый файлы совпали. Шаблоны: [Compose](../deploy/docker-compose.test.yml), [env без секретов](../deploy/env.test.example). @@ -34,6 +34,27 @@ OSPF84: real GRE Full adjacency/private routes/Ping3/3 и4 live PC/320px URL/bro BFD85: Up/Full/own route/Ping,4 live UI и loss/withdrawal/recovery приняты; exact own cleanup8 коллекций×2 passed. Withdrawal5,245ms не подтверждает sub-second BFD timing; этот вопрос открыт. Runtime routing83 без новой сборки. [Отчёт](ux-ui/acceptance/ospf-bfd-loss-2026-10-09.json). +## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15) + +Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json). + +ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены. + +TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах. + +Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены. + +Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json). + +Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe. + +| Референс | Схема и экран | +| --- | --- | +| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) | +| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) | + +Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными. + ## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15) По сохранённому разрешению254 выполнен тот же exact mmt253-8e62a941.pngs.top на104.165.244.7. Перед началом снят свежий приватный native/export/SSL/files/SQL baseline; own certificate/files/jobs/DNS0, Cloudflare token/zone/account проверены. Renewal временно выключен. Подготовленный backend258 активирован; frontend251/Portal/DB сохранены, backend healthy. [Развёртывание](ux-ui/acceptance/acme259-deployment.json). diff --git a/docs/ux-ui/acceptance/ui260-baseline.json b/docs/ux-ui/acceptance/ui260-baseline.json new file mode 100644 index 00000000..c10eefb7 --- /dev/null +++ b/docs/ux-ui/acceptance/ui260-baseline.json @@ -0,0 +1,2063 @@ +{ + "date": "2026-10-15", + "block": "12.5.260", + "origin": "http://localhost:3000", + "realPortalUiLogin": true, + "responseMocks": false, + "deviceWritesPermitted": false, + "cases": [ + { + "theme": "dark", + "path": "/dashboard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 703, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 16 + }, + "performance": { + "lcp": 668, + "cls": 0.18394311005015432, + "longTasks": 1, + "longTaskMs": 118, + "ttfbMs": 94.30000000074506, + "domInteractiveMs": 289, + "durationMs": 523.1999999992549 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/dashboard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 555, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 16 + }, + "performance": { + "lcp": 528, + "cls": 0.035963546874999995, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 54.5, + "domInteractiveMs": 222.5, + "durationMs": 493.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/servers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 553, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 672, + "cls": 0.000044211847752700614, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 97.30000000074506, + "domInteractiveMs": 239.69999999925494, + "durationMs": 471.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/servers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 536, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 548, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 108, + "domInteractiveMs": 207.60000000149012, + "durationMs": 480.69999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/uptime", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 832, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 772, + "cls": 0.01717699266975309, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.70000000298023, + "domInteractiveMs": 201.5, + "durationMs": 498.69999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/uptime", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 777, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 756, + "cls": 0.08757044687499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 53, + "domInteractiveMs": 151.5, + "durationMs": 386.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/traffic", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 563, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 1488, + "cls": 0.025900711534288193, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 87, + "domInteractiveMs": 234, + "durationMs": 532.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/traffic", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 524, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 488, + "cls": 0.11047610429687499, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 57.599999997764826, + "domInteractiveMs": 203.80000000074506, + "durationMs": 444.0999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/statistics", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 633, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 804, + "cls": 0.05435100839120371, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 154.20000000298023, + "domInteractiveMs": 288.30000000074506, + "durationMs": 611.1999999992549 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/statistics", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 612, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 776, + "cls": 0.07823437187499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.400000002235174, + "domInteractiveMs": 205.39999999850988, + "durationMs": 586 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/wireguard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 590, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 900, + "cls": 0.00016905755690586422, + "longTasks": 1, + "longTaskMs": 54, + "ttfbMs": 95.10000000149012, + "domInteractiveMs": 239.79999999701977, + "durationMs": 470.0999999977648 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/wireguard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 526, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 524, + "cls": 0.00063807578125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.19999999925494, + "domInteractiveMs": 201.30000000074506, + "durationMs": 411.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ipsec", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 553, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 944, + "cls": 0.00006102382330246914, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 98.40000000223517, + "domInteractiveMs": 233.5, + "durationMs": 440.30000000074506 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ipsec", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 564, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 560, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 51.399999998509884, + "domInteractiveMs": 216.19999999925494, + "durationMs": 486.5999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/gre", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 586, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 564, + "cls": 0.06258026005497686, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 98.09999999776483, + "domInteractiveMs": 271.30000000074506, + "durationMs": 478.4000000022352 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/gre", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 566, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 564, + "cls": 0.10702192187500001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 92.60000000149012, + "domInteractiveMs": 257.30000000074506, + "durationMs": 486.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/vxlan", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 553, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 548, + "cls": 0.12087190923996914, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 99.19999999925494, + "domInteractiveMs": 234.5, + "durationMs": 432.5 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/vxlan", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 519, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 480, + "cls": 0.24465938125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.5, + "domInteractiveMs": 209.09999999776483, + "durationMs": 432.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ospf", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1113, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 752, + "cls": 0.0054106802782600315, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 154, + "domInteractiveMs": 291, + "durationMs": 722.5 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ospf", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 464, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 428, + "cls": 0.019630139453125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 54.899999998509884, + "domInteractiveMs": 140.80000000074506, + "durationMs": 350.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/bgp", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 552, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 540, + "cls": 0.008227206790123459, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 107.39999999850988, + "domInteractiveMs": 238.39999999850988, + "durationMs": 461.80000000074506 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/bgp", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 533, + "layout": { + "overflow": false, + "mainOverflow": true, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 712, + "cls": 0.043276721875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.69999999925494, + "domInteractiveMs": 211.5, + "durationMs": 427.3999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/filters", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 638, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 656, + "cls": 0.036411010079089506, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 96.89999999850988, + "domInteractiveMs": 239.39999999850988, + "durationMs": 458.5999999977648 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/filters", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 554, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 520, + "cls": 0.073080721875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 60.599999997764826, + "domInteractiveMs": 162, + "durationMs": 378.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/backups", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 535, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 524, + "cls": 0.000029851948302469136, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 91.30000000074506, + "domInteractiveMs": 219.10000000149012, + "durationMs": 511.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/backups", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1024, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 552, + "cls": 0.1396459982421875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 51.900000002235174, + "domInteractiveMs": 199.30000000074506, + "durationMs": 523.2000000029802 + }, + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "targets": [ + [ + ".rounded.py-1.transition-colors:nth-child(2) > .opacity-60.tabular-nums.text-xs" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/certificates", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 586, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 856, + "cls": 0.032897526041666666, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.79999999701977, + "domInteractiveMs": 222.30000000074506, + "durationMs": 492.30000000074506 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/certificates", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 499, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 508, + "cls": 0.013925775000000001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 48.100000001490116, + "domInteractiveMs": 151, + "durationMs": 421.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/containers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 547, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 548, + "cls": 0.011791851851851852, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 100.89999999850988, + "domInteractiveMs": 233.19999999925494, + "durationMs": 435.8999999985099 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/containers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 468, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 404, + "cls": 0.025738421874999997, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.899999998509884, + "domInteractiveMs": 160.39999999850988, + "durationMs": 347.3999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/alerts", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 528, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 1000, + "cls": 0.2825991898148148, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 92.5, + "domInteractiveMs": 220.89999999850988, + "durationMs": 395.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/alerts", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 521, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 912, + "cls": 0.67899836875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.5, + "domInteractiveMs": 197.59999999776483, + "durationMs": 378.69999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/settings", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 549, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 544, + "cls": 0.0034562909613715283, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 75.89999999850988, + "domInteractiveMs": 204.80000000074506, + "durationMs": 447.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/settings", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 545, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 472, + "cls": 0.0003595375, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.599999997764826, + "domInteractiveMs": 216.20000000298023, + "durationMs": 388.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/releases", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 847, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 808, + "cls": 0.000013114872685185185, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 391.79999999701977, + "domInteractiveMs": 533.5, + "durationMs": 708.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/releases", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 511, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 452, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 65.20000000298023, + "domInteractiveMs": 197.80000000074506, + "durationMs": 367.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/dashboard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 680, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 16 + }, + "performance": { + "lcp": 676, + "cls": 0.10677783227237654, + "longTasks": 1, + "longTaskMs": 53, + "ttfbMs": 95.30000000074506, + "domInteractiveMs": 279.6000000014901, + "durationMs": 667.1999999992549 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/dashboard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 672, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 16 + }, + "performance": { + "lcp": 640, + "cls": 0.111360046875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 94.09999999776483, + "domInteractiveMs": 333.5, + "durationMs": 557.1999999992549 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/servers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 550, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 636, + "cls": 0.000044211847752700614, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 99.70000000298023, + "domInteractiveMs": 236.19999999925494, + "durationMs": 465.1000000014901 + }, + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "targets": [ + [ + "tr[data-row-id=\"1\"] > td:nth-child(2) > .bg-emerald-500\\/10.text-emerald-400.border-emerald-500\\/20" + ], + [ + "tr[data-row-id=\"2\"] > td:nth-child(2) > .bg-emerald-500\\/10.text-emerald-400.border-emerald-500\\/20" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/servers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 502, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 492, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.69999999925494, + "domInteractiveMs": 195.70000000298023, + "durationMs": 449.30000000074506 + }, + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "targets": [ + [ + "tr[data-row-id=\"1\"] > td:nth-child(2) > .bg-emerald-500\\/10.text-emerald-400.border-emerald-500\\/20" + ], + [ + "tr[data-row-id=\"2\"] > td:nth-child(2) > .bg-emerald-500\\/10.text-emerald-400.border-emerald-500\\/20" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/uptime", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 814, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 756, + "cls": 0.01717699266975309, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.69999999925494, + "domInteractiveMs": 187.10000000149012, + "durationMs": 484.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/uptime", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 793, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 780, + "cls": 0.08757044687499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 48.599999997764826, + "domInteractiveMs": 175.39999999850988, + "durationMs": 422.0999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/traffic", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 538, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 988, + "cls": 0.02607868776674624, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 86.19999999925494, + "domInteractiveMs": 218.90000000223517, + "durationMs": 461 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/traffic", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1018, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 588, + "cls": 0.11048095278320312, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.399999998509884, + "domInteractiveMs": 203, + "durationMs": 573.3999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/statistics", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 689, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 808, + "cls": 0.05435100839120371, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 101.5, + "domInteractiveMs": 308, + "durationMs": 641.3000000007451 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/statistics", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 461, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 576, + "cls": 0.07823437187499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.899999998509884, + "domInteractiveMs": 142.70000000298023, + "durationMs": 417.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/wireguard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1039, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 1024, + "cls": 0.00016905755690586422, + "longTasks": 1, + "longTaskMs": 57, + "ttfbMs": 93.69999999925494, + "domInteractiveMs": 230.69999999925494, + "durationMs": 619.1000000014901 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/wireguard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 520, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 504, + "cls": 0.00063807578125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56, + "domInteractiveMs": 210.39999999850988, + "durationMs": 417.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ipsec", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 574, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 576, + "cls": 0.00004806086033950618, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 100.39999999850988, + "domInteractiveMs": 243.59999999776483, + "durationMs": 536.6999999992549 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ipsec", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 477, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 428, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 57.69999999925494, + "domInteractiveMs": 161.60000000149012, + "durationMs": 363.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/gre", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 554, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 532, + "cls": 0.06258026005497686, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 97.79999999701977, + "domInteractiveMs": 243.30000000074506, + "durationMs": 448.4000000022352 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/gre", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 528, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 488, + "cls": 0.10702192187500001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.400000002235174, + "domInteractiveMs": 207, + "durationMs": 411.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/vxlan", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 538, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 516, + "cls": 0.12087190923996914, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 95.19999999925494, + "domInteractiveMs": 226.89999999850988, + "durationMs": 424.69999999925494 + }, + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "targets": [ + [ + ".text-sky-600" + ] + ] + }, + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/vxlan", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1070, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 608, + "cls": 0.24465938125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55, + "domInteractiveMs": 225.39999999850988, + "durationMs": 554.0999999977648 + }, + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "targets": [ + [ + ".text-sky-600" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ospf", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 543, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 532, + "cls": 0.0054106802782600315, + "longTasks": 1, + "longTaskMs": 51, + "ttfbMs": 97.30000000074506, + "domInteractiveMs": 231.19999999925494, + "durationMs": 447 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ospf", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 530, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 480, + "cls": 0.019630139453125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.600000001490116, + "domInteractiveMs": 216.60000000149012, + "durationMs": 428 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/bgp", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 553, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 540, + "cls": 0.008227206790123459, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 95, + "domInteractiveMs": 231.90000000223517, + "durationMs": 478.6000000014901 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/bgp", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 533, + "layout": { + "overflow": false, + "mainOverflow": true, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 688, + "cls": 0.043276721875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.899999998509884, + "domInteractiveMs": 209.10000000149012, + "durationMs": 424.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/filters", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 594, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 604, + "cls": 0.036411010079089506, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 96.80000000074506, + "domInteractiveMs": 226.80000000074506, + "durationMs": 445.30000000074506 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/filters", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 600, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 576, + "cls": 0.073080721875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.100000001490116, + "domInteractiveMs": 199.89999999850988, + "durationMs": 421.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/backups", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 542, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 544, + "cls": 0.000029851948302469136, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 90.79999999701977, + "domInteractiveMs": 224, + "durationMs": 528.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/backups", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 492, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 492, + "cls": 0.0657015625, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 58.5, + "domInteractiveMs": 167.40000000223517, + "durationMs": 482.30000000074506 + }, + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "targets": [ + [ + ".rounded.py-1.transition-colors:nth-child(2) > .opacity-60.tabular-nums.text-xs" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/certificates", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 497, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 752, + "cls": 0.032897526041666666, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50, + "domInteractiveMs": 181.20000000298023, + "durationMs": 385 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/certificates", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 507, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 496, + "cls": 0.013925775000000001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.100000001490116, + "domInteractiveMs": 172, + "durationMs": 397 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/containers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 561, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 560, + "cls": 0.011791851851851852, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 100.89999999850988, + "domInteractiveMs": 240.5, + "durationMs": 448.1000000014901 + }, + "violations": [ + { + "id": "target-size", + "impact": "serious", + "targets": [ + [ + ".p-1\\.5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/containers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 478, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 432, + "cls": 0.025738421874999997, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.900000002235174, + "domInteractiveMs": 162.5, + "durationMs": 362.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/alerts", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 531, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 972, + "cls": 0.2825991898148148, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 89.19999999925494, + "domInteractiveMs": 218.89999999850988, + "durationMs": 395.5999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/alerts", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 527, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 940, + "cls": 0.67622226875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 48.5, + "domInteractiveMs": 200.20000000298023, + "durationMs": 385.20000000298023 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/settings", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 504, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 468, + "cls": 0.0035598427553530094, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.19999999925494, + "domInteractiveMs": 185.29999999701977, + "durationMs": 361.29999999701977 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/settings", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 468, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 460, + "cls": 0.0003595375, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50, + "domInteractiveMs": 152.29999999701977, + "durationMs": 368.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/releases", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 555, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 508, + "cls": 0.000013114872685185185, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 99.10000000149012, + "domInteractiveMs": 241.59999999776483, + "durationMs": 415.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/releases", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 566, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 488, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 82.10000000149012, + "domInteractiveMs": 242, + "durationMs": 402.5999999977648 + }, + "violations": [], + "auditCompleted": true + } + ], + "blockedWrites": [], + "bounds": [ + "Default rendered state only; data can still be refreshing", + "Synthetic lab timing over SSH, no CPU/network throttling; not field Core Web Vitals", + "Automated WCAG-tagged checks, not manual screen-reader certification" + ], + "completed": true, + "passed": false +} diff --git a/docs/ux-ui/acceptance/ui260-deployment.json b/docs/ux-ui/acceptance/ui260-deployment.json new file mode 100644 index 00000000..64573650 --- /dev/null +++ b/docs/ux-ui/acceptance/ui260-deployment.json @@ -0,0 +1,20 @@ +{ + "date": "2026-10-15", + "block": "12.5.260", + "productionBuild": "passed", + "sourceMatched": 528, + "services": { + "backend": { + "image": "mmapp-test/backend:ux-certificates258", + "imageId": "sha256:984684709e619972bae8657208e3150d12f1c4f2b5642fecf5286a5d87e9fe71", + "status": "running", + "health": "healthy" + }, + "frontend": { + "image": "mmapp-test/frontend:ux-accessibility260-final", + "imageId": "sha256:0b7b12cfa0e64a021c8c77a3362cc54488c6aea75269f0c2ae2a0cb0b4b5192a", + "status": "running", + "health": null + } + } +} \ No newline at end of file diff --git a/docs/ux-ui/acceptance/ui260-final.json b/docs/ux-ui/acceptance/ui260-final.json new file mode 100644 index 00000000..e4aed324 --- /dev/null +++ b/docs/ux-ui/acceptance/ui260-final.json @@ -0,0 +1,2122 @@ +{ + "date": "2026-10-15", + "block": "12.5.260", + "origin": "http://localhost:3000", + "realPortalUiLogin": true, + "responseMocks": false, + "deviceWritesPermitted": false, + "cases": [ + { + "theme": "dark", + "path": "/dashboard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1243, + "requiredReads": [ + "/api/servers", + "/api/uptime/overview", + "/api/filters/rules", + "/api/bgp/catalog", + "/api/internet-path/latest", + "/api/filters/gre-tunnels", + "/api/wireguard", + "/api/traffic/servers", + "/api/vxlan" + ], + "dataResponsesMs": 3114, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 876, + "cls": 0.18891521697621286, + "longTasks": 1, + "longTaskMs": 106, + "ttfbMs": 101.19999999925494, + "domInteractiveMs": 390.6000000014901, + "durationMs": 745.3000000007451 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/dashboard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 469, + "requiredReads": [ + "/api/servers", + "/api/uptime/overview", + "/api/filters/rules", + "/api/bgp/catalog", + "/api/internet-path/latest", + "/api/filters/gre-tunnels", + "/api/wireguard", + "/api/traffic/servers", + "/api/vxlan" + ], + "dataResponsesMs": 2342, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 432, + "cls": 0.071583796875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 57.600000001490116, + "domInteractiveMs": 148.5, + "durationMs": 404.4000000022352 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/servers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 760, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 1639, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 1232, + "cls": 0.000044211847752700614, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 144.09999999776483, + "domInteractiveMs": 413.5, + "durationMs": 740.3000000007451 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/servers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 480, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 585, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 452, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.30000000074506, + "domInteractiveMs": 157.19999999925494, + "durationMs": 389.29999999701977 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/uptime", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 816, + "requiredReads": [ + "/api/uptime/overview" + ], + "dataResponsesMs": 1180, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 796, + "cls": 0.01717699266975309, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 53.899999998509884, + "domInteractiveMs": 192.5, + "durationMs": 412.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/uptime", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 890, + "requiredReads": [ + "/api/uptime/overview" + ], + "dataResponsesMs": 1246, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 880, + "cls": 0.08757044687499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 97.29999999701977, + "domInteractiveMs": 247.39999999850988, + "durationMs": 517.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/traffic", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 568, + "requiredReads": [ + "/api/traffic/servers" + ], + "dataResponsesMs": 921, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 1056, + "cls": 0.007212276415412808, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 92, + "domInteractiveMs": 226.5, + "durationMs": 540.6999999992549 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/traffic", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 543, + "requiredReads": [ + "/api/traffic/servers" + ], + "dataResponsesMs": 896, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 556, + "cls": 0.11047610429687499, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.20000000298023, + "domInteractiveMs": 199.60000000149012, + "durationMs": 526.9000000022352 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/statistics", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 551, + "requiredReads": [ + "/api/statistics" + ], + "dataResponsesMs": 911, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 720, + "cls": 0.05435100839120371, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.5, + "domInteractiveMs": 189.20000000298023, + "durationMs": 533.2000000029802 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/statistics", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 531, + "requiredReads": [ + "/api/statistics" + ], + "dataResponsesMs": 641, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 620, + "cls": 0.07823437187499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 51.100000001490116, + "domInteractiveMs": 178.80000000074506, + "durationMs": 462.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/wireguard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 562, + "requiredReads": [ + "/api/wireguard" + ], + "dataResponsesMs": 918, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 836, + "cls": 0.00016905755690586422, + "longTasks": 1, + "longTaskMs": 58, + "ttfbMs": 87, + "domInteractiveMs": 235, + "durationMs": 452.69999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/wireguard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 473, + "requiredReads": [ + "/api/wireguard" + ], + "dataResponsesMs": 830, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 460, + "cls": 0.00063807578125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 65.69999999925494, + "domInteractiveMs": 156.10000000149012, + "durationMs": 396.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ipsec", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 587, + "requiredReads": [ + "/api/ipsec" + ], + "dataResponsesMs": 1449, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 980, + "cls": 0.00006102382330246914, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 102.80000000074506, + "domInteractiveMs": 233.69999999925494, + "durationMs": 519.3000000007451 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ipsec", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 958, + "requiredReads": [ + "/api/ipsec" + ], + "dataResponsesMs": 958, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 540, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.69999999925494, + "domInteractiveMs": 141.79999999701977, + "durationMs": 446.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/gre", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 574, + "requiredReads": [ + "/api/gre/tunnels" + ], + "dataResponsesMs": 950, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 552, + "cls": 0.06258026005497686, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 124.40000000223517, + "domInteractiveMs": 253.20000000298023, + "durationMs": 474.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/gre", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 478, + "requiredReads": [ + "/api/gre/tunnels" + ], + "dataResponsesMs": 2353, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 476, + "cls": 0.10702192187500001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.20000000298023, + "domInteractiveMs": 158, + "durationMs": 407.4000000022352 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/vxlan", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 581, + "requiredReads": [ + "/api/vxlan" + ], + "dataResponsesMs": 948, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 556, + "cls": 0.12087190923996914, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 110.89999999850988, + "domInteractiveMs": 249.19999999925494, + "durationMs": 457.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/vxlan", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 452, + "requiredReads": [ + "/api/vxlan" + ], + "dataResponsesMs": 1315, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 444, + "cls": 0.24465938125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 58.69999999925494, + "domInteractiveMs": 150.30000000074506, + "durationMs": 383.5999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ospf", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 571, + "requiredReads": [ + "/api/ospf/catalog" + ], + "dataResponsesMs": 1434, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 540, + "cls": 0.0054106802782600315, + "longTasks": 1, + "longTaskMs": 50, + "ttfbMs": 105.90000000223517, + "domInteractiveMs": 244.89999999850988, + "durationMs": 470.29999999701977 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ospf", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 485, + "requiredReads": [ + "/api/ospf/catalog" + ], + "dataResponsesMs": 2343, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 444, + "cls": 0.019630139453125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.80000000074506, + "domInteractiveMs": 147.5, + "durationMs": 403.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/bgp", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 626, + "requiredReads": [ + "/api/bgp/catalog" + ], + "dataResponsesMs": 2508, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 628, + "cls": 0.008227206790123459, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 109.10000000149012, + "domInteractiveMs": 242.89999999850988, + "durationMs": 571.3000000007451 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/bgp", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1032, + "requiredReads": [ + "/api/bgp/catalog" + ], + "dataResponsesMs": 1033, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 764, + "cls": 0.043276721875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.400000002235174, + "domInteractiveMs": 178.30000000074506, + "durationMs": 471.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/filters", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 875, + "requiredReads": [ + "/api/filters/rules" + ], + "dataResponsesMs": 875, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 816, + "cls": 0.036411010079089506, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 166.10000000149012, + "domInteractiveMs": 310.20000000298023, + "durationMs": 603.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/filters", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 516, + "requiredReads": [ + "/api/filters/rules" + ], + "dataResponsesMs": 516, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 524, + "cls": 0.073080721875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.100000001490116, + "domInteractiveMs": 163.89999999850988, + "durationMs": 382 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/backups", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 520, + "requiredReads": [ + "/api/backups" + ], + "dataResponsesMs": 877, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 500, + "cls": 0.000029851948302469136, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 96.5, + "domInteractiveMs": 191.80000000074506, + "durationMs": 421.3999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/backups", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 501, + "requiredReads": [ + "/api/backups" + ], + "dataResponsesMs": 601, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 468, + "cls": 0.1396483580078125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 54.899999998509884, + "domInteractiveMs": 181.09999999776483, + "durationMs": 457.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/certificates", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 546, + "requiredReads": [ + "/api/certificates/state" + ], + "dataResponsesMs": 2408, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 856, + "cls": 0.062860234375, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 53.099999997764826, + "domInteractiveMs": 203.5, + "durationMs": 428.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/certificates", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 520, + "requiredReads": [ + "/api/certificates/state" + ], + "dataResponsesMs": 2416, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 520, + "cls": 0.061482699999999994, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.19999999925494, + "domInteractiveMs": 176.89999999850988, + "durationMs": 450.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/containers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 548, + "requiredReads": [ + "/api/containers/state" + ], + "dataResponsesMs": 2414, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 544, + "cls": 0.11558015046296297, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 109.20000000298023, + "domInteractiveMs": 239.30000000074506, + "durationMs": 441.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/containers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 502, + "requiredReads": [ + "/api/containers/state" + ], + "dataResponsesMs": 2361, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 456, + "cls": 0.11563842187500001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50, + "domInteractiveMs": 173.80000000074506, + "durationMs": 391.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/alerts", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 581, + "requiredReads": [ + "/api/alerts" + ], + "dataResponsesMs": 942, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 960, + "cls": 0.2825991898148148, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 94.40000000223517, + "domInteractiveMs": 226.10000000149012, + "durationMs": 504.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/alerts", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 510, + "requiredReads": [ + "/api/alerts" + ], + "dataResponsesMs": 1381, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 948, + "cls": 0.67899836875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.19999999925494, + "domInteractiveMs": 180.09999999776483, + "durationMs": 347.69999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/settings", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 620, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 620, + "cls": 0.0035598427553530094, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 117.5, + "domInteractiveMs": 269.5, + "durationMs": 509.0999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/settings", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 531, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 480, + "cls": 0.0003595375, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 65.09999999776483, + "domInteractiveMs": 216.60000000149012, + "durationMs": 391 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/releases", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 743, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 696, + "cls": 0.000013114872685185185, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 278.5, + "domInteractiveMs": 428.29999999701977, + "durationMs": 603.2999999970198 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/releases", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 530, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 464, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 62.5, + "domInteractiveMs": 217.09999999776483, + "durationMs": 386.5999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/dashboard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 588, + "requiredReads": [ + "/api/servers", + "/api/uptime/overview", + "/api/filters/rules", + "/api/bgp/catalog", + "/api/internet-path/latest", + "/api/filters/gre-tunnels", + "/api/wireguard", + "/api/traffic/servers", + "/api/vxlan" + ], + "dataResponsesMs": 3482, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 596, + "cls": 0.11174993919843508, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 97.29999999701977, + "domInteractiveMs": 239.5, + "durationMs": 553 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/dashboard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 607, + "requiredReads": [ + "/api/servers", + "/api/uptime/overview", + "/api/filters/rules", + "/api/bgp/catalog", + "/api/internet-path/latest", + "/api/filters/gre-tunnels", + "/api/wireguard", + "/api/traffic/servers", + "/api/vxlan" + ], + "dataResponsesMs": 2484, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 556, + "cls": 0.071302821875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 66.80000000074506, + "domInteractiveMs": 167.69999999925494, + "durationMs": 490.3999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/servers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1150, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 1150, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 988, + "cls": 0.000044211847752700614, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 115.19999999925494, + "domInteractiveMs": 323.6000000014901, + "durationMs": 730.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/servers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1052, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 1052, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 632, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 63, + "domInteractiveMs": 236, + "durationMs": 567 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/uptime", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 804, + "requiredReads": [ + "/api/uptime/overview" + ], + "dataResponsesMs": 910, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 736, + "cls": 0.01717699266975309, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.5, + "domInteractiveMs": 182.90000000223517, + "durationMs": 465.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/uptime", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 808, + "requiredReads": [ + "/api/uptime/overview" + ], + "dataResponsesMs": 916, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 748, + "cls": 0.08757044687499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.600000001490116, + "domInteractiveMs": 181.09999999776483, + "durationMs": 454.79999999701977 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/traffic", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 541, + "requiredReads": [ + "/api/traffic/servers" + ], + "dataResponsesMs": 895, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 908, + "cls": 0.025901459757486978, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 90.80000000074506, + "domInteractiveMs": 218.69999999925494, + "durationMs": 509.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/traffic", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 572, + "requiredReads": [ + "/api/traffic/servers" + ], + "dataResponsesMs": 930, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 588, + "cls": 0.11093063403320312, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.399999998509884, + "domInteractiveMs": 218.5, + "durationMs": 566.6999999992549 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/statistics", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 536, + "requiredReads": [ + "/api/statistics" + ], + "dataResponsesMs": 900, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 676, + "cls": 0.05435100839120371, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.5, + "domInteractiveMs": 201.29999999701977, + "durationMs": 514.0999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/statistics", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 516, + "requiredReads": [ + "/api/statistics" + ], + "dataResponsesMs": 616, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 620, + "cls": 0.07823437187499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 48.5, + "domInteractiveMs": 177.20000000298023, + "durationMs": 449.9000000022352 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/wireguard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 548, + "requiredReads": [ + "/api/wireguard" + ], + "dataResponsesMs": 900, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 820, + "cls": 0.00016905755690586422, + "longTasks": 1, + "longTaskMs": 54, + "ttfbMs": 88.60000000149012, + "domInteractiveMs": 221.59999999776483, + "durationMs": 433.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/wireguard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 448, + "requiredReads": [ + "/api/wireguard" + ], + "dataResponsesMs": 807, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 400, + "cls": 0.00063807578125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.399999998509884, + "domInteractiveMs": 136.80000000074506, + "durationMs": 341.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ipsec", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 621, + "requiredReads": [ + "/api/ipsec" + ], + "dataResponsesMs": 2492, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 1804, + "cls": 0.00006102382330246914, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 95.5, + "domInteractiveMs": 226.90000000223517, + "durationMs": 654.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ipsec", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 516, + "requiredReads": [ + "/api/ipsec" + ], + "dataResponsesMs": 1378, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 504, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 65.39999999850988, + "domInteractiveMs": 203.30000000074506, + "durationMs": 432.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/gre", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 553, + "requiredReads": [ + "/api/gre/tunnels" + ], + "dataResponsesMs": 1409, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 524, + "cls": 0.06258026005497686, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 108.19999999925494, + "domInteractiveMs": 243.70000000298023, + "durationMs": 460.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/gre", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 484, + "requiredReads": [ + "/api/gre/tunnels" + ], + "dataResponsesMs": 2342, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 488, + "cls": 0.10702192187500001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 48.899999998509884, + "domInteractiveMs": 133.89999999850988, + "durationMs": 448.69999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/vxlan", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 564, + "requiredReads": [ + "/api/vxlan" + ], + "dataResponsesMs": 929, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 528, + "cls": 0.12087190923996914, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 106.80000000074506, + "domInteractiveMs": 244.5, + "durationMs": 446.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/vxlan", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 498, + "requiredReads": [ + "/api/vxlan" + ], + "dataResponsesMs": 854, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 444, + "cls": 0.24465938125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.5, + "domInteractiveMs": 184.19999999925494, + "durationMs": 368.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ospf", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 544, + "requiredReads": [ + "/api/ospf/catalog" + ], + "dataResponsesMs": 2408, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 544, + "cls": 0.0054106802782600315, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 95.80000000074506, + "domInteractiveMs": 225.70000000298023, + "durationMs": 637.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ospf", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 503, + "requiredReads": [ + "/api/ospf/catalog" + ], + "dataResponsesMs": 2382, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 444, + "cls": 0.019630139453125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.900000002235174, + "domInteractiveMs": 178.80000000074506, + "durationMs": 421.0999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/bgp", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 616, + "requiredReads": [ + "/api/bgp/catalog" + ], + "dataResponsesMs": 968, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 612, + "cls": 0.008227206790123459, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 106.70000000298023, + "domInteractiveMs": 275.9000000022352, + "durationMs": 549.1999999992549 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/bgp", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 478, + "requiredReads": [ + "/api/bgp/catalog" + ], + "dataResponsesMs": 1363, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 1228, + "cls": 0.043276721875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.399999998509884, + "domInteractiveMs": 156, + "durationMs": 394.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/filters", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 770, + "requiredReads": [ + "/api/filters/rules" + ], + "dataResponsesMs": 770, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 700, + "cls": 0.036411010079089506, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 109.39999999850988, + "domInteractiveMs": 247.5, + "durationMs": 474.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/filters", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 609, + "requiredReads": [ + "/api/filters/rules" + ], + "dataResponsesMs": 609, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 580, + "cls": 0.073081659375, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.30000000074506, + "domInteractiveMs": 184.19999999925494, + "durationMs": 407.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/backups", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1066, + "requiredReads": [ + "/api/backups" + ], + "dataResponsesMs": 1066, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 652, + "cls": 0.000029851948302469136, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 94.30000000074506, + "domInteractiveMs": 237.69999999925494, + "durationMs": 623.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/backups", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 462, + "requiredReads": [ + "/api/backups" + ], + "dataResponsesMs": 815, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 420, + "cls": 0.1396459982421875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.30000000074506, + "domInteractiveMs": 142.5, + "durationMs": 394.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/certificates", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 536, + "requiredReads": [ + "/api/certificates/state" + ], + "dataResponsesMs": 2389, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 804, + "cls": 0.06154219907407407, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.100000001490116, + "domInteractiveMs": 200.30000000074506, + "durationMs": 442.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/certificates", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 508, + "requiredReads": [ + "/api/certificates/state" + ], + "dataResponsesMs": 2377, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 432, + "cls": 0.060179696874999994, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.5, + "domInteractiveMs": 181.39999999850988, + "durationMs": 371.3999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/containers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 615, + "requiredReads": [ + "/api/containers/state" + ], + "dataResponsesMs": 2494, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 612, + "cls": 0.11558015046296297, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 119.5, + "domInteractiveMs": 282.30000000074506, + "durationMs": 490.69999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/containers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 494, + "requiredReads": [ + "/api/containers/state" + ], + "dataResponsesMs": 2352, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 464, + "cls": 0.11563842187500001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.29999999701977, + "domInteractiveMs": 179.69999999925494, + "durationMs": 386.3999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/alerts", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 617, + "requiredReads": [ + "/api/alerts" + ], + "dataResponsesMs": 970, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 972, + "cls": 0.2825991898148148, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 88, + "domInteractiveMs": 277.69999999925494, + "durationMs": 480.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/alerts", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 472, + "requiredReads": [ + "/api/alerts" + ], + "dataResponsesMs": 842, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 808, + "cls": 0.67622226875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.69999999925494, + "domInteractiveMs": 148.19999999925494, + "durationMs": 367.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/settings", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 539, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 496, + "cls": 0.0034562909613715283, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.400000002235174, + "domInteractiveMs": 221.60000000149012, + "durationMs": 397.4000000022352 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/settings", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 488, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 424, + "cls": 0.0003595375, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 59.69999999925494, + "domInteractiveMs": 168.19999999925494, + "durationMs": 342.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/releases", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 557, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 504, + "cls": 0.000013114872685185185, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 105.30000000074506, + "domInteractiveMs": 234.19999999925494, + "durationMs": 408.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/releases", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 576, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 496, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 66.09999999776483, + "domInteractiveMs": 238.80000000074506, + "durationMs": 412 + }, + "violations": [], + "auditCompleted": true + } + ], + "blockedWrites": [], + "bounds": [ + "Default rendered state only; data can still be refreshing", + "Synthetic lab timing over SSH, no CPU/network throttling; not field Core Web Vitals", + "Automated WCAG-tagged checks, not manual screen-reader certification" + ], + "completed": true, + "passed": true, + "phases": [ + { + "report": "ui260-first-recheck.json", + "cases": 72, + "violating": 2, + "frontend": "ux-accessibility260" + }, + { + "report": "ui260-servers-final.json", + "cases": 4, + "violating": 0, + "frontend": "ux-accessibility260-final", + "onlyProductChange": "TypeBadge text-foreground" + } + ], + "coverageNote": "72 unique states accepted across two rechecks; four server cases replace prior results, not added as distinct states" +} diff --git a/docs/ux-ui/acceptance/ui260-first-recheck.json b/docs/ux-ui/acceptance/ui260-first-recheck.json new file mode 100644 index 00000000..b4dc8de4 --- /dev/null +++ b/docs/ux-ui/acceptance/ui260-first-recheck.json @@ -0,0 +1,2132 @@ +{ + "date": "2026-10-15", + "block": "12.5.260", + "origin": "http://localhost:3000", + "realPortalUiLogin": true, + "responseMocks": false, + "deviceWritesPermitted": false, + "cases": [ + { + "theme": "dark", + "path": "/dashboard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1243, + "requiredReads": [ + "/api/servers", + "/api/uptime/overview", + "/api/filters/rules", + "/api/bgp/catalog", + "/api/internet-path/latest", + "/api/filters/gre-tunnels", + "/api/wireguard", + "/api/traffic/servers", + "/api/vxlan" + ], + "dataResponsesMs": 3114, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 876, + "cls": 0.18891521697621286, + "longTasks": 1, + "longTaskMs": 106, + "ttfbMs": 101.19999999925494, + "domInteractiveMs": 390.6000000014901, + "durationMs": 745.3000000007451 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/dashboard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 469, + "requiredReads": [ + "/api/servers", + "/api/uptime/overview", + "/api/filters/rules", + "/api/bgp/catalog", + "/api/internet-path/latest", + "/api/filters/gre-tunnels", + "/api/wireguard", + "/api/traffic/servers", + "/api/vxlan" + ], + "dataResponsesMs": 2342, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 432, + "cls": 0.071583796875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 57.600000001490116, + "domInteractiveMs": 148.5, + "durationMs": 404.4000000022352 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/servers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 502, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 865, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 628, + "cls": 0.000044211847752700614, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 53, + "domInteractiveMs": 187.80000000074506, + "durationMs": 415.30000000074506 + }, + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "targets": [ + [ + "tr[data-row-id=\"1\"] > td:nth-child(2) > .text-success-foreground.px-1\\.25.h-5" + ], + [ + "tr[data-row-id=\"2\"] > td:nth-child(2) > .text-success-foreground.px-1\\.25.h-5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/servers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 468, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 836, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 464, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 57.5, + "domInteractiveMs": 151.80000000074506, + "durationMs": 416.70000000298023 + }, + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "targets": [ + [ + "tr[data-row-id=\"1\"] > td:nth-child(2) > .text-success-foreground.px-1\\.25.h-5" + ], + [ + "tr[data-row-id=\"2\"] > td:nth-child(2) > .text-success-foreground.px-1\\.25.h-5" + ] + ] + } + ], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/uptime", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 816, + "requiredReads": [ + "/api/uptime/overview" + ], + "dataResponsesMs": 1180, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 796, + "cls": 0.01717699266975309, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 53.899999998509884, + "domInteractiveMs": 192.5, + "durationMs": 412.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/uptime", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 890, + "requiredReads": [ + "/api/uptime/overview" + ], + "dataResponsesMs": 1246, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 880, + "cls": 0.08757044687499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 97.29999999701977, + "domInteractiveMs": 247.39999999850988, + "durationMs": 517.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/traffic", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 568, + "requiredReads": [ + "/api/traffic/servers" + ], + "dataResponsesMs": 921, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 1056, + "cls": 0.007212276415412808, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 92, + "domInteractiveMs": 226.5, + "durationMs": 540.6999999992549 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/traffic", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 543, + "requiredReads": [ + "/api/traffic/servers" + ], + "dataResponsesMs": 896, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 556, + "cls": 0.11047610429687499, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.20000000298023, + "domInteractiveMs": 199.60000000149012, + "durationMs": 526.9000000022352 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/statistics", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 551, + "requiredReads": [ + "/api/statistics" + ], + "dataResponsesMs": 911, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 720, + "cls": 0.05435100839120371, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.5, + "domInteractiveMs": 189.20000000298023, + "durationMs": 533.2000000029802 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/statistics", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 531, + "requiredReads": [ + "/api/statistics" + ], + "dataResponsesMs": 641, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 620, + "cls": 0.07823437187499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 51.100000001490116, + "domInteractiveMs": 178.80000000074506, + "durationMs": 462.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/wireguard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 562, + "requiredReads": [ + "/api/wireguard" + ], + "dataResponsesMs": 918, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 836, + "cls": 0.00016905755690586422, + "longTasks": 1, + "longTaskMs": 58, + "ttfbMs": 87, + "domInteractiveMs": 235, + "durationMs": 452.69999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/wireguard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 473, + "requiredReads": [ + "/api/wireguard" + ], + "dataResponsesMs": 830, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 460, + "cls": 0.00063807578125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 65.69999999925494, + "domInteractiveMs": 156.10000000149012, + "durationMs": 396.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ipsec", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 587, + "requiredReads": [ + "/api/ipsec" + ], + "dataResponsesMs": 1449, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 980, + "cls": 0.00006102382330246914, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 102.80000000074506, + "domInteractiveMs": 233.69999999925494, + "durationMs": 519.3000000007451 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ipsec", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 958, + "requiredReads": [ + "/api/ipsec" + ], + "dataResponsesMs": 958, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 540, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.69999999925494, + "domInteractiveMs": 141.79999999701977, + "durationMs": 446.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/gre", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 574, + "requiredReads": [ + "/api/gre/tunnels" + ], + "dataResponsesMs": 950, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 552, + "cls": 0.06258026005497686, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 124.40000000223517, + "domInteractiveMs": 253.20000000298023, + "durationMs": 474.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/gre", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 478, + "requiredReads": [ + "/api/gre/tunnels" + ], + "dataResponsesMs": 2353, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 476, + "cls": 0.10702192187500001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.20000000298023, + "domInteractiveMs": 158, + "durationMs": 407.4000000022352 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/vxlan", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 581, + "requiredReads": [ + "/api/vxlan" + ], + "dataResponsesMs": 948, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 556, + "cls": 0.12087190923996914, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 110.89999999850988, + "domInteractiveMs": 249.19999999925494, + "durationMs": 457.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/vxlan", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 452, + "requiredReads": [ + "/api/vxlan" + ], + "dataResponsesMs": 1315, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 444, + "cls": 0.24465938125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 58.69999999925494, + "domInteractiveMs": 150.30000000074506, + "durationMs": 383.5999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ospf", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 571, + "requiredReads": [ + "/api/ospf/catalog" + ], + "dataResponsesMs": 1434, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 540, + "cls": 0.0054106802782600315, + "longTasks": 1, + "longTaskMs": 50, + "ttfbMs": 105.90000000223517, + "domInteractiveMs": 244.89999999850988, + "durationMs": 470.29999999701977 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/ospf", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 485, + "requiredReads": [ + "/api/ospf/catalog" + ], + "dataResponsesMs": 2343, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 444, + "cls": 0.019630139453125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.80000000074506, + "domInteractiveMs": 147.5, + "durationMs": 403.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/bgp", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 626, + "requiredReads": [ + "/api/bgp/catalog" + ], + "dataResponsesMs": 2508, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 628, + "cls": 0.008227206790123459, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 109.10000000149012, + "domInteractiveMs": 242.89999999850988, + "durationMs": 571.3000000007451 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/bgp", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1032, + "requiredReads": [ + "/api/bgp/catalog" + ], + "dataResponsesMs": 1033, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 764, + "cls": 0.043276721875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.400000002235174, + "domInteractiveMs": 178.30000000074506, + "durationMs": 471.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/filters", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 875, + "requiredReads": [ + "/api/filters/rules" + ], + "dataResponsesMs": 875, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 816, + "cls": 0.036411010079089506, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 166.10000000149012, + "domInteractiveMs": 310.20000000298023, + "durationMs": 603.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/filters", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 516, + "requiredReads": [ + "/api/filters/rules" + ], + "dataResponsesMs": 516, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 524, + "cls": 0.073080721875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.100000001490116, + "domInteractiveMs": 163.89999999850988, + "durationMs": 382 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/backups", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 520, + "requiredReads": [ + "/api/backups" + ], + "dataResponsesMs": 877, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 500, + "cls": 0.000029851948302469136, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 96.5, + "domInteractiveMs": 191.80000000074506, + "durationMs": 421.3999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/backups", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 501, + "requiredReads": [ + "/api/backups" + ], + "dataResponsesMs": 601, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 468, + "cls": 0.1396483580078125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 54.899999998509884, + "domInteractiveMs": 181.09999999776483, + "durationMs": 457.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/certificates", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 546, + "requiredReads": [ + "/api/certificates/state" + ], + "dataResponsesMs": 2408, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 856, + "cls": 0.062860234375, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 53.099999997764826, + "domInteractiveMs": 203.5, + "durationMs": 428.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/certificates", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 520, + "requiredReads": [ + "/api/certificates/state" + ], + "dataResponsesMs": 2416, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 520, + "cls": 0.061482699999999994, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.19999999925494, + "domInteractiveMs": 176.89999999850988, + "durationMs": 450.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/containers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 548, + "requiredReads": [ + "/api/containers/state" + ], + "dataResponsesMs": 2414, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 544, + "cls": 0.11558015046296297, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 109.20000000298023, + "domInteractiveMs": 239.30000000074506, + "durationMs": 441.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/containers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 502, + "requiredReads": [ + "/api/containers/state" + ], + "dataResponsesMs": 2361, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 456, + "cls": 0.11563842187500001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50, + "domInteractiveMs": 173.80000000074506, + "durationMs": 391.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/alerts", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 581, + "requiredReads": [ + "/api/alerts" + ], + "dataResponsesMs": 942, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 960, + "cls": 0.2825991898148148, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 94.40000000223517, + "domInteractiveMs": 226.10000000149012, + "durationMs": 504.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/alerts", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 510, + "requiredReads": [ + "/api/alerts" + ], + "dataResponsesMs": 1381, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 948, + "cls": 0.67899836875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.19999999925494, + "domInteractiveMs": 180.09999999776483, + "durationMs": 347.69999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/settings", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 620, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 620, + "cls": 0.0035598427553530094, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 117.5, + "domInteractiveMs": 269.5, + "durationMs": 509.0999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/settings", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 531, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 480, + "cls": 0.0003595375, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 65.09999999776483, + "domInteractiveMs": 216.60000000149012, + "durationMs": 391 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/releases", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 743, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 696, + "cls": 0.000013114872685185185, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 278.5, + "domInteractiveMs": 428.29999999701977, + "durationMs": 603.2999999970198 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/releases", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 530, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 464, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 62.5, + "domInteractiveMs": 217.09999999776483, + "durationMs": 386.5999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/dashboard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 588, + "requiredReads": [ + "/api/servers", + "/api/uptime/overview", + "/api/filters/rules", + "/api/bgp/catalog", + "/api/internet-path/latest", + "/api/filters/gre-tunnels", + "/api/wireguard", + "/api/traffic/servers", + "/api/vxlan" + ], + "dataResponsesMs": 3482, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 596, + "cls": 0.11174993919843508, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 97.29999999701977, + "domInteractiveMs": 239.5, + "durationMs": 553 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/dashboard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 607, + "requiredReads": [ + "/api/servers", + "/api/uptime/overview", + "/api/filters/rules", + "/api/bgp/catalog", + "/api/internet-path/latest", + "/api/filters/gre-tunnels", + "/api/wireguard", + "/api/traffic/servers", + "/api/vxlan" + ], + "dataResponsesMs": 2484, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 556, + "cls": 0.071302821875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 66.80000000074506, + "domInteractiveMs": 167.69999999925494, + "durationMs": 490.3999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/servers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 534, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 635, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 608, + "cls": 0.000044211847752700614, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 58, + "domInteractiveMs": 218.59999999776483, + "durationMs": 449.79999999701977 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/servers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 453, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 559, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 396, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 48.899999998509884, + "domInteractiveMs": 136.30000000074506, + "durationMs": 375.70000000298023 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/uptime", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 804, + "requiredReads": [ + "/api/uptime/overview" + ], + "dataResponsesMs": 910, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 736, + "cls": 0.01717699266975309, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.5, + "domInteractiveMs": 182.90000000223517, + "durationMs": 465.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/uptime", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 808, + "requiredReads": [ + "/api/uptime/overview" + ], + "dataResponsesMs": 916, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 748, + "cls": 0.08757044687499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.600000001490116, + "domInteractiveMs": 181.09999999776483, + "durationMs": 454.79999999701977 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/traffic", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 541, + "requiredReads": [ + "/api/traffic/servers" + ], + "dataResponsesMs": 895, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 908, + "cls": 0.025901459757486978, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 90.80000000074506, + "domInteractiveMs": 218.69999999925494, + "durationMs": 509.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/traffic", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 572, + "requiredReads": [ + "/api/traffic/servers" + ], + "dataResponsesMs": 930, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 588, + "cls": 0.11093063403320312, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.399999998509884, + "domInteractiveMs": 218.5, + "durationMs": 566.6999999992549 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/statistics", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 536, + "requiredReads": [ + "/api/statistics" + ], + "dataResponsesMs": 900, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 676, + "cls": 0.05435100839120371, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.5, + "domInteractiveMs": 201.29999999701977, + "durationMs": 514.0999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/statistics", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 516, + "requiredReads": [ + "/api/statistics" + ], + "dataResponsesMs": 616, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 620, + "cls": 0.07823437187499999, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 48.5, + "domInteractiveMs": 177.20000000298023, + "durationMs": 449.9000000022352 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/wireguard", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 548, + "requiredReads": [ + "/api/wireguard" + ], + "dataResponsesMs": 900, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 820, + "cls": 0.00016905755690586422, + "longTasks": 1, + "longTaskMs": 54, + "ttfbMs": 88.60000000149012, + "domInteractiveMs": 221.59999999776483, + "durationMs": 433.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/wireguard", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 448, + "requiredReads": [ + "/api/wireguard" + ], + "dataResponsesMs": 807, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 400, + "cls": 0.00063807578125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.399999998509884, + "domInteractiveMs": 136.80000000074506, + "durationMs": 341.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ipsec", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 621, + "requiredReads": [ + "/api/ipsec" + ], + "dataResponsesMs": 2492, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 1804, + "cls": 0.00006102382330246914, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 95.5, + "domInteractiveMs": 226.90000000223517, + "durationMs": 654.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ipsec", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 516, + "requiredReads": [ + "/api/ipsec" + ], + "dataResponsesMs": 1378, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 504, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 65.39999999850988, + "domInteractiveMs": 203.30000000074506, + "durationMs": 432.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/gre", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 553, + "requiredReads": [ + "/api/gre/tunnels" + ], + "dataResponsesMs": 1409, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 524, + "cls": 0.06258026005497686, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 108.19999999925494, + "domInteractiveMs": 243.70000000298023, + "durationMs": 460.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/gre", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 484, + "requiredReads": [ + "/api/gre/tunnels" + ], + "dataResponsesMs": 2342, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 488, + "cls": 0.10702192187500001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 48.899999998509884, + "domInteractiveMs": 133.89999999850988, + "durationMs": 448.69999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/vxlan", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 564, + "requiredReads": [ + "/api/vxlan" + ], + "dataResponsesMs": 929, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 528, + "cls": 0.12087190923996914, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 106.80000000074506, + "domInteractiveMs": 244.5, + "durationMs": 446.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/vxlan", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 498, + "requiredReads": [ + "/api/vxlan" + ], + "dataResponsesMs": 854, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 444, + "cls": 0.24465938125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.5, + "domInteractiveMs": 184.19999999925494, + "durationMs": 368.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ospf", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 544, + "requiredReads": [ + "/api/ospf/catalog" + ], + "dataResponsesMs": 2408, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 544, + "cls": 0.0054106802782600315, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 95.80000000074506, + "domInteractiveMs": 225.70000000298023, + "durationMs": 637.6000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/ospf", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 503, + "requiredReads": [ + "/api/ospf/catalog" + ], + "dataResponsesMs": 2382, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 444, + "cls": 0.019630139453125, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.900000002235174, + "domInteractiveMs": 178.80000000074506, + "durationMs": 421.0999999977648 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/bgp", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 616, + "requiredReads": [ + "/api/bgp/catalog" + ], + "dataResponsesMs": 968, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 612, + "cls": 0.008227206790123459, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 106.70000000298023, + "domInteractiveMs": 275.9000000022352, + "durationMs": 549.1999999992549 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/bgp", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 478, + "requiredReads": [ + "/api/bgp/catalog" + ], + "dataResponsesMs": 1363, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 1228, + "cls": 0.043276721875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.399999998509884, + "domInteractiveMs": 156, + "durationMs": 394.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/filters", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 770, + "requiredReads": [ + "/api/filters/rules" + ], + "dataResponsesMs": 770, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 700, + "cls": 0.036411010079089506, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 109.39999999850988, + "domInteractiveMs": 247.5, + "durationMs": 474.30000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/filters", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 609, + "requiredReads": [ + "/api/filters/rules" + ], + "dataResponsesMs": 609, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 580, + "cls": 0.073081659375, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.30000000074506, + "domInteractiveMs": 184.19999999925494, + "durationMs": 407.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/backups", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1066, + "requiredReads": [ + "/api/backups" + ], + "dataResponsesMs": 1066, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 652, + "cls": 0.000029851948302469136, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 94.30000000074506, + "domInteractiveMs": 237.69999999925494, + "durationMs": 623.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/backups", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 462, + "requiredReads": [ + "/api/backups" + ], + "dataResponsesMs": 815, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 420, + "cls": 0.1396459982421875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.30000000074506, + "domInteractiveMs": 142.5, + "durationMs": 394.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/certificates", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 536, + "requiredReads": [ + "/api/certificates/state" + ], + "dataResponsesMs": 2389, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 804, + "cls": 0.06154219907407407, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 56.100000001490116, + "domInteractiveMs": 200.30000000074506, + "durationMs": 442.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/certificates", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 508, + "requiredReads": [ + "/api/certificates/state" + ], + "dataResponsesMs": 2377, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 432, + "cls": 0.060179696874999994, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 52.5, + "domInteractiveMs": 181.39999999850988, + "durationMs": 371.3999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/containers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 615, + "requiredReads": [ + "/api/containers/state" + ], + "dataResponsesMs": 2494, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 612, + "cls": 0.11558015046296297, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 119.5, + "domInteractiveMs": 282.30000000074506, + "durationMs": 490.69999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/containers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 494, + "requiredReads": [ + "/api/containers/state" + ], + "dataResponsesMs": 2352, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 464, + "cls": 0.11563842187500001, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.29999999701977, + "domInteractiveMs": 179.69999999925494, + "durationMs": 386.3999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/alerts", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 617, + "requiredReads": [ + "/api/alerts" + ], + "dataResponsesMs": 970, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 972, + "cls": 0.2825991898148148, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 88, + "domInteractiveMs": 277.69999999925494, + "durationMs": 480.80000000074506 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/alerts", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 472, + "requiredReads": [ + "/api/alerts" + ], + "dataResponsesMs": 842, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 808, + "cls": 0.67622226875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 49.69999999925494, + "domInteractiveMs": 148.19999999925494, + "durationMs": 367.19999999925494 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/settings", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 539, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 496, + "cls": 0.0034562909613715283, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 50.400000002235174, + "domInteractiveMs": 221.60000000149012, + "durationMs": 397.4000000022352 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/settings", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 488, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 424, + "cls": 0.0003595375, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 59.69999999925494, + "domInteractiveMs": 168.19999999925494, + "durationMs": 342.8999999985099 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/releases", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 557, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 504, + "cls": 0.000013114872685185185, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 105.30000000074506, + "domInteractiveMs": 234.19999999925494, + "durationMs": 408.1000000014901 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/releases", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 576, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 496, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 66.09999999776483, + "domInteractiveMs": 238.80000000074506, + "durationMs": 412 + }, + "violations": [], + "auditCompleted": true + } + ], + "blockedWrites": [], + "bounds": [ + "Default rendered state only; data can still be refreshing", + "Synthetic lab timing over SSH, no CPU/network throttling; not field Core Web Vitals", + "Automated WCAG-tagged checks, not manual screen-reader certification" + ], + "completed": true, + "passed": false +} diff --git a/docs/ux-ui/acceptance/ui260-servers-final.json b/docs/ux-ui/acceptance/ui260-servers-final.json new file mode 100644 index 00000000..60f79d45 --- /dev/null +++ b/docs/ux-ui/acceptance/ui260-servers-final.json @@ -0,0 +1,135 @@ +{ + "date": "2026-10-15", + "block": "12.5.260", + "origin": "http://localhost:3000", + "realPortalUiLogin": true, + "responseMocks": false, + "deviceWritesPermitted": false, + "cases": [ + { + "theme": "dark", + "path": "/servers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 760, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 1639, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 1232, + "cls": 0.000044211847752700614, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 144.09999999776483, + "domInteractiveMs": 413.5, + "durationMs": 740.3000000007451 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "dark", + "path": "/servers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 480, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 585, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "dark", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 452, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 55.30000000074506, + "domInteractiveMs": 157.19999999925494, + "durationMs": 389.29999999701977 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/servers", + "width": 1440, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1150, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 1150, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 988, + "cls": 0.000044211847752700614, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 115.19999999925494, + "domInteractiveMs": 323.6000000014901, + "durationMs": 730.5 + }, + "violations": [], + "auditCompleted": true + }, + { + "theme": "light", + "path": "/servers", + "width": 320, + "pageErrors": [], + "apiErrors": [], + "mainVisibleMs": 1052, + "requiredReads": [ + "/api/servers" + ], + "dataResponsesMs": 1052, + "layout": { + "overflow": false, + "mainOverflow": false, + "theme": "light", + "pendingSkeletons": 0 + }, + "performance": { + "lcp": 632, + "cls": 0.000062321875, + "longTasks": 0, + "longTaskMs": 0, + "ttfbMs": 63, + "domInteractiveMs": 236, + "durationMs": 567 + }, + "violations": [], + "auditCompleted": true + } + ], + "blockedWrites": [], + "readOnlyPosts": [], + "bounds": [ + "Default rendered state only; data can still be refreshing", + "Synthetic lab timing over SSH, no CPU/network throttling; not field Core Web Vitals", + "Automated WCAG-tagged checks, not manual screen-reader certification" + ], + "completed": true, + "passed": true +} diff --git a/docs/ux-ui/acceptance/ui260-summary.json b/docs/ux-ui/acceptance/ui260-summary.json new file mode 100644 index 00000000..f226af55 --- /dev/null +++ b/docs/ux-ui/acceptance/ui260-summary.json @@ -0,0 +1,62 @@ +{ + "date": "2026-10-15", + "block": "12.5.260", + "runtime": { + "backend": "ux-certificates258", + "frontend": "ux-accessibility260-final" + }, + "baseline": { + "cases": 72, + "violatingCases": 23, + "documentOverflow": 0, + "internalBgpOverflowCases": 2 + }, + "final": { + "cases": 72, + "axeViolations": 0, + "documentOverflow": 0, + "mainOverflow": 0, + "pageErrors": 0, + "apiErrors": 0, + "blockedWrites": 0, + "pendingSkeletonCases": 0 + }, + "timings": { + "mainVisible": { + "count": 72, + "p50Ms": 548, + "p95Ms": 1052, + "maxMs": 1243 + }, + "requiredApiResponses": { + "count": 64, + "p50Ms": 1033, + "p95Ms": 2494, + "maxMs": 3482 + } + }, + "local": { + "initialE2E": { + "passed": 55, + "failed": 2 + }, + "afterFocusRemountFix": { + "passed": 6, + "failed": 0 + }, + "finalSharedCases": { + "passed": 4, + "failed": 0 + }, + "uniqueCasesThatPassed": 59, + "typecheck": "passed", + "scopedLint": "passed", + "productionBuild": "passed", + "sourceManifestMatched": 528 + }, + "bounds": [ + "Default rendered state only; data can still be refreshing", + "Synthetic lab timing over SSH, no CPU/network throttling; not field Core Web Vitals", + "Automated WCAG-tagged checks, not manual screen-reader certification" + ] +} diff --git a/docs/ux-ui/screens/ui260-bgp.png b/docs/ux-ui/screens/ui260-bgp.png new file mode 100644 index 00000000..cfcd121f Binary files /dev/null and b/docs/ux-ui/screens/ui260-bgp.png differ diff --git a/docs/ux-ui/screens/ui260-servers.png b/docs/ux-ui/screens/ui260-servers.png new file mode 100644 index 00000000..4112f41e Binary files /dev/null and b/docs/ux-ui/screens/ui260-servers.png differ diff --git a/tests/e2e/server-rail-accessibility.spec.ts b/tests/e2e/server-rail-accessibility.spec.ts new file mode 100644 index 00000000..13e391eb --- /dev/null +++ b/tests/e2e/server-rail-accessibility.spec.ts @@ -0,0 +1,68 @@ +import { test, expect } from "@playwright/test" +import axe from "axe-core" +import { serverReadSchema } from "../../packages/contracts/src/servers" + +for (const theme of ["light", "dark"]) test(`Server type badges: all types have readable contrast/${theme}`, async ({ page }) => { + await page.addInitScript(value => localStorage.setItem("rl-theme", value), theme) + await page.route("**/health", route => route.fulfill({ json: { ok: true } })) + const servers = ["home-router", "exit-node", "jump-host"].map((type, i) => serverReadSchema.parse({ + id: i + 1, name: `Контраст ${i + 1}`, host: `192.0.2.${i + 1}`, port: 443, + username: "fixture", password: "fixture", useSsl: true, verifySsl: true, type, site: "TEST", country: "RU", asn: "", comment: "", + enabled: true, lanSubnet: "", wanUplinks: [], createdAt: "2026-10-15T00:00:00Z", updatedAt: "2026-10-15T00:00:00Z", + status: "online", latency: 1, os: "7.24.5", model: "CHR", uptime: null, cpuLoad: null, + freeMemory: null, totalMemory: null, identityName: null, sessions: 0, polledAt: null, + })) + await page.route("**/api/**", route => route.fulfill({ json: new URL(route.request().url()).pathname === "/api/servers" ? servers + : new URL(route.request().url()).pathname === "/api/auth/config" ? { required: false } : {} })) + await page.goto("/servers") + for (const name of ["Home Router", "Exit Node", "JumpHost"]) await expect(page.getByRole("cell", { name, exact: true })).toBeVisible() + await page.addScriptTag({ content: axe.source }) + const violations = await page.evaluate(async () => (await (window as unknown as { axe: typeof axe }).axe.run("#main-content", { + runOnly: { type: "rule", values: ["color-contrast"] }, + })).violations.map(v => ({ id: v.id, targets: v.nodes.map(n => n.target) }))) + expect(violations).toEqual([]) +}) + +for (const theme of ["light", "dark"]) test(`Server rail: keyboard selection across pages and target sizes/${theme}`, async ({ page }) => { + await page.addInitScript(value => localStorage.setItem("rl-theme", value), theme) + await page.route("**/health", route => route.fulfill({ json: { ok: true } })) + await page.route("**/api/**", route => { + const path = new URL(route.request().url()).pathname + return route.fulfill({ json: path === "/api/auth/config" ? { required: false } + : path === "/api/servers" ? Array.from({ length: 61 }, (_, i) => ({ + id: i + 1, name: `Роутер ${String(i + 1).padStart(3, "0")}`, host: `192.0.2.${i + 1}`, + enabled: true, status: "online", type: "home-router", country: "RU", + })) : path === "/api/gre/tunnels" ? { tunnels: [], failures: [] } : {} }) + }) + await page.goto("/gre") + const rail = page.getByRole("listbox", { name: "Серверы", exact: true }) + const all = rail.getByRole("option", { name: /Все серверы/ }) + await expect(all).toHaveAttribute("aria-selected", "true") + await all.focus() + await page.keyboard.press("ArrowDown") + const first = rail.getByRole("option", { name: /Роутер 001/ }) + await expect(first).toBeFocused() + await expect(first).toHaveAttribute("aria-selected", "true") + await expect(page).toHaveURL(/server=1(?:&|$)/) + await page.keyboard.press("End") + const last = rail.getByRole("option", { name: /Роутер 061/ }) + await expect(last).toBeFocused() + await expect(last).toHaveAttribute("aria-selected", "true") + await expect(last).toBeInViewport() + await expect(page).toHaveURL(/server=61(?:&|$)/) + await page.keyboard.press("ArrowUp") + await expect(rail.getByRole("option", { name: /Роутер 060/ })).toBeFocused() + await page.keyboard.press("Home") + await expect(all).toBeFocused() + await expect(all).toHaveAttribute("aria-selected", "true") + await page.keyboard.press("Tab") + await expect.poll(() => rail.evaluate(el => el.contains(document.activeElement))).toBe(false) + await page.addScriptTag({ content: axe.source }) + const violations = await page.evaluate(async () => { + const engine = (window as unknown as { axe: typeof axe }).axe + return (await engine.run(document.querySelector('[role="listbox"][aria-label="Серверы"]')!, { + runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"] }, + })).violations.map(v => ({ id: v.id, targets: v.nodes.map(n => n.target) })) + }) + expect(violations).toEqual([]) +})