From c4110fa32393756af2c36798ca1d9120606dfebd Mon Sep 17 00:00:00 2001 From: Denozordec Date: Sun, 4 Oct 2026 03:27:41 +0700 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D1=8F=D1=82=D1=8C=20=D1=80=D1=83=D1=87=D0=BD=D1=83=D1=8E=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=83=20=D0=B2=D1=8B?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=BD=D0=BD=D0=BE=D0=B9=20=D1=81=D0=B5=D1=81?= =?UTF-8?q?=D1=81=D0=B8=D0=B8=20BGP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/data-grids/bgp-sessions-data-grid.tsx | 10 +++++----- docs/ux-ui-plan.md | 2 +- docs/ux-ui-progress.md | 10 +++++++++- docs/ux-ui-stage-12.md | 8 ++++++++ tests/e2e/large-bgp.spec.ts | 2 +- 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/components/data-grids/bgp-sessions-data-grid.tsx b/components/data-grids/bgp-sessions-data-grid.tsx index 4d82130d..747e1c13 100644 --- a/components/data-grids/bgp-sessions-data-grid.tsx +++ b/components/data-grids/bgp-sessions-data-grid.tsx @@ -282,11 +282,11 @@ function BgpSessionsDataGrid({ sessions, emptyTitle, selectedSessionId, onSelect const last=Math.max(0,Math.ceil(sessions.length/pageSize)-1) if(pageIndex>last)table.setPageIndex(last) },[sessions.length,pageSize,pageIndex,table]) - useEffect(()=>{ - if(!selectedSessionId)return - const index=table.getPrePaginatedRowModel().rows.findIndex(row=>row.id===selectedSessionId) - if(index>=0)table.setPageIndex(Math.floor(index/pageSize)) - },[selectedSessionId,pageSize,sessions.length,sorting,table]) + const setPageIndex = table.setPageIndex + const selectedIndex = selectedSessionId ? table.getPrePaginatedRowModel().rows.findIndex(row => row.id === selectedSessionId) : -1 + useEffect(() => { + if (selectedIndex >= 0) setPageIndex(Math.floor(selectedIndex / pageSize)) + }, [selectedSessionId, selectedIndex, pageSize, sorting, setPageIndex]) if (sessions.length === 0) { return ( diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md index d4e97fbe..dbd6f1a1 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 selected URL/ручная страница и стабильные зависимости эффекта, затем общая регрессия12.4 и остальные grids;12.4/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:main browser regression1091 тест/163 файла, затем context и оставшиеся grids;12.4/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 a7d28b55..1c8ecccd 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -29,13 +29,21 @@ | 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.1/12.2.1–12.2.10 и12.3.1–12.3.5 и12.3.7–12.3.11 приняты в описанных локальных границах | +| 12 | Итоговая доступность, адаптивность, производительность и регрессия | В работе;12.1/12.2.1–12.2.10 и12.3.1–12.3.5 и12.3.7–12.3.12 приняты в описанных локальных границах | +## 12.3.12 — ручная страница BGP при выборе сессии из URL (2026-10-04) + +Воспроизведён возврат со страницы19 на20 после ручного Previous при selectedSessionId из URL. Эффект BGP больше не зависит от меняющегося объекта useTable: используются стабильный setPageIndex и вычисленный индекс выбранной сессии. Глобальная сортировка и первичное восстановление выбранной сессии сохранены; ручная страница19 сохраняется при refresh, reload восстанавливает linked session на20. + +16 browser BGP/data/links/1000 sessions прошли после исправления; Typecheck/ESLint и frontend production/contracts build(34 страницы) прошли. Метрики BGP50 строк/1932 nodes остаются. Реальный BGP/RouterOS не проверен. + +Следующий шаг12.4.1: общий main browser прогон (1091 тест в163 файлах) с JSON отчётом; ограничить ранние падения20, затем разобрать причины и профильные повторы. Это проверка после изменения общей основы;12.3 ещё имеет25 specialized grids и широкую pivot для проверки. После main — context конфигурации и итоговая матрица;12.5 внешняя приёмка отдельно. Без push. + ## 12.3.11 — ограничение DOM бокового каталога устройств (2026-10-04) ServerTileRail сохраняет существующую композицию Frame/Item/List9, а страницы управляются штатными TanStack v9/DataGridPagination. До50 устройств в DOM, поиск по полному каталогу, группировка по площадкам и выбор из URL сохранены. Home/End/стрелки открывают страницу выбранного устройства; ручное перелистывание не меняет URL и сохраняется при повторном получении каталога. Эффект выбора использует стабильный setPageIndex: объект useTable в v9 меняется при рендере и иначе возвращал страницу назад. ID карточек уникальны для desktop/mobile, активный descendant указывается только для смонтированной карточки своего списка. diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md index 34edf4ac..d9021619 100644 --- a/docs/ux-ui-stage-12.md +++ b/docs/ux-ui-stage-12.md @@ -215,3 +215,11 @@ ServerTileRail сохраняет существующую композицию 47 browser BGP/диагностика/collector-probes прошли после финальной правки, Typecheck/ESLint и frontend production/contracts build(34 страницы) прошли. ReUI validate_usage/checklist применены; primitives/theme не изменены. Полный список остаётся в памяти; это pagination, не серверная выборка/виртуализация. Общая регрессия ещё не завершена: main config содержит1091 тест в163 файлах. Следующий блок12.3.12: проверить тот же эффект зависимости от объекта table в BGP при выбранной сессии из URL и ручном переходе страницы. Затем общая регрессия12.4 по группам и оставшиеся25 специализированных grids;12.5 внешняя PG/RouterOS приёмка отдельно. Без push. + +## 12.3.12 — ручная страница BGP при выборе сессии из URL (2026-10-04) + +Воспроизведён возврат со страницы19 на20 после ручного Previous при selectedSessionId из URL. Эффект BGP больше не зависит от меняющегося объекта useTable: используются стабильный setPageIndex и вычисленный индекс выбранной сессии. Глобальная сортировка и первичное восстановление выбранной сессии сохранены; ручная страница19 сохраняется при refresh, reload восстанавливает linked session на20. + +16 browser BGP/data/links/1000 sessions прошли после исправления; Typecheck/ESLint и frontend production/contracts build(34 страницы) прошли. Метрики BGP50 строк/1932 nodes остаются. Реальный BGP/RouterOS не проверен. + +Следующий шаг12.4.1: общий main browser прогон (1091 тест в163 файлах) с JSON отчётом; ограничить ранние падения20, затем разобрать причины и профильные повторы. Это проверка после изменения общей основы;12.3 ещё имеет25 specialized grids и широкую pivot для проверки. После main — context конфигурации и итоговая матрица;12.5 внешняя приёмка отдельно. Без push. diff --git a/tests/e2e/large-bgp.spec.ts b/tests/e2e/large-bgp.spec.ts index 7a7bb001..0d8aa7b6 100644 --- a/tests/e2e/large-bgp.spec.ts +++ b/tests/e2e/large-bgp.spec.ts @@ -4,5 +4,5 @@ test("large BGP bounds DOM, preserves page on refresh and restores linked sessio test.setTimeout(90000);const sessions=Array.from({length:1000},(_,i)=>({...bgpSession(1,i),id:"*"+(i+1),name:"Large peer "+String(i+1).padStart(4,"0"),remoteAs:65000+i,capabilities:["route-refresh"]}));let calls=0 await page.route("**/health",r=>r.fulfill({json:{ok:true}}));await page.route("**/api/**",r=>{const path=new URL(r.request().url()).pathname;if(path==="/api/bgp/catalog")calls++;return r.fulfill({json:path==="/api/auth/config"?{required:false}:path==="/api/servers"?[{id:1,name:"BGP node",host:"192.0.2.1",country:"RU",enabled:true,status:"online"}]:path==="/api/bgp/catalog"?{sessions,failures:[]}:{}})}) const start=Date.now();await page.goto("/bgp?server=1");await expect(page.getByText("Large peer 0001",{exact:true})).toBeVisible({timeout:60000});const sample={fixtureRows:1000,readyMs:Date.now()-start,renderedRows:await page.locator("tr[data-row-id]").count(),domNodes:await page.locator("*").count()};console.log(JSON.stringify(sample));await info.attach("large-bgp-metrics",{body:JSON.stringify(sample),contentType:"application/json"});expect(sample.renderedRows).toBe(50);expect(sample.domNodes).toBeLessThan(3500) - await page.getByRole("button",{name:"Следующая страница",exact:true}).click();await expect(page.getByText("Large peer 0051",{exact:true})).toBeVisible();const before=calls;await page.getByRole("button",{name:"Обновить",exact:true}).click();await expect.poll(()=>calls).toBeGreaterThan(before);await expect(page.getByRole("button",{name:"Обновить",exact:true})).toBeEnabled();await expect(page.getByText("51–100 из 1000",{exact:true})).toBeVisible();await page.getByPlaceholder("IP, AS, описание…").fill("Large peer 1000");await expect(page.locator("tr[data-row-id]")).toHaveCount(1);await expect(page.getByText("Large peer 1000",{exact:true})).toBeVisible();await page.goto("/bgp?server=1&session=1-%2A1000");await expect(page.getByText("Large peer 1000",{exact:true})).toBeVisible();await expect(page.getByText("route-refresh",{exact:true})).toBeVisible();await expect(page.getByText("951–1000 из 1000",{exact:true})).toBeVisible();await page.setViewportSize({width:320,height:900});expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth)).toBe(true) + await page.getByRole("button",{name:"Следующая страница",exact:true}).click();await expect(page.getByText("Large peer 0051",{exact:true})).toBeVisible();const before=calls;await page.getByRole("button",{name:"Обновить",exact:true}).click();await expect.poll(()=>calls).toBeGreaterThan(before);await expect(page.getByRole("button",{name:"Обновить",exact:true})).toBeEnabled();await expect(page.getByText("51–100 из 1000",{exact:true})).toBeVisible();await page.getByPlaceholder("IP, AS, описание…").fill("Large peer 1000");await expect(page.locator("tr[data-row-id]")).toHaveCount(1);await expect(page.getByText("Large peer 1000",{exact:true})).toBeVisible();await page.goto("/bgp?server=1&session=1-%2A1000");await expect(page.getByText("Large peer 1000",{exact:true})).toBeVisible();await expect(page.getByText("route-refresh",{exact:true})).toBeVisible();await expect(page.getByText("951–1000 из 1000",{exact:true})).toBeVisible();await page.getByRole("button",{name:"Предыдущая страница",exact:true}).click();await expect(page.getByText("901–950 из 1000",{exact:true})).toBeVisible();const linkedBefore=calls;await page.getByRole("button",{name:"Обновить",exact:true}).click();await expect.poll(()=>calls).toBeGreaterThan(linkedBefore);await expect(page.getByRole("button",{name:"Обновить",exact:true})).toBeEnabled();await expect(page.getByText("901–950 из 1000",{exact:true})).toBeVisible();await page.reload();await expect(page.getByText("951–1000 из 1000",{exact:true})).toBeVisible();await page.setViewportSize({width:320,height:900});expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth)).toBe(true) })