From 21dc646553dbc820210dd8fd7de2c3285e0e98fb Mon Sep 17 00:00:00 2001 From: Denozordec Date: Sun, 4 Oct 2026 16:45:39 +0700 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=D0=BE=D0=B3=D1=80=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=87=D0=B8=D1=82=D1=8C=20=D1=82=D1=80=D0=B0=D1=81=D1=81=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=BA=D1=83=20=D0=B8=20=D0=B7=D0=B0=D1=89?= =?UTF-8?q?=D0=B8=D1=82=D0=B8=D1=82=D1=8C=20=D0=BF=D0=BE=D0=B2=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=BD=D1=83=D1=8E=20=D1=81=D0=B8=D0=BC=D1=83=D0=BB=D1=8F?= =?UTF-8?q?=D1=86=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(main)/firewall/page.tsx | 63 +++++++++++++----- docs/ux-ui-performance.md | 2 + docs/ux-ui-plan.md | 2 +- docs/ux-ui-progress.md | 8 +++ docs/ux-ui-stage-12.md | 9 +++ .../screens/large-firewall-trace-320.png | Bin 0 -> 58676 bytes tests/e2e/large-firewall-scenario.spec.ts | 30 +++++++++ 7 files changed, 96 insertions(+), 18 deletions(-) create mode 100644 docs/ux-ui/screens/large-firewall-trace-320.png diff --git a/app/(main)/firewall/page.tsx b/app/(main)/firewall/page.tsx index 27eae6b4..9c3798ae 100644 --- a/app/(main)/firewall/page.tsx +++ b/app/(main)/firewall/page.tsx @@ -988,7 +988,7 @@ function SimStepRow({ step, index }: { step: SimStep; index: number }) { type="button" onClick={() => isClickable && setExpanded(e => !e)} className={cn( - "w-full flex items-center gap-3 px-5 py-2.5 text-left transition-colors", + "w-full min-h-11 flex flex-wrap sm:flex-nowrap items-center gap-3 px-5 py-2.5 text-left transition-colors", isClickable ? "hover:bg-muted/30 cursor-pointer" : "cursor-default", step.status === "pending" && "opacity-40", step.status === "disabled" && "opacity-25", @@ -999,7 +999,7 @@ function SimStepRow({ step, index }: { step: SimStep; index: number }) {
- + {step.rule.src || "any"} → {step.rule.dst || "any"} {step.rule.proto && step.rule.proto !== "all" && ( {step.rule.proto} @@ -1015,7 +1015,7 @@ function SimStepRow({ step, index }: { step: SimStep; index: number }) { )} {/* Status badge */} -
+
{step.status === "pending" && ( ожидание )} @@ -1030,7 +1030,7 @@ function SimStepRow({ step, index }: { step: SimStep; index: number }) { )} {step.status === "passthrough" && ( - + passthrough )} @@ -1355,9 +1355,12 @@ function SimulatorTab({ rules: allRules }: { rules: FirewallRule[] }) { const [activePreset, setPreset] = useState("https") const [simState, setSimState] = useState<"idle" | "running" | "done">("idle") const [steps, setSteps] = useState([]) + const [tracePage, setTracePage] = useState(0) const [speed, setSpeed] = useState("normal") const [verdict, setVerdict] = useState<{ action: string; ruleIndex: number | null } | null>(null) const cancelRef = useRef(false) + const runEpoch = useRef(0) + useEffect(() => () => { runEpoch.current += 1; cancelRef.current = true }, []) // ── Scenarios state ───────────────────────────────────────────────────────── const [scenarios, setScenarios] = useState(() => { @@ -1392,9 +1395,11 @@ function SimulatorTab({ rules: allRules }: { rules: FirewallRule[] }) { // ── Handlers ──────────────────────────────────────────────────────────────── function resetSim() { + runEpoch.current += 1 cancelRef.current = true setSimState("idle") setSteps([]) + setTracePage(0) setVerdict(null) } @@ -1402,18 +1407,22 @@ function SimulatorTab({ rules: allRules }: { rules: FirewallRule[] }) { setActiveScenarioId(null) setPreset(p.id) setPacket({ ...DEFAULT_PKT, ...p.pkt }) + runEpoch.current += 1 cancelRef.current = true setSimState("idle") setSteps([]) + setTracePage(0) setVerdict(null) } function activateScenario(id: string | null) { setActiveScenarioId(id) setPreset(null) + runEpoch.current += 1 cancelRef.current = true setSimState("idle") setSteps([]) + setTracePage(0) setVerdict(null) if (id) { const sc = scenarios.find(s => s.id === id) @@ -1444,7 +1453,9 @@ function SimulatorTab({ rules: allRules }: { rules: FirewallRule[] }) { return } + const epoch = ++runEpoch.current cancelRef.current = false + setTracePage(0) setSimState("running") setVerdict(null) setSteps(rules.map((r, i) => ({ rule: r, index: i, status: "pending", checks: [] }))) @@ -1453,10 +1464,10 @@ function SimulatorTab({ rules: allRules }: { rules: FirewallRule[] }) { let finalVerdict: { action: string; ruleIndex: number | null } | null = null for (let i = 0; i < rules.length; i++) { - if (cancelRef.current) return + if (cancelRef.current || epoch !== runEpoch.current) return setSteps(prev => prev.map((s, idx) => idx === i ? { ...s, status: "evaluating" } : s)) if (delay > 0) await new Promise(res => setTimeout(res, delay)) - if (cancelRef.current) return + if (cancelRef.current || epoch !== runEpoch.current) return const result = evalRule(rules[i], packet) setSteps(prev => prev.map((s, idx) => @@ -1471,12 +1482,21 @@ function SimulatorTab({ rules: allRules }: { rules: FirewallRule[] }) { if (delay > 0) await new Promise(res => setTimeout(res, Math.max(delay * 0.25, 40))) } - if (!cancelRef.current) { + if (!cancelRef.current && epoch === runEpoch.current) { setVerdict(finalVerdict ?? { action: "accept (default)", ruleIndex: null }) setSimState("done") } } + const tracePageCount = Math.max(1, Math.ceil(steps.length / 50)) + const currentTracePage = Math.min(tracePage, tracePageCount - 1) + const traceOffset = currentTracePage * 50 + const traceChainCounts = useMemo(() => { + const counts = new Map() + for (const step of steps) counts.set(step.rule.chain, (counts.get(step.rule.chain) ?? 0) + 1) + return counts + }, [steps]) + const verdictColors = verdict ? getVerdictColors(verdict.action) : null return ( @@ -1690,7 +1710,7 @@ function SimulatorTab({ rules: allRules }: { rules: FirewallRule[] }) { {simState === "running" ? ( ) : ( @@ -1730,8 +1750,8 @@ function SimulatorTab({ rules: allRules }: { rules: FirewallRule[] }) { {/* ── Trace list ──────────────────────────────────────────────────────── */} {steps.length > 0 && ( - -
+ +

Трассировка · цепочка: {packet.chain} {activeScenario && ( @@ -1745,23 +1765,24 @@ function SimulatorTab({ rules: allRules }: { rules: FirewallRule[] }) {

- {steps.map((step, i) => { + {steps.slice(traceOffset, traceOffset + 50).map((step, pageIndex) => { + const i = traceOffset + pageIndex const prevChain = i > 0 ? steps[i - 1].rule.chain : null - const showHeader = step.rule.chain !== prevChain + const showHeader = pageIndex === 0 || step.rule.chain !== prevChain return ( -
+
{showHeader && (
- + {step.rule.chain} {CHAIN_TABLE_HINT[step.rule.chain] && ( - + · {CHAIN_TABLE_HINT[step.rule.chain]} )} - - {steps.filter(s => s.rule.chain === step.rule.chain).length} правил + + {traceChainCounts.get(step.rule.chain)} правил
)} @@ -1770,6 +1791,14 @@ function SimulatorTab({ rules: allRules }: { rules: FirewallRule[] }) { ) })}
+
+ Шаги {traceOffset + 1}–{Math.min(traceOffset + 50, steps.length)} из {steps.length} +
+ + + {verdict?.ruleIndex != null && } +
+
)} diff --git a/docs/ux-ui-performance.md b/docs/ux-ui-performance.md index 414e0ee5..78f2709a 100644 --- a/docs/ux-ui-performance.md +++ b/docs/ux-ui-performance.md @@ -87,3 +87,5 @@ Statistics pivot в backend/src/services/statistics-aggregate.ts огранич 12.3.44: stale операции Firewall заблокированы до успешного read/после retry, ограничения12.3.43 по полному GET failure исправлены.29+2 main/context4/type/build прошли;4 исходных ESLint errors страницы подтверждены сравнением с HEAD.11 manualPagination:true остаются. 12.3.45: локальный сценарий1000→50 rows/1847 modal nodes; pagination/full draft999/скрытый verdict#999/320px/axe проверены,6 browser/type/grid lint/build прошли. Трассировка999 остаётся15529 page nodes, требует12.3.46; ошибки записи localStorage не проверены.10 manualPagination:true остаются. + +12.3.46: trace999/15529→1293 page nodes,50 steps/whole verdict#999/страницы/jump/stop→restart epoch/reset/320px/axe проверены;6+1 browser/type/test lint/build прошли. Полный steps в памяти и O(N²) обновления instant не менялись; общий CPU performance не принят.10 manualPagination:true остаются. diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md index a80856be..b17da40c 100644 --- a/docs/ux-ui-plan.md +++ b/docs/ux-ui-plan.md @@ -4,7 +4,7 @@ ## Актуальная точка продолжения — 2026-10-04 -Этап 11Б завершён в границах локальной приёмки (11Б.7); внешний выпуск не проверен. 12.1 принят локально: доступность оболочки/клавиатура/health в описанных сценариях. 12.2.1 принят локально: strict BGP catalog/partial/nullable counters. 12.2.2 принят локально: strict traffic DTO и агрегация без выдуманных точек. 12.2.3 принят локально: observations для server traffic от backend до dashboard. 12.2.4 принят локально: strict servers/probes и unknown server status. 12.2.5 принят локально: реальные ping samples/unknown/ось RTT/Recharts. 12.2.6 принят локально: strict GRE/WireGuard/VXLAN и device failures. 12.2.7 принят локально: strict Internet path snapshot/fallback. 12.2.8 принят локально: подтверждение полного backend сбора. 12.2.9 принят локально: метрики участков и наблюдаемый WAN без эвристической подмены. 12.3.1 принят локально:1000 серверов/штатная пагинация. 12.3.2 принят локально: пагинация CompactDataGrid/1000 строк статистики. 12.3.3 принят локально: порционный DOM очереди проблем. 12.3.4 принят локально: большой BGP каталог/страница при обновлении. Production build и123 профильных browser приняты локально на3978976. 12.2.10 принят локально: обязательные RouterOS поля. 12.3.5 принят локально: страница CompactDataGrid сохраняется при опросе,89 browser и повторный production build прошли. 12.3.6 сохранён: baseline1000 speed probes (ограничение DOM пока не выполнено). 12.3.7 принят локально:50 строк speed-проб/сохранение страницы/глобальная сортировка/1000→1; каталог1001 устройства остаётся в DOM. 12.3.8 принят локально:50 строк ресурсов, URL/поиск/сохранение страницы/1000→1; исправлена pagination оболочки с children, production build и8 больших browser прошли. 12.3.9 принят локально:порционное раскрытие1000 предупреждений ресурсов/клавиатура/axe; исходная ошибка ESLint speedDraft подтверждена. 12.3.10 принят локально:недоступные интерфейсы очищаются в обработчиках,54 browser/полный ESLint/production build прошли. 12.3.11 принят локально:ServerTileRail50 устройств/URL/поиск/клавиатура/две площадки/mobile Sheet/axe обе темы;47 browser и production build прошли. 12.3.12 принят локально:BGP ручная страница при выбранной URL сессии/refresh/reload,16 browser/build прошли. 12.4.1 записан:main1091 сценарий/1088 passed/3 устаревших fixtures; оба исправленных файла6 passed. 12.4.2 принят локально:context205/24 файла и unit28 прошли. 12.3.13 принят локально:wide pivot51×16/ScrollArea/axe/24+2 browser/builds. 12.3.14 принят локально:history200/50 DOM/54+1 browser/build. 12.3.15 принят локально:live расписание1000/page20 error cache/15 browser. 12.3.16 принят локально:certificates1000/50 DOM/refresh деталей/19+3 browser/build. 12.3.17 принят локально:pinning идентификатора/видимость значений/29 browser/build. 12.3.18 принят локально:users1000/поиск всего списка/детали и ошибка refresh/32 browser/build. 12.3.19 принят локально:backups1000/все мобильные действия/возврат focus/39+4 browser/build. 12.3.20 принят локально:независимые speed GET/cache/freshness/58+4 browser/build. 12.3.21 принят локально:strict history DTO/null rates от DB до UI/60+5 browser/unit4/builds. 12.4.3 принят локально:users/backups delta context36 на a558412. 12.3.22 принят локально:shared speed-probes DTO/context8/60+6+47 browser/unit5/builds. 12.3.23 принят локально:strict write DTO/atomic replacement/confirmed scheduler warning/65 browser/HTTP DI JWT/builds. 12.3.24 принят локально:OSPF routes1000/50 DOM/page20/cache/320px/22+1 browser/build. 12.3.25 принят локально:BFD1000/50 DOM/cache/320px/12 browser/build. 12.4.4 принят локально:context213/25 файлов и unit36 прошли на5b2a291. 12.4.5 принят локально:main1110/171 прошли на21859be. 12.3.26 принят локально:strict atomic Ping write/подтверждение каталога и сохранения/история удаления/62 browser/context11/DI6/builds. 12.3.27 принят локально:WireGuard zero/missing counters/UI/backend mapper/29 browser/unit/builds. 12.3.28 принят локально:WireGuard interfaces1000/50 DOM/pinning/actions/cache/29+1 browser/build. 12.3.29 принят локально:WG peers1000/50 DOM/pinning/точный delete target/29+3 browser/build. 12.3.30 принят локально:WG detail1000/50 DOM/320px/клавиатура/31+1 browser/build. 12.3.31 принят локально:WG handshake без ложного online/unknown/7+1 browser/build. 12.3.32 принят локально:WG URL search/tab/status/reload/история/34+4 browser/unit/build. 12.3.33 принят локально:WG strict read/cache/freshness/unknown/40+7 browser/context8/build. 12.3.34 принят локально:VXLAN1000/50 DOM/pinning/export/22 browser/build. 12.3.35 принят локально:GRE1000/50 DOM/pinning/20+1 browser/build. 12.3.36 карта15 оставшихся источников обновлена. 12.3.37 top50 traffic/пустой локальный фильтр/перенос tabs принят в описанных границах. 12.3.38 Communities1000/50 DOM/19 browser/build принят локально. 12.3.39 clipboard ack/error/retry/unmount/5 browser/build принят локально. 12.3.40 recursive groups/endpoints1000/50 DOM/35 browser/build принят локально. 12.3.41 большая форма1000×1000/10 cards/полный PUT/22+1 browser/build принята локально. 12.3.42 Filters1000/50 DOM/полный PUT/10+1 browser/build принят локально. 12.3.43 Firewall1000/50 DOM/DnD/35+1 main/context4/build принят локально. 12.3.44 stale guard/29+2 main/context4/build принят локально;4 исходных lint errors страницы требуют cleanup. 12.3.45 сценарий1000/50 DOM/полный draft/verdict/6 browser/build принят локально. Следующий блок12.3.46: trace999/15529 nodes; затем lint cleanup/OSPF/optimizer и полная main/context регрессия.12.5 отдельно. См. docs/ux-ui-stage-12.md. Внешние ограничения 11Б сохраняются. +Этап 11Б завершён в границах локальной приёмки (11Б.7); внешний выпуск не проверен. 12.1 принят локально: доступность оболочки/клавиатура/health в описанных сценариях. 12.2.1 принят локально: strict BGP catalog/partial/nullable counters. 12.2.2 принят локально: strict traffic DTO и агрегация без выдуманных точек. 12.2.3 принят локально: observations для server traffic от backend до dashboard. 12.2.4 принят локально: strict servers/probes и unknown server status. 12.2.5 принят локально: реальные ping samples/unknown/ось RTT/Recharts. 12.2.6 принят локально: strict GRE/WireGuard/VXLAN и device failures. 12.2.7 принят локально: strict Internet path snapshot/fallback. 12.2.8 принят локально: подтверждение полного backend сбора. 12.2.9 принят локально: метрики участков и наблюдаемый WAN без эвристической подмены. 12.3.1 принят локально:1000 серверов/штатная пагинация. 12.3.2 принят локально: пагинация CompactDataGrid/1000 строк статистики. 12.3.3 принят локально: порционный DOM очереди проблем. 12.3.4 принят локально: большой BGP каталог/страница при обновлении. Production build и123 профильных browser приняты локально на3978976. 12.2.10 принят локально: обязательные RouterOS поля. 12.3.5 принят локально: страница CompactDataGrid сохраняется при опросе,89 browser и повторный production build прошли. 12.3.6 сохранён: baseline1000 speed probes (ограничение DOM пока не выполнено). 12.3.7 принят локально:50 строк speed-проб/сохранение страницы/глобальная сортировка/1000→1; каталог1001 устройства остаётся в DOM. 12.3.8 принят локально:50 строк ресурсов, URL/поиск/сохранение страницы/1000→1; исправлена pagination оболочки с children, production build и8 больших browser прошли. 12.3.9 принят локально:порционное раскрытие1000 предупреждений ресурсов/клавиатура/axe; исходная ошибка ESLint speedDraft подтверждена. 12.3.10 принят локально:недоступные интерфейсы очищаются в обработчиках,54 browser/полный ESLint/production build прошли. 12.3.11 принят локально:ServerTileRail50 устройств/URL/поиск/клавиатура/две площадки/mobile Sheet/axe обе темы;47 browser и production build прошли. 12.3.12 принят локально:BGP ручная страница при выбранной URL сессии/refresh/reload,16 browser/build прошли. 12.4.1 записан:main1091 сценарий/1088 passed/3 устаревших fixtures; оба исправленных файла6 passed. 12.4.2 принят локально:context205/24 файла и unit28 прошли. 12.3.13 принят локально:wide pivot51×16/ScrollArea/axe/24+2 browser/builds. 12.3.14 принят локально:history200/50 DOM/54+1 browser/build. 12.3.15 принят локально:live расписание1000/page20 error cache/15 browser. 12.3.16 принят локально:certificates1000/50 DOM/refresh деталей/19+3 browser/build. 12.3.17 принят локально:pinning идентификатора/видимость значений/29 browser/build. 12.3.18 принят локально:users1000/поиск всего списка/детали и ошибка refresh/32 browser/build. 12.3.19 принят локально:backups1000/все мобильные действия/возврат focus/39+4 browser/build. 12.3.20 принят локально:независимые speed GET/cache/freshness/58+4 browser/build. 12.3.21 принят локально:strict history DTO/null rates от DB до UI/60+5 browser/unit4/builds. 12.4.3 принят локально:users/backups delta context36 на a558412. 12.3.22 принят локально:shared speed-probes DTO/context8/60+6+47 browser/unit5/builds. 12.3.23 принят локально:strict write DTO/atomic replacement/confirmed scheduler warning/65 browser/HTTP DI JWT/builds. 12.3.24 принят локально:OSPF routes1000/50 DOM/page20/cache/320px/22+1 browser/build. 12.3.25 принят локально:BFD1000/50 DOM/cache/320px/12 browser/build. 12.4.4 принят локально:context213/25 файлов и unit36 прошли на5b2a291. 12.4.5 принят локально:main1110/171 прошли на21859be. 12.3.26 принят локально:strict atomic Ping write/подтверждение каталога и сохранения/история удаления/62 browser/context11/DI6/builds. 12.3.27 принят локально:WireGuard zero/missing counters/UI/backend mapper/29 browser/unit/builds. 12.3.28 принят локально:WireGuard interfaces1000/50 DOM/pinning/actions/cache/29+1 browser/build. 12.3.29 принят локально:WG peers1000/50 DOM/pinning/точный delete target/29+3 browser/build. 12.3.30 принят локально:WG detail1000/50 DOM/320px/клавиатура/31+1 browser/build. 12.3.31 принят локально:WG handshake без ложного online/unknown/7+1 browser/build. 12.3.32 принят локально:WG URL search/tab/status/reload/история/34+4 browser/unit/build. 12.3.33 принят локально:WG strict read/cache/freshness/unknown/40+7 browser/context8/build. 12.3.34 принят локально:VXLAN1000/50 DOM/pinning/export/22 browser/build. 12.3.35 принят локально:GRE1000/50 DOM/pinning/20+1 browser/build. 12.3.36 карта15 оставшихся источников обновлена. 12.3.37 top50 traffic/пустой локальный фильтр/перенос tabs принят в описанных границах. 12.3.38 Communities1000/50 DOM/19 browser/build принят локально. 12.3.39 clipboard ack/error/retry/unmount/5 browser/build принят локально. 12.3.40 recursive groups/endpoints1000/50 DOM/35 browser/build принят локально. 12.3.41 большая форма1000×1000/10 cards/полный PUT/22+1 browser/build принята локально. 12.3.42 Filters1000/50 DOM/полный PUT/10+1 browser/build принят локально. 12.3.43 Firewall1000/50 DOM/DnD/35+1 main/context4/build принят локально. 12.3.44 stale guard/29+2 main/context4/build принят локально;4 исходных lint errors страницы требуют cleanup. 12.3.45 сценарий1000/50 DOM/полный draft/verdict/6 browser/build принят локально. 12.3.46 trace999/50 steps/1293 nodes/epoch/6+1 browser/build принят локально. Следующий блок12.3.47: Firewall lint cleanup; затем OSPF/optimizer и полная main/context регрессия.12.5 отдельно. См. docs/ux-ui-stage-12.md. Внешние ограничения 11Б сохраняются. Реальные pg_dump/pg_restore, PG rollback и остановка UDP worker требуют внешней приёмки. Датированные продолжения ниже — история; точный следующий шаг также указан в начале [журнала](ux-ui-progress.md). diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md index 247dad0b..1ea4892c 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -31,6 +31,14 @@ | 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 | | 12 | Итоговая доступность, адаптивность, производительность и регрессия | В работе;12.1/12.2.1–12.2.10 и12.3.1–12.3.5 и12.3.7–12.3.12 приняты в описанных локальных границах;12.4.1 main регрессия и профильный повтор записаны;12.4.2 context205 и unit28 прошли;12.3.13–12.3.25 приняты локально в описанных границах;12.4.4 context213 и12.4.5 main1110 прошли, 12.3.26–12.3.33 приняты локально; 12.3.34 VXLAN и12.3.35 GRE tunnels приняты; остаются network grids/общая регрессия после delta/внешняя приёмка | +## 12.3.46 — ограниченная трассировка Firewall (2026-10-04) + +Trace показывает50 шагов, общий verdict/counts/номера относятся ко всему расчёту. Предыдущая/следующая страница и переход к правилу результата44px; новая симуляция/reset/preset/scenario начинают с первой страницы. Строки переносятся на320px, исправлен контраст passthrough и пояснений. runEpoch защищает от старого таймера после stop→новый run; unmount инвалидирует запуск. + +6/6 browser на начальной pagination и финальный large1/1 после mobile/epoch/axe прошли: trace999 уменьшен15529→1293 page DOM nodes,50 steps на странице/49 на последней. Full verdict#999, page2, keyboard jump951–999,320px/axe, stop→instant run и продвижение clock1000ms без старого evaluating, reset проверены. TypeScript без incremental/ESLint теста/production build прошли; ESLint API baseline/current подтверждает те же4 исходных ошибки страницы и2 warnings. [Трассировка320px](ux-ui/screens/large-firewall-trace-320.png) просмотрена. Алгоритм вычисления не изменён: instant по-прежнему делает обновления полного steps на каждом правиле, CPU complexity не принята нагрузочным тестом. Данные остаются полными в памяти. + +Следующий блок12.3.47: Firewall lint cleanup четырёх старых эффектов без потери draft/URL/filter/focus. Затем OSPF таблица+topology/optimizer; ошибки localStorage и большой multi-server sort отдельно.10 manualPagination:true остаются. Общая main/context после delta и внешняя12.5 отдельно. Коммит локальный без push. + ## 12.3.45 — большой редактор сценария Firewall (2026-10-04) ReUI pagination50/clamp/ScrollArea, закреплённые номер и chain, действия44px без hover; номера и up/down края считаются по полному draft. ScenarioSheet320px занимает всю ширину viewport. Кнопки редактирования/удаления сценария получили44px и доступные имена. Локальный сценарий не отправляет операции RouterOS. diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md index dd162789..8f115bc0 100644 --- a/docs/ux-ui-stage-12.md +++ b/docs/ux-ui-stage-12.md @@ -560,3 +560,12 @@ ReUI pagination50/clamp/ScrollArea, закреплённые номер и chain Обнаружено: trace999 создаёт15529 page DOM nodes; ограничение редактора не означает принятие всего симулятора. Следующий блок12.3.46: страницы трассировки/полный verdict/переход к результату и сохранение отмены/повторного запуска. Затем Firewall lint cleanup, OSPF таблица+topology/optimizer.10 manualPagination:true specialized grids остаются. Общая main/context после delta и внешняя12.5 отдельно. Коммит локальный без push. + +## 12.3.46 — ограниченная трассировка Firewall (2026-10-04) + +Trace показывает50 шагов, общий verdict/counts/номера относятся ко всему расчёту. Предыдущая/следующая страница и переход к правилу результата44px; новая симуляция/reset/preset/scenario начинают с первой страницы. Строки переносятся на320px, исправлен контраст passthrough и пояснений. runEpoch защищает от старого таймера после stop→новый run; unmount инвалидирует запуск. + +6/6 browser на начальной pagination и финальный large1/1 после mobile/epoch/axe прошли: trace999 уменьшен15529→1293 page DOM nodes,50 steps на странице/49 на последней. Full verdict#999, page2, keyboard jump951–999,320px/axe, stop→instant run и продвижение clock1000ms без старого evaluating, reset проверены. TypeScript без incremental/ESLint теста/production build прошли; ESLint API baseline/current подтверждает те же4 исходных ошибки страницы и2 warnings. [Трассировка320px](ux-ui/screens/large-firewall-trace-320.png) просмотрена. Алгоритм вычисления не изменён: instant по-прежнему делает обновления полного steps на каждом правиле, CPU complexity не принята нагрузочным тестом. Данные остаются полными в памяти. + +Следующий блок12.3.47: Firewall lint cleanup четырёх старых эффектов без потери draft/URL/filter/focus. Затем OSPF таблица+topology/optimizer; ошибки localStorage и большой multi-server sort отдельно.10 manualPagination:true остаются. Общая main/context после delta и внешняя12.5 отдельно. Коммит локальный без push. + diff --git a/docs/ux-ui/screens/large-firewall-trace-320.png b/docs/ux-ui/screens/large-firewall-trace-320.png new file mode 100644 index 0000000000000000000000000000000000000000..a50e7bb398260626dbdb30a5b880d1346155cc4a GIT binary patch literal 58676 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

yskBV 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

Ab)BqXBiZB~7$~|LHTx8h`eeSJBz>3g6QK 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�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

gQ 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 literal 0 HcmV?d00001 diff --git a/tests/e2e/large-firewall-scenario.spec.ts b/tests/e2e/large-firewall-scenario.spec.ts index ce68e1a2..a286cce1 100644 --- a/tests/e2e/large-firewall-scenario.spec.ts +++ b/tests/e2e/large-firewall-scenario.spec.ts @@ -52,6 +52,36 @@ test("Сценарий1000: граница страниц, полный draft и await page.getByRole("button", { name: "Запустить", exact: true }).click() await expect(page.getByText("— правило #999", { exact: true })).toBeVisible() await expect(page.getByText("Пакет заблокирован", { exact: false })).toBeVisible() + const trace = page.getByRole("region", { name: "Трассировка Firewall", exact: true }) + const traceRows = trace.locator("[data-firewall-trace-step]") + await expect(traceRows).toHaveCount(50) + await expect(traceRows.first()).toHaveAttribute("data-firewall-trace-step", "1") console.log("Scenario simulation nodes", await page.locator("*").count()) + await trace.getByRole("button", { name: "Следующая страница трассировки", exact: true }).click() + await expect(traceRows.first()).toHaveAttribute("data-firewall-trace-step", "51") + await trace.getByRole("button", { name: "К правилу результата", exact: true }).focus(); await page.keyboard.press("Enter") + await expect(traceRows).toHaveCount(49) + await expect(traceRows.last()).toHaveAttribute("data-firewall-trace-step", "999") + await expect(trace.getByRole("status")).toHaveText("Шаги 951–999 из 999") + await expect(trace.getByRole("button", { name: "Следующая страница трассировки", exact: true })).toBeDisabled() + expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(320) + expect((await page.evaluate(async () => (window as unknown as { axe: typeof axe }).axe.run(document.querySelector('[aria-label="Трассировка Firewall"]')!, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] } }))).violations.map(v => ({ id: v.id, nodes: v.nodes.map(n => n.target) }))).toEqual([]) + await traceRows.last().scrollIntoViewIfNeeded() + await page.screenshot({ path: info.outputPath("trace-320.png"), animations: "disabled" }) + await page.clock.install() + await page.getByRole("button", { name: "Быстро", exact: true }).click() + await page.getByRole("button", { name: "Снова", exact: true }).click() + await expect(traceRows.first()).toHaveAttribute("data-firewall-trace-step", "1") + await page.getByRole("button", { name: "Стоп", exact: true }).click() + await page.getByRole("button", { name: "Мгновенно", exact: true }).click() + await page.getByRole("button", { name: "Снова", exact: true }).click() + await expect(page.getByText("— правило #999", { exact: true })).toBeVisible() + await expect(traceRows.first()).toHaveAttribute("data-firewall-trace-step", "1") + await page.clock.runFor(1000) + await expect(trace.getByText("проверяется…", { exact: true })).toHaveCount(0) + await expect(page.getByText("— правило #999", { exact: true })).toBeVisible() + await page.getByRole("button", { name: "Сбросить", exact: true }).click() + await expect(trace).toHaveCount(0) + await expect(page.getByText("— правило #999", { exact: true })).toHaveCount(0) expect(writes).toBe(0) })