feat(web): enhance ResourcePage and AgentsPage with table layout and column pinning
- Updated ResourcePage to support customizable table layout and column pinning, improving data presentation and user interaction. - Introduced a new `formatAgentSeen` function in AgentsPage for better date formatting of agent last seen timestamps. - Adjusted column sizes and added min/max size constraints for various columns in AgentsPage to enhance layout consistency. - Enhanced the tooltip functionality for displaying agent last seen timestamps, improving user experience.
This commit is contained in:
@@ -76,6 +76,20 @@ function formatPackets(n: number | undefined, hasApply: boolean): string {
|
||||
return packetFmt.format(n)
|
||||
}
|
||||
|
||||
const seenFmt = new Intl.DateTimeFormat('ru-RU', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
|
||||
function formatAgentSeen(iso: string | null | undefined): string {
|
||||
if (!iso) return '—'
|
||||
const t = Date.parse(iso)
|
||||
if (Number.isNaN(t)) return '—'
|
||||
return seenFmt.format(t)
|
||||
}
|
||||
|
||||
/**
|
||||
* Agents ops console — Solutions Agents DNA.
|
||||
* Preview: https://reui.io/preview/base/solution-agents-1 · stats-12 · data-grid-filtering-2
|
||||
@@ -265,6 +279,9 @@ function AgentsPage() {
|
||||
() => [
|
||||
{
|
||||
accessorKey: 'name',
|
||||
size: 220,
|
||||
minSize: 160,
|
||||
maxSize: 320,
|
||||
header: ({ column }) => (
|
||||
<DataGridColumnHeader column={column} title="Имя" />
|
||||
),
|
||||
@@ -284,6 +301,9 @@ function AgentsPage() {
|
||||
},
|
||||
{
|
||||
accessorKey: 'status',
|
||||
size: 110,
|
||||
minSize: 100,
|
||||
maxSize: 130,
|
||||
header: ({ column }) => (
|
||||
<DataGridColumnHeader column={column} title="Статус" />
|
||||
),
|
||||
@@ -291,6 +311,9 @@ function AgentsPage() {
|
||||
},
|
||||
{
|
||||
id: 'apply',
|
||||
size: 90,
|
||||
minSize: 80,
|
||||
maxSize: 110,
|
||||
accessorFn: (row) => row.last_apply_status ?? '',
|
||||
header: ({ column }) => (
|
||||
<DataGridColumnHeader column={column} title="Apply" />
|
||||
@@ -316,6 +339,9 @@ function AgentsPage() {
|
||||
},
|
||||
{
|
||||
id: 'dropped',
|
||||
size: 90,
|
||||
minSize: 80,
|
||||
maxSize: 110,
|
||||
accessorFn: (row) => row.last_apply_packets_dropped ?? -1,
|
||||
header: ({ column }) => (
|
||||
<DataGridColumnHeader column={column} title="Dropped" />
|
||||
@@ -346,6 +372,9 @@ function AgentsPage() {
|
||||
},
|
||||
{
|
||||
id: 'accepted',
|
||||
size: 90,
|
||||
minSize: 80,
|
||||
maxSize: 110,
|
||||
accessorFn: (row) => row.last_apply_packets_accepted ?? -1,
|
||||
header: ({ column }) => (
|
||||
<DataGridColumnHeader column={column} title="Accepted" />
|
||||
@@ -376,6 +405,9 @@ function AgentsPage() {
|
||||
},
|
||||
{
|
||||
id: 'install',
|
||||
size: 100,
|
||||
minSize: 90,
|
||||
maxSize: 120,
|
||||
enableSorting: false,
|
||||
header: ({ column }) => (
|
||||
<DataGridColumnHeader column={column} title="Install" />
|
||||
@@ -392,13 +424,13 @@ function AgentsPage() {
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="max-w-[14rem] font-mono text-xs"
|
||||
className="font-mono text-xs"
|
||||
onClick={(e) => handleCopyCurl(curl, e)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Copy data-icon="inline-start" className="size-3.5" />
|
||||
<span className="truncate">{curl}</span>
|
||||
curl
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="max-w-sm break-all font-mono text-xs">
|
||||
{curl}
|
||||
@@ -409,18 +441,39 @@ function AgentsPage() {
|
||||
},
|
||||
{
|
||||
accessorKey: 'last_seen_at',
|
||||
size: 130,
|
||||
minSize: 110,
|
||||
maxSize: 160,
|
||||
header: ({ column }) => (
|
||||
<DataGridColumnHeader column={column} title="Seen" />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<DataGridMutedCell>
|
||||
{row.original.last_seen_at ?? '—'}
|
||||
</DataGridMutedCell>
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
const iso = row.original.last_seen_at
|
||||
const short = formatAgentSeen(iso)
|
||||
if (!iso || short === '—') {
|
||||
return <DataGridMutedCell>—</DataGridMutedCell>
|
||||
}
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
render={
|
||||
<span className="text-muted-foreground cursor-default text-xs tabular-nums" />
|
||||
}
|
||||
>
|
||||
{short}
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="font-mono text-xs">{iso}</TooltipContent>
|
||||
</Tooltip>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
size: 108,
|
||||
minSize: 108,
|
||||
maxSize: 108,
|
||||
enableSorting: false,
|
||||
enablePinning: true,
|
||||
header: () => <span className="sr-only">Действия</span>,
|
||||
cell: ({ row }) => {
|
||||
const a = row.original
|
||||
@@ -553,6 +606,8 @@ function AgentsPage() {
|
||||
onSearchChange={setSearchQuery}
|
||||
searchPlaceholder="Поиск агентов…"
|
||||
getSearchText={getSearchText}
|
||||
tableLayout={{ columnsPinnable: true, width: 'fixed' }}
|
||||
columnPinning={{ right: ['actions'] }}
|
||||
onRowClick={(row) =>
|
||||
void navigate({ to: '/agents/$id', params: { id: row.id } })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user