fix(ui): ограничить каталог BGP и сохранить страницу при обновлении

This commit is contained in:
Denozordec committed 2026-10-04 00:38:44 +07:00
1 parent 604b2b0b51
commit 39789765cb
5 files changed
+68 -3

No files matched your search

@@ -2,7 +2,7 @@
import { dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid"
import { useMemo } from "react"
import { useMemo, useEffect } from "react"
import {
type ColumnDef,
useTable,
@@ -266,7 +266,9 @@ function BgpSessionsDataGrid({ sessions, emptyTitle, selectedSessionId, onSelect
const table = useTable({
features: dataGridFeatures,
manualPagination: true,
manualPagination: false,
autoResetPageIndex: false,
initialState: { pagination: { pageIndex: 0, pageSize: 50 } },
data: sessions,
columns,
getRowId: (row) => row.id,
@@ -274,6 +276,18 @@ function BgpSessionsDataGrid({ sessions, emptyTitle, selectedSessionId, onSelect
...(onSelectSession ? { state: { expanded: selectedSessionId ? { [selectedSessionId]: true } : {} } } : {}),
})
const {pageIndex,pageSize}=table.state.pagination
const sorting=table.state.sorting
useEffect(()=>{
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])
if (sessions.length === 0) {
return (
<EmptyState
@@ -289,6 +303,7 @@ function BgpSessionsDataGrid({ sessions, emptyTitle, selectedSessionId, onSelect
<DataGridShell
table={table}
recordCount={sessions.length}
pagination={sessions.length>50 || pageSize!==50}
onRowClick={(row) => onSelectSession ? onSelectSession(row.id === selectedSessionId ? null : row.id) : table.getRow(row.id).toggleExpanded()}
/>
)
+1 -1
View File
@@ -4,7 +4,7 @@
## Актуальная точка продолжения — 2026-10-03
Этап 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 каталог/сохранение страницы при обновлении; затем остальные специализированные grids и12.4 регрессия/build. См. 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 накопленных изменений и переходы дашборда, затем12.3.5 остальные специализированные grids и12.4 сводная регрессия. См. docs/ux-ui-stage-12.md. Внешние ограничения 11Б сохраняются.
Реальные pg_dump/pg_restore, PG rollback и остановка UDP worker требуют внешней приёмки. Датированные продолжения ниже — история; точный следующий шаг также указан в начале [журнала](ux-ui-progress.md).
+21
View File
@@ -36,6 +36,27 @@
## 12.3.4 — большой каталог BGP и контекст страницы (2026-10-04)
BGP использует штатную client pagination50. При обновлении того же каталога страница сохраняется (autoResetPageIndex:false); при уменьшении результатов индекс ограничивается последней существующей страницей. Сессия из URL открывает соответствующую страницу по pre-paginated row model; изменение сортировки/page size пересчитывает её положение. Client фильтры/сортировка и раскрытие работают по всем данным, права/сетевые операции неизменны.
На фиксированных1000 сессиях DOM25657→1932 nodes, rendered1000→50. Единичные dev ready до2990ms/после898ms не являются production SLA.13 BGP/routing/link/large browser, typecheck/ESLint прошли, включая обновление на странице2, поиск последней сессии, URL последней страницы/детали и320px. ReUI DataGridPagination повторно использован; upstream API и установленная групповая навигация различаются, primitive не менялся.
| Доработка | Референс | Схема |
| --- | --- | --- |
| Серверы/CompactDataGrid/BGP | [Data Grid Filtering2](https://reui.io/preview/base/data-grid-filtering-2?ref=mcp) | Полный каталог → поиск/сортировка → страница50 → детали |
| Большая очередь проблем | [List9](https://reui.io/preview/base/list-9?ref=mcp) | Полный счётчик → первые5 → раскрыть50 → добавить50 → объект |
```mermaid
flowchart LR
A["1000 строк API"] --> B["Проверка DTO и фильтры"]
B --> C["ReUI/TanStack: 50 строк в DOM"]
C --> D["Страница и детали сессии"]
D --> E["Обновление сохраняет страницу"]
```
Этап12.3 ещё в работе: остальные27 manualPagination grids/широкие pivot не проверены на1000 строк. Следующий шаг: production build накопленных изменений, профильная регрессия переходов дашборда; затем12.3.5 остальные grids, отдельно Firewall порядок/drag и server-paged traffic без слепой client миграции. Итоговая12.4 и внешняя12.5 не приняты.
## 12.3.3 — DOM очереди проблем дашборда (2026-10-04)
AttentionRows больше не создаёт весь скрытый список: первые5 всегда видны, native details монтирует следующие50 по запросу, кнопка добавляет по50. Закрытие удаляет скрытые строки и сбрасывает порцию; полный счётчик/ссылки сохраняются. Нет скрытых1000 anchors до действия пользователя. Раскрытие выполняется обычным summary и клавиатурой, используется существующий Button; Frame/AttentionQueue primitives unchanged.
+21
View File
@@ -126,3 +126,24 @@ AttentionRows больше не создаёт весь скрытый спис
Проверки:62 dashboard/permissions browser (включая1000 проблем: DOM5→55→105→5→55, переход к серверу,320px),20 dashboard unit, typecheck/ESLint прошли. На fixture1000 offline servers начальный dashboard:831 DOM nodes,1081ms ready в одном dev прогоне. Порционное раскрытие может по запросу пользователя постепенно создать весь список; полноценная виртуализация очереди не заявляется.
Далее12.3.4: большой каталог BGP, учитывая сохранение страницы при фоновом обновлении. Другие27 специализированных manualPagination grids и wide pivot остаются отдельными ограничениями.12.4 сводные проверки/production build ещё не приняты; внешняя приёмка не заменяется локальными данными.
## 12.3.4 — большой каталог BGP и контекст страницы (2026-10-04)
BGP использует штатную client pagination50. При обновлении того же каталога страница сохраняется (autoResetPageIndex:false); при уменьшении результатов индекс ограничивается последней существующей страницей. Сессия из URL открывает соответствующую страницу по pre-paginated row model; изменение сортировки/page size пересчитывает её положение. Client фильтры/сортировка и раскрытие работают по всем данным, права/сетевые операции неизменны.
На фиксированных1000 сессиях DOM25657→1932 nodes, rendered1000→50. Единичные dev ready до2990ms/после898ms не являются production SLA.13 BGP/routing/link/large browser, typecheck/ESLint прошли, включая обновление на странице2, поиск последней сессии, URL последней страницы/детали и320px. ReUI DataGridPagination повторно использован; upstream API и установленная групповая навигация различаются, primitive не менялся.
| Доработка | Референс | Схема |
| --- | --- | --- |
| Серверы/CompactDataGrid/BGP | [Data Grid Filtering2](https://reui.io/preview/base/data-grid-filtering-2?ref=mcp) | Полный каталог → поиск/сортировка → страница50 → детали |
| Большая очередь проблем | [List9](https://reui.io/preview/base/list-9?ref=mcp) | Полный счётчик → первые5 → раскрыть50 → добавить50 → объект |
```mermaid
flowchart LR
A["1000 строк API"] --> B["Проверка DTO и фильтры"]
B --> C["ReUI/TanStack: 50 строк в DOM"]
C --> D["Страница и детали сессии"]
D --> E["Обновление сохраняет страницу"]
```
Этап12.3 ещё в работе: остальные27 manualPagination grids/широкие pivot не проверены на1000 строк. Следующий шаг: production build накопленных изменений, профильная регрессия переходов дашборда; затем12.3.5 остальные grids, отдельно Firewall порядок/drag и server-paged traffic без слепой client миграции. Итоговая12.4 и внешняя12.5 не приняты.
+8
View File
@@ -0,0 +1,8 @@
import {test,expect} from "@playwright/test"
import {bgpSession} from "./fixtures/bgp"
test("large BGP bounds DOM, preserves page on refresh and restores linked session",async({page},info)=>{
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)
})