fix(web): колонка выделения в таблице агентов, корень Command в CommandDialog, RU-метки KPI и дашборда, пустой f не пишется в URL
quality / commitlint (push) Skipped
quality / changes (push) Successful in 11s
quality / docker-check (push) Skipped
quality / openapi (push) Failing after 36s
quality / web (push) Failing after 36s
quality / api (push) Failing after 36s
CD / quality (push) Failing after 2m7s
CD / publish (push) Skipped

This commit is contained in:
Denozordec
2026-09-25 02:06:29 +07:00
parent 19af67f8a3
commit 27c1ba7e23
8 changed files with 39 additions and 18 deletions
@@ -6,6 +6,10 @@ import type { Filter, FilterFieldConfig } from '@/components/reui/filters'
import { ResourcePage } from '@/components/reui-kit'
import { Badge } from '@/components/reui/badge'
import { DataGridColumnHeader } from '@/components/reui/data-grid/data-grid-column-header'
import {
DataGridTableRowSelect,
DataGridTableRowSelectAll,
} from '@/components/reui/data-grid/data-grid-table'
import {
DataGridMutedCell,
DataGridPrimaryCell,
@@ -115,6 +119,18 @@ export function AgentFleetDataGrid({
const columns: ColumnDef<Agent>[] = useMemo(
() => [
...(enableRowSelection
? [
{
id: '__select__',
enableSorting: false,
enableResizing: false,
size: 44,
header: () => <DataGridTableRowSelectAll />,
cell: ({ row }) => <DataGridTableRowSelect row={row} />,
} as ColumnDef<Agent>,
]
: []),
{
accessorKey: 'name',
size: 260,
@@ -348,7 +364,7 @@ export function AgentFleetDataGrid({
},
},
],
[approvePending, handleCopy, onApprove, onDelete, onSelect],
[approvePending, handleCopy, onApprove, onDelete, onSelect, enableRowSelection],
)
return (
@@ -52,24 +52,24 @@ export function fleetKpiCards(
return [
{
id: 'pending',
label: 'Pending',
label: 'Ожидают',
value: counts.pending,
hint: 'approve backlog',
hint: 'одобрение',
icon: icons.pending,
iconClassName: 'text-warning',
variant: counts.pending > 0 ? 'warning' : 'default',
},
{
id: 'invited',
label: 'Invited',
label: 'Приглашённые',
value: counts.invited,
hint: 'ожидают install',
hint: 'ждут установки',
icon: icons.invited,
iconClassName: 'text-info',
},
{
id: 'approved',
label: 'Approved',
label: 'Одобренные',
value: counts.approved,
hint: 'в парке',
icon: icons.approved,
@@ -77,9 +77,9 @@ export function fleetKpiCards(
},
{
id: 'stale',
label: 'Offline / stale',
label: 'Не на связи',
value: counts.stale,
hint: '>24ч без seen',
hint: 'нет seen >24ч',
icon: icons.stale,
iconClassName: 'text-muted-foreground',
variant: counts.stale > 0 ? 'warning' : 'default',
+2 -1
View File
@@ -98,13 +98,14 @@ function parseAgentsSearch(search: Record<string, unknown>): AgentsSearch {
? search.agent
: undefined
const add = searchToFlag(search.add)
const f = searchToFilters(search.f)
return {
view,
agent,
add,
tab: searchToTab(search.tab, AGENT_TAB_IDS),
q: searchToQ(search.q),
f: searchToFilters(search.f),
f: f.length > 0 ? f : undefined,
}
}
+5 -5
View File
@@ -76,7 +76,7 @@ function DashboardPage() {
},
{
id: 'dropped',
label: 'Dropped',
label: 'Заблокировано',
value: d?.packets_dropped ?? '—',
hint: 'сумма counters',
to: '/stats',
@@ -86,7 +86,7 @@ function DashboardPage() {
},
{
id: 'accepted',
label: 'Accepted',
label: 'Пропущено',
value: d?.packets_accepted ?? '—',
hint: 'сумма counters',
to: '/stats',
@@ -110,7 +110,7 @@ function DashboardPage() {
{
id: 'agents',
title: 'Агенты',
description: 'Enroll и approve',
description: 'Подключение и одобрение агентов',
to: '/agents',
icon: <ServerIcon aria-hidden />,
iconClassName: 'text-info',
@@ -118,7 +118,7 @@ function DashboardPage() {
{
id: 'lists',
title: 'Списки',
description: 'Blocklists / sources',
description: 'Блок-листы и источники',
to: '/lists',
icon: <ListIcon aria-hidden />,
iconClassName: 'text-primary',
@@ -126,7 +126,7 @@ function DashboardPage() {
{
id: 'rules',
title: 'Правила',
description: 'Allow / deny policy',
description: 'Политика блокировки',
to: '/rules',
icon: <BanIcon aria-hidden />,
iconClassName: 'text-warning',
+2 -1
View File
@@ -67,11 +67,12 @@ type ListsSearch = {
/** URL-состояние страницы: create/tab/q/f (deep-link и refresh сохраняют вид). */
function parseListsSearch(search: Record<string, unknown>): ListsSearch {
const f = searchToFilters(search.f)
return {
create: searchToFlag(search.create),
tab: searchToTab(search.tab, LIST_TAB_IDS),
q: searchToQ(search.q),
f: searchToFilters(search.f),
f: f.length > 0 ? f : undefined,
}
}
+2 -1
View File
@@ -37,10 +37,11 @@ type RulesSearch = {
/** URL-состояние страницы: create/q/f (deep-link и refresh сохраняют вид). */
function parseRulesSearch(search: Record<string, unknown>): RulesSearch {
const f = searchToFilters(search.f)
return {
create: searchToFlag(search.create),
q: searchToQ(search.q),
f: searchToFilters(search.f),
f: f.length > 0 ? f : undefined,
}
}
+2 -1
View File
@@ -48,8 +48,9 @@ type StatsSearch = {
/** URL-состояние страницы: f (deep-link и refresh сохраняют вид). */
function parseStatsSearch(search: Record<string, unknown>): StatsSearch {
const f = searchToFilters(search.f)
return {
f: searchToFilters(search.f),
f: f.length > 0 ? f : undefined,
}
}
+2 -1
View File
@@ -58,7 +58,8 @@ function CommandDialog({
)}
showCloseButton={showCloseButton}
>
{children}
{/* cmdk-компоненты требуют корень Command — без него useSyncExternalStore падает */}
<Command className="overflow-hidden">{children}</Command>
</DialogContent>
</Dialog>
)