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
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:
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,8 @@ function CommandDialog({
|
||||
)}
|
||||
showCloseButton={showCloseButton}
|
||||
>
|
||||
{children}
|
||||
{/* cmdk-компоненты требуют корень Command — без него useSyncExternalStore падает */}
|
||||
<Command className="overflow-hidden">{children}</Command>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user