fix(gre): сохранять меню действий при обновлении таблицы
This commit is contained in:
1 parent
26e8225815
commit
731803bade
7 files changed
+170
-85
No files matched your search
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { DataGridContainer, DataGridScrollArea, DataGridTable, dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid"
|
import { DataGridContainer, DataGridScrollArea, DataGridTable, dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid"
|
||||||
|
|
||||||
import { useEffect, useMemo } from "react"
|
import { createContext, useContext, useEffect, useMemo } from "react"
|
||||||
import {
|
import {
|
||||||
type ColumnDef,
|
type ColumnDef,
|
||||||
useTable,
|
useTable,
|
||||||
@@ -111,6 +111,82 @@ interface GreTunnelsDataGridProps {
|
|||||||
emptyDescription?: string
|
emptyDescription?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GreActions = Pick<GreTunnelsDataGridProps, "onCodePreview" | "onEdit" | "onToggle" | "onDelete" | "mutationsLocked" | "canManage">
|
||||||
|
|
||||||
|
const GreActionsContext = createContext<GreActions | null>(null)
|
||||||
|
|
||||||
|
// Keep the cell component identity stable while permissions and callbacks update.
|
||||||
|
function GreActionsCell({ row }: { row: { original: GreTunnel } }) {
|
||||||
|
const actions = useContext(GreActionsContext)
|
||||||
|
if (!actions) throw new Error("GRE actions context is missing")
|
||||||
|
const { onCodePreview, onEdit, onToggle, onDelete, mutationsLocked, canManage } = actions
|
||||||
|
const t = row.original
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-1 justify-end">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className={cn(
|
||||||
|
"size-11 shrink-0",
|
||||||
|
)}
|
||||||
|
title="Предпросмотр кода RouterOS"
|
||||||
|
aria-label={`Просмотр кода: ${t.name}`}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
onCodePreview(t)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CodeXmlIcon className="size-3.5" />
|
||||||
|
</Button>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger
|
||||||
|
render={
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
className={cn(
|
||||||
|
"size-11 shrink-0 border-border/60 bg-background/80 text-muted-foreground shadow-none",
|
||||||
|
"opacity-100",
|
||||||
|
"data-popup-open:opacity-100",
|
||||||
|
)}
|
||||||
|
aria-label={`Действия: ${t.name}`}
|
||||||
|
>
|
||||||
|
<MoreHorizontalIcon className="size-4" />
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<DropdownMenuContent side="bottom" align="end">
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuLabel>{t.name}</DropdownMenuLabel>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem onClick={() => onCodePreview(t)}>
|
||||||
|
<CodeXmlIcon className="size-4" /> Просмотр кода
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem disabled={mutationsLocked || (canManage && !canManage(t.serverId)) || !onEdit} onClick={() => onEdit?.(t)}>
|
||||||
|
<PencilIcon className="size-4" /> Редактировать
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem disabled={mutationsLocked || (canManage && !canManage(t.serverId)) || !onToggle} onClick={() => onToggle?.(t)}>
|
||||||
|
<PowerIcon className="size-4" />
|
||||||
|
{t.enabled ? "Выключить" : "Включить"}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem
|
||||||
|
variant="destructive"
|
||||||
|
disabled={mutationsLocked || (canManage && !canManage(t.serverId)) || !onDelete}
|
||||||
|
onClick={() => onDelete?.(t)}
|
||||||
|
>
|
||||||
|
<Trash2Icon className="size-4" /> Удалить туннель
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function GreTunnelsDataGrid({
|
function GreTunnelsDataGrid({
|
||||||
tunnels,
|
tunnels,
|
||||||
servers,
|
servers,
|
||||||
@@ -281,78 +357,12 @@ function GreTunnelsDataGrid({
|
|||||||
id: "actions",
|
id: "actions",
|
||||||
header: () => <span className="sr-only">Действия</span>,
|
header: () => <span className="sr-only">Действия</span>,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
cell: ({ row }) => {
|
cell: GreActionsCell,
|
||||||
const t = row.original
|
|
||||||
return (
|
|
||||||
<div className="flex items-center gap-1 justify-end">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className={cn(
|
|
||||||
"size-11 shrink-0",
|
|
||||||
)}
|
|
||||||
title="Предпросмотр кода RouterOS"
|
|
||||||
aria-label={`Просмотр кода: ${t.name}`}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
onCodePreview(t)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CodeXmlIcon className="size-3.5" />
|
|
||||||
</Button>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger
|
|
||||||
render={
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
size="icon"
|
|
||||||
className={cn(
|
|
||||||
"size-11 shrink-0 border-border/60 bg-background/80 text-muted-foreground shadow-none",
|
|
||||||
"opacity-100",
|
|
||||||
"data-popup-open:opacity-100",
|
|
||||||
)}
|
|
||||||
aria-label={`Действия: ${t.name}`}
|
|
||||||
>
|
|
||||||
<MoreHorizontalIcon className="size-4" />
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<DropdownMenuContent side="bottom" align="end">
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
<DropdownMenuLabel>{t.name}</DropdownMenuLabel>
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem onClick={() => onCodePreview(t)}>
|
|
||||||
<CodeXmlIcon className="size-4" /> Просмотр кода
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem disabled={mutationsLocked || (canManage && !canManage(t.serverId)) || !onEdit} onClick={() => onEdit?.(t)}>
|
|
||||||
<PencilIcon className="size-4" /> Редактировать
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem disabled={mutationsLocked || (canManage && !canManage(t.serverId)) || !onToggle} onClick={() => onToggle?.(t)}>
|
|
||||||
<PowerIcon className="size-4" />
|
|
||||||
{t.enabled ? "Выключить" : "Включить"}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem
|
|
||||||
variant="destructive"
|
|
||||||
disabled={mutationsLocked || (canManage && !canManage(t.serverId)) || !onDelete}
|
|
||||||
onClick={() => onDelete?.(t)}
|
|
||||||
>
|
|
||||||
<Trash2Icon className="size-4" /> Удалить туннель
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
size: 112,
|
size: 112,
|
||||||
meta: { headerClassName: DATA_GRID_CELL_PAD_LAST, cellClassName: DATA_GRID_CELL_PAD_LAST },
|
meta: { headerClassName: DATA_GRID_CELL_PAD_LAST, cellClassName: DATA_GRID_CELL_PAD_LAST },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[onCodePreview, onEdit, onToggle, onDelete, mutationsLocked, canManage, unknownServerIds, poolMap, serverMap],
|
[unknownServerIds, poolMap, serverMap],
|
||||||
)
|
)
|
||||||
|
|
||||||
const table = useTable({
|
const table = useTable({
|
||||||
@@ -383,17 +393,19 @@ function GreTunnelsDataGrid({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataGridShell
|
<GreActionsContext.Provider value={{ onCodePreview, onEdit, onToggle, onDelete, mutationsLocked, canManage }}>
|
||||||
table={table}
|
<DataGridShell
|
||||||
recordCount={tunnels.length}
|
table={table}
|
||||||
pagination={tunnels.length > 50 || pageSize !== 50}
|
recordCount={tunnels.length}
|
||||||
tableLayout={{ dense: true, columnsPinnable: true }}
|
pagination={tunnels.length > 50 || pageSize !== 50}
|
||||||
tableClassNames={{ headerRow: "border-b border-border", bodyRow: "group/row" }}
|
tableLayout={{ dense: true, columnsPinnable: true }}
|
||||||
>
|
tableClassNames={{ headerRow: "border-b border-border", bodyRow: "group/row" }}
|
||||||
<DataGridContainer border={false} className="w-full rounded-none border-0">
|
>
|
||||||
<DataGridScrollArea orientation="horizontal"><DataGridTable /></DataGridScrollArea>
|
<DataGridContainer border={false} className="w-full rounded-none border-0">
|
||||||
</DataGridContainer>
|
<DataGridScrollArea orientation="horizontal"><DataGridTable /></DataGridScrollArea>
|
||||||
</DataGridShell>
|
</DataGridContainer>
|
||||||
|
</DataGridShell>
|
||||||
|
</GreActionsContext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -377,3 +377,11 @@ gre-comment.test.ts проверил create plan→native comment→map→snapsh
|
|||||||
Первые browser попытки остановились на меню, которое исчезало при перерисовке. Таймер первой пары удалил собственные объекты. Немедленная сверка /ip/route дала mismatch; финальный harness добавил ограниченное ожидание сходимости маршрутов, не меняя их вручную. Итоговый запуск exit0: DELETE200/200, все выбранные поля GRE/IP/routes/IPv4 Firewall совпали с baseline; CHR1 потребовал одно повторное чтение через250ms, CHR2 совпал сразу. Тестовое меню в успешном проходе пришлось открыть повторно1раз: это не исправленный UX, а отдельная выявленная недоработка для54. Проверка не является доказательством неизменности всех runtime параметров/полного export.
|
Первые browser попытки остановились на меню, которое исчезало при перерисовке. Таймер первой пары удалил собственные объекты. Немедленная сверка /ip/route дала mismatch; финальный harness добавил ограниченное ожидание сходимости маршрутов, не меняя их вручную. Итоговый запуск exit0: DELETE200/200, все выбранные поля GRE/IP/routes/IPv4 Firewall совпали с baseline; CHR1 потребовал одно повторное чтение через250ms, CHR2 совпал сразу. Тестовое меню в успешном проходе пришлось открыть повторно1раз: это не исправленный UX, а отдельная выявленная недоработка для54. Проверка не является доказательством неизменности всех runtime параметров/полного export.
|
||||||
|
|
||||||
Own GRE интерфейсы, адреса и connected route удалены; эфемерный контейнер и туннель8018 закрыты, остались три основные Compose-службы. Main controls до/после: servers2/speedHistory9/retention14/migrations9 совпадают; GRE audit revisions намеренно сохранены. Следующий12.5.54 — сохранить устойчивость меню действий GRE при перерисовке и проверить профильный сценарий; затем продолжать настоящие VPN объекты. Права, частичные отказы остальных VPN, Portal/Telegram и прочие внешние ограничения остаются открытыми. Локальный коммит без push.
|
Own GRE интерфейсы, адреса и connected route удалены; эфемерный контейнер и туннель8018 закрыты, остались три основные Compose-службы. Main controls до/после: servers2/speedHistory9/retention14/migrations9 совпадают; GRE audit revisions намеренно сохранены. Следующий12.5.54 — сохранить устойчивость меню действий GRE при перерисовке и проверить профильный сценарий; затем продолжать настоящие VPN объекты. Права, частичные отказы остальных VPN, Portal/Telegram и прочие внешние ограничения остаются открытыми. Локальный коммит без push.
|
||||||
|
|
||||||
|
## 12.5.54 — устойчивость меню GRE при обновлении таблицы (2026-10-08)
|
||||||
|
|
||||||
|
При обновлении callbacks/прав пересоздавался inline renderer actions column; React монтировал ячейку заново и закрывал Base UI меню. В components/data-grids/gre-tunnels-data-grid.tsx вынесен стабильный GreActionsCell, текущие callbacks/permissions передаются через типизированный React context. ReUI composition, оформление, API таблицы и ключ строки serverId/id сохранены. Политика блокировки операций при загрузке и отсутствии доступа сохранена.
|
||||||
|
|
||||||
|
Проверки: новый tests/e2e/gre-menu-refresh.spec.ts открывает меню, запускает GET без outside pointer event, проверяет сохранение меню, disabled во время загрузки, восстановление enabled и свежий peer IP в форме. На прежнем HEAD тест падает именно из-за исчезнувшего menuitem; исправленный код проходит. Вместе с gre-toggle.spec.ts — 3/3 passed. Scoped TypeScript (tmp/tsconfig-speed-check.json) и ESLint изменённых файлов passed. Первую фикстуру скорректировали: инициализация источника допускает несколько GET, задержка включается только при проверяемом refresh. ReUI data-grid checklist проверен, общие компоненты не менялись.
|
||||||
|
|
||||||
|
Это локальное исправление. На тестовом сервере backend/frontend bd3f996; live CHR сценарий стабильности меню пока не проверен. Следующий12.5.55 — собрать и развернуть frontend, создать собственную реальную GRE пару, проверить меню при обновлении и форму, затем удалить только собственные интерфейсы/IP и проверить baseline. Реальные VPN объекты обязательны по указанию пользователя. Telegram/Portal/multi-instance и остальные внешние ограничения остаются открытыми. Локальный коммит без push.
|
||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
# План реализации UX/UI по пятичасовым окнам
|
# План реализации UX/UI по пятичасовым окнам
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-08: 12.5.53 развернул backend/frontendbd3f996 и подтвердил peer IP на новой реальной GRE паре: receipt/native/snapshot/restore/MTU/toggle/clear и UI/reload/320px passed, Ping3/3 в обе стороны. Own GRE/IP/connected routes удалены, выбранная конфигурация и DB controls восстановлены; backend healthy. Осталась UX-недоработка: меню действий закрывается при перерисовке, потребовалось повторное открытие. Следующий12.5.54 — стабильность меню GRE при обновлении данных; реальные VPN тестовые объекты создавать и очищать по правилу пользователя.
|
Актуальное продолжение 2026-10-08: 12.5.54 локально исправил закрытие меню GRE при перерисовке: стабильный компонент ячейки получает актуальные обработчики и права через React context. Playwright 3/3, scoped TypeScript и ESLint passed; новый тест воспроизводит сбой на прежнем коде. Исправление ещё не развёрнуто: удалённый backend/frontend остаётся bd3f996. Следующий12.5.55 — Docker build/deploy frontend и повторная проверка меню на собственной реальной GRE паре с очисткой. Этап12.5 остаётся открытым.
|
||||||
|
|
||||||
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-08. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-08. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
||||||
|
|
||||||
## Актуальная точка продолжения — 2026-10-08
|
## Актуальная точка продолжения — 2026-10-08
|
||||||
|
|
||||||
Этапы0–11 завершены в описанных локальных границах. Этап12 открыт. На тестовом сервере161.104.46.222 работают backend/frontendbd3f996 и два CHR. Внешние проверки подтвердили PG backup/restore/rollback, остановку фоновой работы для одного экземпляра, API JWT/RBAC и браузерные состояния восстановления. Native TCP/UDP speed, история и UI проверены в границах журнала; доступ ко второму CHR восстановлен пользователем. Очередь Telegram проверена на реальном PG: dedupe, конкурентный claim, retry/version, ошибки сохранения и неопределённый исход; ответы отправителя подменялись, реальной доставки это не доказывает. Блоки12.5.43–45 подтвердили browser retry/checkpoint и развернули понятное пояснение HTTP500. 12.5.46 подтвердил browser JWT/RBAC очереди на реальном HTTP/PG; 12.5.47 подтвердил изоляцию checkpoint при смене двух реальных HTTP/PG источников в конфигурации local; 12.5.48 подтвердил scoped axe/клавиатуру в обеих темах и320px; 12.5.49 собрал Q-матрицу и подтвердил отсутствие настройки Telegram; 12.5.50 подтвердил GET-only инвентаризацию: VPN/BGP/OSPF пусты, роли home-router/home-router; По уточнению пользователя12.5.51 создал реальные GRE туннели и подтвердил lifecycle/связность; обнаружена потеря peer IP, 12.5.52 локально исправил сохранение peer metadata, профильные проверки passed; 12.5.53 развернул исправление и подтвердил его на реальной GRE паре/API/UI; следующий12.5.54 — стабильность меню действий GRE при перерисовке. Реальная Telegram доставка, Portal SSO, физический TCP disconnect, multi-instance и остальные профильные сетевые сценарии остаются открытыми. [Протокол](ux-ui-external-acceptance.md) и [журнал](ux-ui-stage-12.md) содержат доказательства и ограничения.
|
Этапы0–11 завершены в описанных локальных границах. Этап12 открыт. На тестовом сервере161.104.46.222 работают backend/frontendbd3f996 и два CHR. Внешние проверки подтвердили PG backup/restore/rollback, остановку фоновой работы для одного экземпляра, API JWT/RBAC и браузерные состояния восстановления. Native TCP/UDP speed, история и UI проверены в границах журнала; доступ ко второму CHR восстановлен пользователем. Очередь Telegram проверена на реальном PG: dedupe, конкурентный claim, retry/version, ошибки сохранения и неопределённый исход; ответы отправителя подменялись, реальной доставки это не доказывает. Блоки12.5.43–45 подтвердили browser retry/checkpoint и развернули понятное пояснение HTTP500. 12.5.46 подтвердил browser JWT/RBAC очереди на реальном HTTP/PG; 12.5.47 подтвердил изоляцию checkpoint при смене двух реальных HTTP/PG источников в конфигурации local; 12.5.48 подтвердил scoped axe/клавиатуру в обеих темах и320px; 12.5.49 собрал Q-матрицу и подтвердил отсутствие настройки Telegram; 12.5.50 подтвердил GET-only инвентаризацию: VPN/BGP/OSPF пусты, роли home-router/home-router; По уточнению пользователя12.5.51 создал реальные GRE туннели и подтвердил lifecycle/связность; обнаружена потеря peer IP, 12.5.52 локально исправил сохранение peer metadata, профильные проверки passed; 12.5.53 развернул исправление и подтвердил его на реальной GRE паре/API/UI; 12.5.54 локально исправил устойчивость меню GRE, профильные проверки passed; следующий12.5.55 — frontend Docker deploy и повторная проверка на реальной GRE паре. Реальная Telegram доставка, Portal SSO, физический TCP disconnect, multi-instance и остальные профильные сетевые сценарии остаются открытыми. [Протокол](ux-ui-external-acceptance.md) и [журнал](ux-ui-stage-12.md) содержат доказательства и ограничения.
|
||||||
|
|
||||||
Выполненные внешние сценарии не заменяют приёмку оставшихся интеграций и условий эксплуатации. Датированные продолжения ниже — история; точный следующий шаг указан в начале [журнала](ux-ui-progress.md).
|
Выполненные внешние сценарии не заменяют приёмку оставшихся интеграций и условий эксплуатации. Датированные продолжения ниже — история; точный следующий шаг указан в начале [журнала](ux-ui-progress.md).
|
||||||
|
|
||||||
|
|||||||
+10
-2
@@ -1,6 +1,6 @@
|
|||||||
# UX/UI: журнал реализации
|
# UX/UI: журнал реализации
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-08: 12.5.53 развернул backend/frontendbd3f996 и подтвердил peer IP на новой реальной GRE паре: receipt/native/snapshot/restore/MTU/toggle/clear и UI/reload/320px passed, Ping3/3 в обе стороны. Own GRE/IP/connected routes удалены, выбранная конфигурация и DB controls восстановлены; backend healthy. Осталась UX-недоработка: меню действий закрывается при перерисовке, потребовалось повторное открытие. Следующий12.5.54 — стабильность меню GRE при обновлении данных; реальные VPN тестовые объекты создавать и очищать по правилу пользователя.
|
Актуальное продолжение 2026-10-08: 12.5.54 локально исправил закрытие меню GRE при перерисовке: стабильный компонент ячейки получает актуальные обработчики и права через React context. Playwright 3/3, scoped TypeScript и ESLint passed; новый тест воспроизводит сбой на прежнем коде. Исправление ещё не развёрнуто: удалённый backend/frontend остаётся bd3f996. Следующий12.5.55 — Docker build/deploy frontend и повторная проверка меню на собственной реальной GRE паре с очисткой. Этап12.5 остаётся открытым.
|
||||||
|
|
||||||
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
|
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
|
||||||
|
|
||||||
@@ -34,7 +34,15 @@
|
|||||||
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
||||||
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
|
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
|
||||||
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
|
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
|
||||||
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–53 и ограничения ниже. Runtime backend/frontendbd3f996. |
|
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–54 и ограничения ниже. Runtime backend/frontendbd3f996. |
|
||||||
|
|
||||||
|
## 12.5.54 — устойчивость меню GRE при обновлении таблицы (2026-10-08)
|
||||||
|
|
||||||
|
При обновлении callbacks/прав пересоздавался inline renderer actions column; React монтировал ячейку заново и закрывал Base UI меню. В components/data-grids/gre-tunnels-data-grid.tsx вынесен стабильный GreActionsCell, текущие callbacks/permissions передаются через типизированный React context. ReUI composition, оформление, API таблицы и ключ строки serverId/id сохранены. Политика блокировки операций при загрузке и отсутствии доступа сохранена.
|
||||||
|
|
||||||
|
Проверки: новый tests/e2e/gre-menu-refresh.spec.ts открывает меню, запускает GET без outside pointer event, проверяет сохранение меню, disabled во время загрузки, восстановление enabled и свежий peer IP в форме. На прежнем HEAD тест падает именно из-за исчезнувшего menuitem; исправленный код проходит. Вместе с gre-toggle.spec.ts — 3/3 passed. Scoped TypeScript (tmp/tsconfig-speed-check.json) и ESLint изменённых файлов passed. Первую фикстуру скорректировали: инициализация источника допускает несколько GET, задержка включается только при проверяемом refresh. ReUI data-grid checklist проверен, общие компоненты не менялись.
|
||||||
|
|
||||||
|
Это локальное исправление. На тестовом сервере backend/frontend bd3f996; live CHR сценарий стабильности меню пока не проверен. Следующий12.5.55 — собрать и развернуть frontend, создать собственную реальную GRE пару, проверить меню при обновлении и форму, затем удалить только собственные интерфейсы/IP и проверить baseline. Реальные VPN объекты обязательны по указанию пользователя. Telegram/Portal/multi-instance и остальные внешние ограничения остаются открытыми. Локальный коммит без push.
|
||||||
|
|
||||||
## 12.5.53 — развёртывание peer metadata и реальный GRE regression (2026-10-08)
|
## 12.5.53 — развёртывание peer metadata и реальный GRE regression (2026-10-08)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Этап 12: итоговая приёмка UX/UI
|
# Этап 12: итоговая приёмка UX/UI
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-08: 12.5.53 развернул backend/frontendbd3f996 и подтвердил peer IP на новой реальной GRE паре: receipt/native/snapshot/restore/MTU/toggle/clear и UI/reload/320px passed, Ping3/3 в обе стороны. Own GRE/IP/connected routes удалены, выбранная конфигурация и DB controls восстановлены; backend healthy. Осталась UX-недоработка: меню действий закрывается при перерисовке, потребовалось повторное открытие. Следующий12.5.54 — стабильность меню GRE при обновлении данных; реальные VPN тестовые объекты создавать и очищать по правилу пользователя.
|
Актуальное продолжение 2026-10-08: 12.5.54 локально исправил закрытие меню GRE при перерисовке: стабильный компонент ячейки получает актуальные обработчики и права через React context. Playwright 3/3, scoped TypeScript и ESLint passed; новый тест воспроизводит сбой на прежнем коде. Исправление ещё не развёрнуто: удалённый backend/frontend остаётся bd3f996. Следующий12.5.55 — Docker build/deploy frontend и повторная проверка меню на собственной реальной GRE паре с очисткой. Этап12.5 остаётся открытым.
|
||||||
|
|
||||||
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
|
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
|
||||||
|
|
||||||
@@ -1542,3 +1542,11 @@ gre-comment.test.ts проверил create plan→native comment→map→snapsh
|
|||||||
Первые browser попытки остановились на меню, которое исчезало при перерисовке. Таймер первой пары удалил собственные объекты. Немедленная сверка /ip/route дала mismatch; финальный harness добавил ограниченное ожидание сходимости маршрутов, не меняя их вручную. Итоговый запуск exit0: DELETE200/200, все выбранные поля GRE/IP/routes/IPv4 Firewall совпали с baseline; CHR1 потребовал одно повторное чтение через250ms, CHR2 совпал сразу. Тестовое меню в успешном проходе пришлось открыть повторно1раз: это не исправленный UX, а отдельная выявленная недоработка для54. Проверка не является доказательством неизменности всех runtime параметров/полного export.
|
Первые browser попытки остановились на меню, которое исчезало при перерисовке. Таймер первой пары удалил собственные объекты. Немедленная сверка /ip/route дала mismatch; финальный harness добавил ограниченное ожидание сходимости маршрутов, не меняя их вручную. Итоговый запуск exit0: DELETE200/200, все выбранные поля GRE/IP/routes/IPv4 Firewall совпали с baseline; CHR1 потребовал одно повторное чтение через250ms, CHR2 совпал сразу. Тестовое меню в успешном проходе пришлось открыть повторно1раз: это не исправленный UX, а отдельная выявленная недоработка для54. Проверка не является доказательством неизменности всех runtime параметров/полного export.
|
||||||
|
|
||||||
Own GRE интерфейсы, адреса и connected route удалены; эфемерный контейнер и туннель8018 закрыты, остались три основные Compose-службы. Main controls до/после: servers2/speedHistory9/retention14/migrations9 совпадают; GRE audit revisions намеренно сохранены. Следующий12.5.54 — сохранить устойчивость меню действий GRE при перерисовке и проверить профильный сценарий; затем продолжать настоящие VPN объекты. Права, частичные отказы остальных VPN, Portal/Telegram и прочие внешние ограничения остаются открытыми. Локальный коммит без push.
|
Own GRE интерфейсы, адреса и connected route удалены; эфемерный контейнер и туннель8018 закрыты, остались три основные Compose-службы. Main controls до/после: servers2/speedHistory9/retention14/migrations9 совпадают; GRE audit revisions намеренно сохранены. Следующий12.5.54 — сохранить устойчивость меню действий GRE при перерисовке и проверить профильный сценарий; затем продолжать настоящие VPN объекты. Права, частичные отказы остальных VPN, Portal/Telegram и прочие внешние ограничения остаются открытыми. Локальный коммит без push.
|
||||||
|
|
||||||
|
## 12.5.54 — устойчивость меню GRE при обновлении таблицы (2026-10-08)
|
||||||
|
|
||||||
|
При обновлении callbacks/прав пересоздавался inline renderer actions column; React монтировал ячейку заново и закрывал Base UI меню. В components/data-grids/gre-tunnels-data-grid.tsx вынесен стабильный GreActionsCell, текущие callbacks/permissions передаются через типизированный React context. ReUI composition, оформление, API таблицы и ключ строки serverId/id сохранены. Политика блокировки операций при загрузке и отсутствии доступа сохранена.
|
||||||
|
|
||||||
|
Проверки: новый tests/e2e/gre-menu-refresh.spec.ts открывает меню, запускает GET без outside pointer event, проверяет сохранение меню, disabled во время загрузки, восстановление enabled и свежий peer IP в форме. На прежнем HEAD тест падает именно из-за исчезнувшего menuitem; исправленный код проходит. Вместе с gre-toggle.spec.ts — 3/3 passed. Scoped TypeScript (tmp/tsconfig-speed-check.json) и ESLint изменённых файлов passed. Первую фикстуру скорректировали: инициализация источника допускает несколько GET, задержка включается только при проверяемом refresh. ReUI data-grid checklist проверен, общие компоненты не менялись.
|
||||||
|
|
||||||
|
Это локальное исправление. На тестовом сервере backend/frontend bd3f996; live CHR сценарий стабильности меню пока не проверен. Следующий12.5.55 — собрать и развернуть frontend, создать собственную реальную GRE пару, проверить меню при обновлении и форму, затем удалить только собственные интерфейсы/IP и проверить baseline. Реальные VPN объекты обязательны по указанию пользователя. Telegram/Portal/multi-instance и остальные внешние ограничения остаются открытыми. Локальный коммит без push.
|
||||||
@@ -385,3 +385,11 @@ gre-comment.test.ts проверил create plan→native comment→map→snapsh
|
|||||||
Первые browser попытки остановились на меню, которое исчезало при перерисовке. Таймер первой пары удалил собственные объекты. Немедленная сверка /ip/route дала mismatch; финальный harness добавил ограниченное ожидание сходимости маршрутов, не меняя их вручную. Итоговый запуск exit0: DELETE200/200, все выбранные поля GRE/IP/routes/IPv4 Firewall совпали с baseline; CHR1 потребовал одно повторное чтение через250ms, CHR2 совпал сразу. Тестовое меню в успешном проходе пришлось открыть повторно1раз: это не исправленный UX, а отдельная выявленная недоработка для54. Проверка не является доказательством неизменности всех runtime параметров/полного export.
|
Первые browser попытки остановились на меню, которое исчезало при перерисовке. Таймер первой пары удалил собственные объекты. Немедленная сверка /ip/route дала mismatch; финальный harness добавил ограниченное ожидание сходимости маршрутов, не меняя их вручную. Итоговый запуск exit0: DELETE200/200, все выбранные поля GRE/IP/routes/IPv4 Firewall совпали с baseline; CHR1 потребовал одно повторное чтение через250ms, CHR2 совпал сразу. Тестовое меню в успешном проходе пришлось открыть повторно1раз: это не исправленный UX, а отдельная выявленная недоработка для54. Проверка не является доказательством неизменности всех runtime параметров/полного export.
|
||||||
|
|
||||||
Own GRE интерфейсы, адреса и connected route удалены; эфемерный контейнер и туннель8018 закрыты, остались три основные Compose-службы. Main controls до/после: servers2/speedHistory9/retention14/migrations9 совпадают; GRE audit revisions намеренно сохранены. Следующий12.5.54 — сохранить устойчивость меню действий GRE при перерисовке и проверить профильный сценарий; затем продолжать настоящие VPN объекты. Права, частичные отказы остальных VPN, Portal/Telegram и прочие внешние ограничения остаются открытыми. Локальный коммит без push.
|
Own GRE интерфейсы, адреса и connected route удалены; эфемерный контейнер и туннель8018 закрыты, остались три основные Compose-службы. Main controls до/после: servers2/speedHistory9/retention14/migrations9 совпадают; GRE audit revisions намеренно сохранены. Следующий12.5.54 — сохранить устойчивость меню действий GRE при перерисовке и проверить профильный сценарий; затем продолжать настоящие VPN объекты. Права, частичные отказы остальных VPN, Portal/Telegram и прочие внешние ограничения остаются открытыми. Локальный коммит без push.
|
||||||
|
|
||||||
|
## 12.5.54 — устойчивость меню GRE при обновлении таблицы (2026-10-08)
|
||||||
|
|
||||||
|
При обновлении callbacks/прав пересоздавался inline renderer actions column; React монтировал ячейку заново и закрывал Base UI меню. В components/data-grids/gre-tunnels-data-grid.tsx вынесен стабильный GreActionsCell, текущие callbacks/permissions передаются через типизированный React context. ReUI composition, оформление, API таблицы и ключ строки serverId/id сохранены. Политика блокировки операций при загрузке и отсутствии доступа сохранена.
|
||||||
|
|
||||||
|
Проверки: новый tests/e2e/gre-menu-refresh.spec.ts открывает меню, запускает GET без outside pointer event, проверяет сохранение меню, disabled во время загрузки, восстановление enabled и свежий peer IP в форме. На прежнем HEAD тест падает именно из-за исчезнувшего menuitem; исправленный код проходит. Вместе с gre-toggle.spec.ts — 3/3 passed. Scoped TypeScript (tmp/tsconfig-speed-check.json) и ESLint изменённых файлов passed. Первую фикстуру скорректировали: инициализация источника допускает несколько GET, задержка включается только при проверяемом refresh. ReUI data-grid checklist проверен, общие компоненты не менялись.
|
||||||
|
|
||||||
|
Это локальное исправление. На тестовом сервере backend/frontend bd3f996; live CHR сценарий стабильности меню пока не проверен. Следующий12.5.55 — собрать и развернуть frontend, создать собственную реальную GRE пару, проверить меню при обновлении и форму, затем удалить только собственные интерфейсы/IP и проверить baseline. Реальные VPN объекты обязательны по указанию пользователя. Telegram/Portal/multi-instance и остальные внешние ограничения остаются открытыми. Локальный коммит без push.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { test, expect } from "@playwright/test"
|
||||||
|
|
||||||
|
test("GRE: открытое меню переживает обновление и использует свежие права и данные", async ({ page }) => {
|
||||||
|
let loads = 0
|
||||||
|
let refreshing = false
|
||||||
|
let release: (() => void) | undefined
|
||||||
|
const gate = new Promise<void>(resolve => { release = resolve })
|
||||||
|
const tunnel = { id: "*M", name: "gre-menu", serverId: "1", localAddress: "192.0.2.1", remoteAddress: "192.0.2.2", localInnerIp: "10.0.0.1/30", remoteInnerIp: "10.0.0.2/30", poolId: "pool", ipsec: null, mtu: 1400, keepaliveInterval: 10, keepaliveRetries: 3, dscp: "inherit", clampTcpMss: true, allowFastPath: true, comment: "", enabled: true, status: "up" }
|
||||||
|
await page.route("**/health", route => route.fulfill({ json: { ok: true } }))
|
||||||
|
await page.route("**/api/**", async route => {
|
||||||
|
const path = new URL(route.request().url()).pathname
|
||||||
|
if (path === "/api/gre/tunnels") {
|
||||||
|
loads++
|
||||||
|
if (refreshing) await gate
|
||||||
|
return route.fulfill({ json: { tunnels: [{ ...tunnel, remoteInnerIp: refreshing ? "10.0.0.6/30" : tunnel.remoteInnerIp }] } })
|
||||||
|
}
|
||||||
|
return route.fulfill({ json: path === "/api/auth/config" ? { required: false }
|
||||||
|
: path === "/api/servers" ? [{ id: 1, name: "Роутер", host: "192.0.2.1", enabled: true, type: "exit-node", country: "RU", status: "online" }] : {} })
|
||||||
|
})
|
||||||
|
await page.goto("/gre")
|
||||||
|
const refresh = page.getByRole("button", { name: "Обновить", exact: true })
|
||||||
|
await expect(refresh).toBeEnabled()
|
||||||
|
await page.getByRole("button", { name: "Действия: gre-menu" }).click()
|
||||||
|
const edit = page.getByRole("menuitem", { name: /Редактировать/ })
|
||||||
|
await expect(edit).toBeEnabled()
|
||||||
|
// Invoke refresh without an outside pointer event: that event should close a menu.
|
||||||
|
const initialLoads = loads
|
||||||
|
refreshing = true
|
||||||
|
await refresh.evaluate(element => (element as HTMLButtonElement).click())
|
||||||
|
await expect.poll(() => loads).toBe(initialLoads + 1)
|
||||||
|
await expect(edit).toBeVisible()
|
||||||
|
await expect(edit).toBeDisabled()
|
||||||
|
release!()
|
||||||
|
await expect(refresh).toBeEnabled()
|
||||||
|
await expect(edit).toBeVisible()
|
||||||
|
await expect(edit).toBeEnabled()
|
||||||
|
await edit.click()
|
||||||
|
const dialog = page.getByRole("dialog", { name: "Редактировать GRE-туннель" })
|
||||||
|
await expect(dialog).toBeVisible()
|
||||||
|
await expect(dialog.getByLabel("Удалённый IP", { exact: true })).toHaveValue("10.0.0.6/30")
|
||||||
|
})
|
||||||
Reference in new issue
Block a user