feat(web): enhance ResourcePage and AgentsPage with table layout and column pinning
Build and Push EvoFirewall Docker Image / build-and-push (push) Successful in 2m0s
Build and Push EvoFirewall Docker Image / create-release (push) Skipped

- 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:
Denozordec
2026-07-22 01:40:39 +07:00
parent 68d9246158
commit 817f0af191
2 changed files with 96 additions and 10 deletions
@@ -1,10 +1,11 @@
import { useCallback, useMemo, useState, type ReactNode } from 'react'
import { useCallback, useMemo, useState, type ComponentProps, type ReactNode } from 'react'
import {
getCoreRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
type ColumnDef,
type ColumnPinningState,
type PaginationState,
type RowSelectionState,
type SortingState,
@@ -46,6 +47,10 @@ import {
import { EmptyState } from '@/components/empty-state'
import { applyFiltersToData } from './filter-utils'
type DataGridTableLayout = NonNullable<
ComponentProps<typeof DataGrid>['tableLayout']
>
export interface ResourcePageTab {
id: string
label: string
@@ -88,6 +93,10 @@ export interface ResourcePageProps<T extends object> {
onSearchChange?: (query: string) => void
searchPlaceholder?: string
getSearchText?: (item: T) => string
/** Merge over defaults: dense + headerSticky + width fixed */
tableLayout?: Partial<DataGridTableLayout>
/** Pin columns (e.g. `{ right: ['actions'] }`) — enables column pinning */
columnPinning?: ColumnPinningState
}
function ResourcePageSkeleton() {
@@ -140,12 +149,17 @@ export function ResourcePage<T extends object>({
onSearchChange,
searchPlaceholder = 'Поиск…',
getSearchText,
tableLayout: tableLayoutProp,
columnPinning: columnPinningProp,
}: ResourcePageProps<T>) {
const [internalTab, setInternalTab] = useState(tabs?.[0]?.id ?? 'all')
const activeTab = controlledTab ?? internalTab
const [sorting, setSorting] = useState<SortingState>([])
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
const [columnPinning, setColumnPinning] = useState<ColumnPinningState>(
() => columnPinningProp ?? {},
)
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize,
@@ -213,15 +227,32 @@ export function ResourcePage<T extends object>({
setRowSelection({})
}, [])
const tableLayout = useMemo(
() => ({
dense: true,
headerSticky: true,
width: 'fixed' as const,
...tableLayoutProp,
...(columnPinningProp || Object.keys(columnPinning).length > 0
? { columnsPinnable: true }
: {}),
}),
[tableLayoutProp, columnPinningProp, columnPinning],
)
const table = useReactTable({
data: filteredData,
columns,
getRowId,
state: { sorting, rowSelection, pagination },
state: { sorting, rowSelection, pagination, columnPinning },
enableRowSelection,
enableColumnPinning: Boolean(
tableLayout.columnsPinnable || columnPinningProp,
),
onSortingChange: setSorting,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
onColumnPinningChange: setColumnPinning,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
@@ -338,7 +369,7 @@ export function ResourcePage<T extends object>({
table={table}
recordCount={filteredData.length}
emptyMessage={emptyMessage}
tableLayout={{ dense: true }}
tableLayout={tableLayout}
onRowClick={onRowClick}
>
<Frame dense variant="default" spacing="sm" className="w-full">