From adfb2fc4c31a7753290d11142224c0727fb26260 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Sun, 4 Oct 2026 15:41:21 +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=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=B0=D1=82=D0=B0=D0=BB?= =?UTF-8?q?=D0=BE=D0=B3=D0=B0=20Communities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data-grids/communities-data-grid.tsx | 44 +++++++++++---- docs/ux-ui-performance.md | 7 +-- docs/ux-ui-plan.md | 2 +- docs/ux-ui-progress.md | 8 +++ docs/ux-ui-stage-12.md | 9 ++++ docs/ux-ui/screens/large-communities-320.png | Bin 0 -> 53860 bytes tests/e2e/large-communities.spec.ts | 51 ++++++++++++++++++ 7 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 docs/ux-ui/screens/large-communities-320.png create mode 100644 tests/e2e/large-communities.spec.ts diff --git a/components/data-grids/communities-data-grid.tsx b/components/data-grids/communities-data-grid.tsx index 0db97a61..0a67d80b 100644 --- a/components/data-grids/communities-data-grid.tsx +++ b/components/data-grids/communities-data-grid.tsx @@ -1,12 +1,13 @@ "use client" -import { dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid" +import { DataGridContainer, DataGridScrollArea, DataGridTable, dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid" -import { useMemo } from "react" +import { useEffect, useMemo } from "react" import { type ColumnDef, useTable, } from "@tanstack/react-table" +import { Button } from "@/components/ui/button" import { cn } from "@/lib/utils" import { DataGridShell } from "@/components/data-grids/shared/data-grid-shell" import { @@ -81,6 +82,7 @@ function CommunitiesDataGrid({ () => [ { id: "value", + size: 192, accessorKey: "value", header: ({ column }) => ( @@ -93,7 +95,7 @@ function CommunitiesDataGrid({ data-community-disabled={!c.enabled ? true : undefined} > - + {c.value} + ), + size: 64, meta: { headerClassName: DATA_GRID_CELL_PAD_LAST, cellClassName: DATA_GRID_CELL_PAD_LAST, }, }, ], - [copiedValue, onCopy], + [copiedValue, onCopy, onSelect], ) const rowSelection = useMemo( @@ -228,7 +240,9 @@ function CommunitiesDataGrid({ const table = useTable({ features: dataGridFeatures, - manualPagination: true, + manualPagination: false, + autoResetPageIndex: false, + initialState: { pagination: { pageIndex: 0, pageSize: 50 }, columnPinning: { start: ["value"], end: [] } }, data: communities, columns, getRowId: (row) => row.id, @@ -236,6 +250,12 @@ function CommunitiesDataGrid({ state: { rowSelection }, }) + const { pageIndex, pageSize } = table.state.pagination + useEffect(() => { + const lastPage = Math.max(0, Math.ceil(communities.length / pageSize) - 1) + if (pageIndex > lastPage) table.setPageIndex(lastPage) + }, [communities.length, pageIndex, pageSize, table]) + if (communities.length === 0) { return ( 50 || pageSize !== 50} + tableLayout={{ dense: true, columnsPinnable: true }} onRowClick={(row) => onSelect(row)} tableClassNames={{ headerRow: "border-b border-border", @@ -259,7 +281,11 @@ function CommunitiesDataGrid({ "[&:has([data-community-disabled=true])]:opacity-50", ), }} - /> + > + + + + ) } diff --git a/docs/ux-ui-performance.md b/docs/ux-ui-performance.md index e4df7371..0242fc29 100644 --- a/docs/ux-ui-performance.md +++ b/docs/ux-ui-performance.md @@ -15,11 +15,10 @@ ## Оставшиеся специализированные grids -Аудит12.3.36 от2026-10-04: точный поиск `manualPagination: true` в components/data-grids —15 файлов. Это не15 дефектов: есть фиксированный источник, demo, неподключённый компонент и ограниченные срезы. Файлы ниже сопоставлены с действующими JSX-потребителями; ограничения установлены чтением кода, не нагрузочной приёмкой API. +Аудит12.3.36 от2026-10-04: точный поиск `manualPagination: true` в components/data-grids —14 файлов после12.3.38. Это не15 дефектов: есть фиксированный источник, demo, неподключённый компонент и ограниченные срезы. Файлы ниже сопоставлены с действующими JSX-потребителями; ограничения установлены чтением кода, не нагрузочной приёмкой API. | Файл | Источник и необходимая проверка | | --- | --- | -| [communities-data-grid.tsx](../components/data-grids/communities-data-grid.tsx) | Evo catalog snapshot; полный доступный список/поиск/выбор. Первый live кандидат1000. | | [data-collection-scheduler-data-grid.tsx](../components/data-grids/data-collection-scheduler-data-grid.tsx) | 11 фиксированных SCHEDULER_JOB_KEYS (lib/scheduler-settings.ts); UI map этих ключей. Fixture1000 не соответствует источнику. | | [filters-data-grid.tsx](../components/data-grids/filters-data-grid.tsx) | currentRules/filteredRules + orderedRules: move up/down относятся ко всему исходному порядку. Проверить границу страниц и точный target. | | [firewall-rules-data-grid.tsx](../components/data-grids/firewall-rules-data-grid.tsx) | filteredRules; reorderable отключает сортировку, DnD dataIds включает исходный массив. Ограничение DOM требует отдельной проверки cross-page порядка. | @@ -35,7 +34,7 @@ | [subusers-data-grid.tsx](../components/data-grids/subusers-data-grid.tsx) | Поиск импортов/JSX в app/components не нашёл потребителя: компонент экспортируется, но не подключён. Не доказывает дефект экрана /users. | | [traffic-flows-data-grid.tsx](../components/data-grids/traffic-flows-data-grid.tsx) | FlowAnalyticsPanel/conversationsList: backend traffic-flow-analytics.ts top=min(50,max(10,settings.topN)), slice(0,top). Это top slice, не полный каталог/серверная pagination. Проверить сообщение об ограничении50 и связывание с графиками. | -Очередь: Communities → recursive groups/details → Filters порядок → Firewall/DnD → OSPF таблица+topology → optimizer preview/комбинаторика. Scheduler11, traffic cap50 и commRecs24 проверять на реальной границе, не искусственно подставлять1000 в недоступный DTO. Demo и неиспользуемый Subusers отделены от live приёмки. Все новые профили должны сохранять search/sort/refresh/clamp, точный target и права,320px и URL scope. +Очередь: clipboard Communities → recursive groups/details → Filters порядок → Firewall/DnD → OSPF таблица+topology → optimizer preview/комбинаторика. Scheduler11, traffic cap50 и commRecs24 проверять на реальной границе, не искусственно подставлять1000 в недоступный DTO. Demo и неиспользуемый Subusers отделены от live приёмки. Все новые профили должны сохранять search/sort/refresh/clamp, точный target и права,320px и URL scope. Принятые после ранней карты: OSPF routes/BFD, WireGuard interfaces/peers/detail, VXLAN/GRE tunnels. Pagination сохраняет полный DTO в памяти и не доказывает серверную производительность. Текущий журнал: [ux-ui-progress.md](ux-ui-progress.md). @@ -78,3 +77,5 @@ Statistics pivot в backend/src/services/statistics-aggregate.ts огранич Аудит источников12.4.5:RouteOptimizerCommRecsDataGrid получает flatRules.filter(sourceServerId===home.id).slice(0,24), максимум24 на один HomeCard; это не cap всей страницы с несколькими HomeCard. WireGuard /wireguard агрегирует полный interfaces/peers устройств без page/limit;1000-профиль применим к реальному пути, а WireGuardPeersDetail дополнительно рендерит полный peers массив раскрытого интерфейса. Эти границы установлены чтением кода, не внешней нагрузочной приёмкой. 12.3.28–12.3.35: WireGuard interfaces1000→50/2136 nodes/1573ms, peers1000→50/2596 nodes/1369ms, раскрытые peers1000→50/2388 nodes; VXLAN1000→50/2137 nodes/1249ms; GRE1000→50/2748 nodes/1609ms. Baseline до по этим экранам не замерялся; сравнение latency между разными dev прогонами не является ускорением production. Evidence и проверки по каждому блоку в ux-ui-stage-12.md. + +12.3.38: Communities1000/190573 JSON bytes →50 DOM rows/2216 page nodes/1206ms dev ready.19 browser/type/lint/build прошли.14 manualPagination:true остаются; обновления/selection по ID подтверждены локально. diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md index 57133ed6..bff84c86 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; далее network grids и полная 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 confirmation; затем recursive groups/endpoints и полная 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 757d582d..0853630c 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.38 — большой каталог Communities (2026-10-04) + +ReUI pagination50, global sort/search, сохранение страницы при refresh и clamp при уменьшении списка. Закреплено значение community; копирование и явная кнопка деталей имеют44px/доступные подписи. Выбор по ID не привязан к странице и сохраняется при сортировке/обновлении; удалённая запись закрывает детали. Кнопка деталей работает с клавиатуры. Права/источник/отсутствующие prefix counts сохранены. + +19 browser (large Communities и Evolution consumers) прошли:1000/190573 JSON bytes →50 rows/2216 page nodes/1206ms dev ready; page20/refresh/search1000/global sort/cache/320px/pinning/axe, clipboard stub,1000→1→0/selection. TypeScript без incremental/ESLint прошли. Первый production build не получил Google Fonts, повторная обычная сборка успешно собрала34 страницы. [Снимок320px](ux-ui/screens/large-communities-320.png) просмотрен. Реальная интеграция Evolution и системный clipboard не проверены. Поиск/выбор Communities пока локальны, восстановления из URL нет. + +Осталось14 manualPagination:true specialized grids. Обнаружено прежнее ложное подтверждение clipboard: handleCopy ставит copied до завершения writeText и игнорирует reject. Следующий блок12.3.39: исправить ack/error/pending копирования и поздние ответы при смене источника; затем12.3.40 recursive groups+endpoints. Общая main/context регрессия после delta и внешняя12.5 отдельно. Коммит локальный без push. + ## 12.3.37 — границы выборки сессий трафика (2026-10-04) FlowAnalyticsPanel сообщает фактическое число строк после фильтра и размер полученной выборки, объясняет top по объёму до50 и локальную область фильтра. Пустой выбранный фильтр имеет собственную причину, вместо общей parent emptyHint; сводка общего conversations не подменяется числом строк. Backend cap/read/network операции не изменены. diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md index 694dc5f2..af006eac 100644 --- a/docs/ux-ui-stage-12.md +++ b/docs/ux-ui-stage-12.md @@ -488,3 +488,12 @@ FlowAnalyticsPanel сообщает фактическое число строк Следующий блок12.3.38: Communities1000 с полным поиском/sort/refresh/выбором/320px по актуальной performance карте.15 manualPagination:true остаются, traffic cap50 не требует фиктивной пагинации. Общая main/context после delta и внешняя12.5 отдельно; реальные RouterOS/PG не проверены. Коммит локальный без push. + +## 12.3.38 — большой каталог Communities (2026-10-04) + +ReUI pagination50, global sort/search, сохранение страницы при refresh и clamp при уменьшении списка. Закреплено значение community; копирование и явная кнопка деталей имеют44px/доступные подписи. Выбор по ID не привязан к странице и сохраняется при сортировке/обновлении; удалённая запись закрывает детали. Кнопка деталей работает с клавиатуры. Права/источник/отсутствующие prefix counts сохранены. + +19 browser (large Communities и Evolution consumers) прошли:1000/190573 JSON bytes →50 rows/2216 page nodes/1206ms dev ready; page20/refresh/search1000/global sort/cache/320px/pinning/axe, clipboard stub,1000→1→0/selection. TypeScript без incremental/ESLint прошли. Первый production build не получил Google Fonts, повторная обычная сборка успешно собрала34 страницы. [Снимок320px](ux-ui/screens/large-communities-320.png) просмотрен. Реальная интеграция Evolution и системный clipboard не проверены. Поиск/выбор Communities пока локальны, восстановления из URL нет. + +Осталось14 manualPagination:true specialized grids. Обнаружено прежнее ложное подтверждение clipboard: handleCopy ставит copied до завершения writeText и игнорирует reject. Следующий блок12.3.39: исправить ack/error/pending копирования и поздние ответы при смене источника; затем12.3.40 recursive groups+endpoints. Общая main/context регрессия после delta и внешняя12.5 отдельно. Коммит локальный без push. + diff --git a/docs/ux-ui/screens/large-communities-320.png b/docs/ux-ui/screens/large-communities-320.png new file mode 100644 index 0000000000000000000000000000000000000000..468a6ce032e943989e218938825c41545ab84a63 GIT binary patch literal 53860 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 literal 0 HcmV?d00001 diff --git a/tests/e2e/large-communities.spec.ts b/tests/e2e/large-communities.spec.ts new file mode 100644 index 00000000..051dafe8 --- /dev/null +++ b/tests/e2e/large-communities.spec.ts @@ -0,0 +1,51 @@ +import { test, expect } from "@playwright/test" +import axe from "axe-core" +import { evoCatalog, evoSettings } from "./fixtures/evobgp" +import { parseEvoBgpCatalog } from "../../lib/evobgp-catalog" + +test("Communities1000: full search/sort/page/selection/cache/320px", async ({ page }, info) => { + test.setTimeout(90000) + let communities = Array.from({ length: 1000 }, (_, i) => ({ id: "c" + (i + 1), value: "64512:" + (i + 1), name: "community-" + String(i + 1).padStart(4, "0"), description: "Описание", type: "custom", filterIds: [], serverCount: i + 1, prefixCount: i + 1, action: "permit", enabled: true })) + let failed = false, calls = 0 + const writes: string[] = [] + await page.addInitScript(() => Object.defineProperty(navigator, "clipboard", { value: { writeText: async (value: string) => { (window as unknown as { copied: string }).copied = value } } })) + await page.route("**/health", r => r.fulfill({ json: { ok: true } })) + await page.route("**/api/**", r => { + const p = new URL(r.request().url()).pathname + if (r.request().method() !== "GET" && p !== "/api/evobgp/catalog") writes.push(p) + if (p === "/api/evobgp/catalog") { calls++; return r.fulfill(failed ? { status: 503, json: { error: "Каталог недоступен тест" } } : { json: parseEvoBgpCatalog({ ...evoCatalog, communities }) }) } + return r.fulfill({ json: p === "/api/auth/config" ? { required: false } : p === "/api/evobgp/settings" ? evoSettings : {} }) + }) + const started = Date.now(); await page.goto("/communities") + const rows = page.locator("tr[data-row-id]"); await expect(rows).toHaveCount(50) + const profile = { count: communities.length, jsonBytes: Buffer.byteLength(JSON.stringify(communities)), rows: await rows.count(), nodes: await page.locator("*").count(), readyMs: Date.now() - started } + console.log("Communities profile", JSON.stringify(profile)); await info.attach("profile", { body: JSON.stringify(profile), contentType: "application/json" }) + const next = page.getByRole("button", { name: "Следующая страница", exact: true }) + for (let i = 0; i < 19; i++) await next.click() + await expect(next).toBeDisabled(); await expect(rows.first()).toContainText("community-0951") + const refresh = page.getByRole("button", { name: "Обновить каталог EvoBGP", exact: true }) + const before = calls; await refresh.click(); await expect.poll(() => calls).toBeGreaterThan(before); await expect(rows.first()).toContainText("community-0951") + const search = page.getByPlaceholder("Поиск community…") + await search.fill("community-1000"); await expect(rows).toHaveCount(1) + const detailButton = rows.first().getByRole("button", { name: "Детали community: 64512:1000", exact: true }) + await detailButton.focus(); await page.keyboard.press("Enter") + await expect(page.getByText("Скопировать значение", { exact: true })).toBeVisible() + await expect(page.getByRole("button", { name: "Изменить community", exact: true })).toBeDisabled() + await search.fill(""); await expect(rows).toHaveCount(50) + await page.getByRole("table").getByRole("button", { name: "Серверов", exact: true }).click(); await expect(rows.first()).toContainText("community-1000") + await next.click(); const previous = await rows.first().getAttribute("data-row-id") + failed = true; await refresh.click(); await expect(page.getByText("Каталог не обновлён; показаны последние данные", { exact: true })).toBeVisible() + await expect(rows.first()).toHaveAttribute("data-row-id", previous!) + await page.setViewportSize({ width: 320, height: 1000 }) + const copy = rows.first().getByRole("button", { name: /Скопировать community:/ }) + await copy.scrollIntoViewIfNeeded(); const viewport = page.locator('[data-slot="data-grid"] [data-slot="scroll-area-viewport"]') + await viewport.evaluate(el => { el.scrollLeft = el.scrollWidth }); await expect(copy).toBeInViewport() + await copy.click(); expect(await page.evaluate(() => (window as unknown as { copied: string }).copied)).toBe("64512:950") + await expect(page.getByText("Скопировать значение", { exact: true })).toHaveCount(1) + await expect.poll(() => page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(320) + await page.addScriptTag({ content: axe.source }); expect((await page.evaluate(async () => (window as unknown as { axe: typeof axe }).axe.run(document.querySelector('[data-slot="data-grid"]')!, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] } }))).violations).toEqual([]) + await page.addStyleTag({ content: "nextjs-portal{display:none!important}" }); await page.screenshot({ path: info.outputPath("communities-320.png"), animations: "disabled" }) + failed = false; communities = [communities[999]]; await refresh.click(); await expect(rows).toHaveCount(1); await expect(rows.first()).toContainText("community-1000") + communities = []; await refresh.click(); await expect(rows).toHaveCount(0); await expect(page.getByText("Выберите community", { exact: false })).toBeVisible() + expect(writes).toEqual([]) +})