fix(frontend): починить сортировку DataGrid и стиль колонки actions
Клик по заголовку снова сортирует напрямую; убран pin последней колонки, из-за которого actions выглядел отрезанным от строки. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -238,7 +238,12 @@ export function createErrorsColumns(opts: {
|
|||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
enableResizing: false,
|
enableResizing: false,
|
||||||
meta: { skeleton: <Skeleton className="size-7 rounded-md" /> },
|
enablePinning: false,
|
||||||
|
meta: {
|
||||||
|
skeleton: <Skeleton className="size-7 rounded-md" />,
|
||||||
|
cellClassName: 'bg-transparent!',
|
||||||
|
headerClassName: 'bg-transparent!',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,6 @@ export function ErrorsGridView() {
|
|||||||
}
|
}
|
||||||
getFilterFieldValue={getFilterFieldValue}
|
getFilterFieldValue={getFilterFieldValue}
|
||||||
onRowClick={handleOpen}
|
onRowClick={handleOpen}
|
||||||
pinLastColumn
|
|
||||||
pageSize={10}
|
pageSize={10}
|
||||||
initialSorting={[{ id: 'total', desc: true }]}
|
initialSorting={[{ id: 'total', desc: true }]}
|
||||||
enableColumnVisibility
|
enableColumnVisibility
|
||||||
@@ -177,8 +176,10 @@ export function ErrorsGridView() {
|
|||||||
columnsResizable: true,
|
columnsResizable: true,
|
||||||
columnsMovable: true,
|
columnsMovable: true,
|
||||||
columnsVisibility: true,
|
columnsVisibility: true,
|
||||||
|
columnsPinnable: false,
|
||||||
rowsPinnable: false,
|
rowsPinnable: false,
|
||||||
}}
|
}}
|
||||||
|
pinLastColumn={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ResourcePage
|
<ResourcePage
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
useReactTable,
|
useReactTable,
|
||||||
type ColumnDef,
|
type ColumnDef,
|
||||||
type ColumnOrderState,
|
type ColumnOrderState,
|
||||||
|
type ColumnPinningState,
|
||||||
type OnChangeFn,
|
type OnChangeFn,
|
||||||
type PaginationState,
|
type PaginationState,
|
||||||
type RowPinningState,
|
type RowPinningState,
|
||||||
@@ -172,7 +173,7 @@ const DEFAULT_FILTERED_LAYOUT: TableLayoutConfig = {
|
|||||||
dense: true,
|
dense: true,
|
||||||
columnsVisibility: true,
|
columnsVisibility: true,
|
||||||
columnsResizable: true,
|
columnsResizable: true,
|
||||||
columnsPinnable: true,
|
columnsPinnable: false,
|
||||||
columnsMovable: true,
|
columnsMovable: true,
|
||||||
rowsPinnable: false,
|
rowsPinnable: false,
|
||||||
}
|
}
|
||||||
@@ -404,6 +405,20 @@ function ResourcePageFiltered<T extends object>({
|
|||||||
? (columns[columns.length - 1]?.id ?? '')
|
? (columns[columns.length - 1]?.id ?? '')
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
|
const [columnPinning, setColumnPinning] = useState<ColumnPinningState>(() =>
|
||||||
|
pinLastColumn && lastColId
|
||||||
|
? { left: [], right: [lastColId] }
|
||||||
|
: { left: [], right: [] },
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleSortingChange = useCallback<OnChangeFn<SortingState>>(
|
||||||
|
(updater) => {
|
||||||
|
setSorting(updater)
|
||||||
|
resetPagination()
|
||||||
|
},
|
||||||
|
[resetPagination],
|
||||||
|
)
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: filteredData,
|
data: filteredData,
|
||||||
columns,
|
columns,
|
||||||
@@ -414,6 +429,7 @@ function ResourcePageFiltered<T extends object>({
|
|||||||
pagination,
|
pagination,
|
||||||
columnVisibility,
|
columnVisibility,
|
||||||
columnOrder,
|
columnOrder,
|
||||||
|
columnPinning,
|
||||||
...(enableRowPinning ? { rowPinning: resolvedRowPinning } : {}),
|
...(enableRowPinning ? { rowPinning: resolvedRowPinning } : {}),
|
||||||
},
|
},
|
||||||
enableRowSelection,
|
enableRowSelection,
|
||||||
@@ -421,20 +437,18 @@ function ResourcePageFiltered<T extends object>({
|
|||||||
keepPinnedRows: enableRowPinning,
|
keepPinnedRows: enableRowPinning,
|
||||||
enableColumnPinning: Boolean(pinLastColumn || layout.columnsPinnable),
|
enableColumnPinning: Boolean(pinLastColumn || layout.columnsPinnable),
|
||||||
enableHiding: enableColumnVisibility,
|
enableHiding: enableColumnVisibility,
|
||||||
onSortingChange: setSorting,
|
enableSorting: true,
|
||||||
|
manualSorting: false,
|
||||||
|
onSortingChange: handleSortingChange,
|
||||||
onRowSelectionChange: setRowSelection,
|
onRowSelectionChange: setRowSelection,
|
||||||
onPaginationChange: setPagination,
|
onPaginationChange: setPagination,
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
onColumnOrderChange: setColumnOrder,
|
onColumnOrderChange: setColumnOrder,
|
||||||
|
onColumnPinningChange: setColumnPinning,
|
||||||
onRowPinningChange: enableRowPinning ? setRowPinning : undefined,
|
onRowPinningChange: enableRowPinning ? setRowPinning : undefined,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
getSortedRowModel: getSortedRowModel(),
|
getSortedRowModel: getSortedRowModel(),
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
initialState: {
|
|
||||||
...(pinLastColumn && lastColId
|
|
||||||
? { columnPinning: { right: [lastColId] } }
|
|
||||||
: {}),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleTabChange = useCallback(
|
const handleTabChange = useCallback(
|
||||||
|
|||||||
@@ -289,18 +289,36 @@ function DataGridColumnHeaderInner<TData, TValue>({
|
|||||||
|
|
||||||
if (hasControls) {
|
if (hasControls) {
|
||||||
return (
|
return (
|
||||||
<div className="-ms-2 flex h-full items-center justify-between gap-1.5">
|
<div className="-ms-2 flex h-full items-center gap-0.5">
|
||||||
|
{canSort ? (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className={headerButtonClassName}
|
||||||
|
disabled={isLoading}
|
||||||
|
onClick={handleSort}
|
||||||
|
aria-label={`Сортировать: ${resolvedTitle}`}
|
||||||
|
>
|
||||||
|
{icon && icon}
|
||||||
|
{resolvedTitle}
|
||||||
|
{sortIcon}
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<span className={cn(headerLabelClassName, "px-2")}>
|
||||||
|
{icon && icon}
|
||||||
|
{resolvedTitle}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger
|
<DropdownMenuTrigger
|
||||||
render={
|
render={
|
||||||
<Button
|
<Button
|
||||||
|
size="icon-sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className={headerButtonClassName}
|
className="rounded-lg size-7"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
|
aria-label={`Параметры колонки: ${resolvedTitle}`}
|
||||||
>
|
>
|
||||||
{icon && icon}
|
<Settings2Icon className="size-3.5 opacity-60" aria-hidden="true" />
|
||||||
{resolvedTitle}
|
|
||||||
{sortIcon}
|
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -308,7 +326,7 @@ function DataGridColumnHeaderInner<TData, TValue>({
|
|||||||
{menuItems}
|
{menuItems}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
{props.tableLayout?.columnsPinnable && canPin && isPinned && (
|
{props.tableLayout?.columnsPinnable && canPin && isPinned ? (
|
||||||
<Button
|
<Button
|
||||||
size="icon-sm"
|
size="icon-sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -319,7 +337,7 @@ function DataGridColumnHeaderInner<TData, TValue>({
|
|||||||
>
|
>
|
||||||
<PinOffIcon className="size-3.5! opacity-50!" aria-hidden="true" />
|
<PinOffIcon className="size-3.5! opacity-50!" aria-hidden="true" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ export function createUsersColumns(opts: {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'current_connections',
|
id: 'current_connections',
|
||||||
accessorKey: 'current_connections',
|
accessorFn: (row) => Number(row.current_connections ?? 0),
|
||||||
header: ({ column }) => (
|
header: ({ column }) => (
|
||||||
<DataGridColumnHeader
|
<DataGridColumnHeader
|
||||||
title="Соединения"
|
title="Соединения"
|
||||||
@@ -362,6 +362,7 @@ export function createUsersColumns(opts: {
|
|||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
enableHiding: true,
|
enableHiding: true,
|
||||||
enableResizing: true,
|
enableResizing: true,
|
||||||
|
sortingFn: 'basic',
|
||||||
meta: {
|
meta: {
|
||||||
skeleton: (
|
skeleton: (
|
||||||
<div className="flex min-w-0 items-center gap-2">
|
<div className="flex min-w-0 items-center gap-2">
|
||||||
@@ -442,7 +443,12 @@ export function createUsersColumns(opts: {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'data_quota_bytes',
|
id: 'data_quota_bytes',
|
||||||
accessorKey: 'data_quota_bytes',
|
accessorFn: (row) => {
|
||||||
|
const quota = row.data_quota_bytes
|
||||||
|
if (typeof quota === 'number' && quota > 0) return quota
|
||||||
|
// «Без квоты» в UI показывает usage — сортируем по тому же смыслу
|
||||||
|
return Number(row.total_octets ?? 0)
|
||||||
|
},
|
||||||
header: ({ column }) => (
|
header: ({ column }) => (
|
||||||
<DataGridColumnHeader title="Квота" visibility={true} column={column} />
|
<DataGridColumnHeader title="Квота" visibility={true} column={column} />
|
||||||
),
|
),
|
||||||
@@ -451,6 +457,7 @@ export function createUsersColumns(opts: {
|
|||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
enableHiding: true,
|
enableHiding: true,
|
||||||
enableResizing: true,
|
enableResizing: true,
|
||||||
|
sortingFn: 'basic',
|
||||||
meta: {
|
meta: {
|
||||||
skeleton: (
|
skeleton: (
|
||||||
<div className="flex min-w-0 flex-col gap-1">
|
<div className="flex min-w-0 flex-col gap-1">
|
||||||
@@ -534,9 +541,12 @@ export function createUsersColumns(opts: {
|
|||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
enableResizing: false,
|
enableResizing: false,
|
||||||
|
enablePinning: false,
|
||||||
size: 52,
|
size: 52,
|
||||||
meta: {
|
meta: {
|
||||||
skeleton: <Skeleton className="size-7 rounded-md" />,
|
skeleton: <Skeleton className="size-7 rounded-md" />,
|
||||||
|
cellClassName: 'bg-transparent!',
|
||||||
|
headerClassName: 'bg-transparent!',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -276,9 +276,17 @@ export function UsersGridView() {
|
|||||||
enableColumnVisibility
|
enableColumnVisibility
|
||||||
columnVisibility={columnVisibility}
|
columnVisibility={columnVisibility}
|
||||||
onColumnVisibilityChange={setColumnVisibility}
|
onColumnVisibilityChange={setColumnVisibility}
|
||||||
pinLastColumn
|
|
||||||
pageSize={5}
|
pageSize={5}
|
||||||
initialSorting={[{ id: 'username', desc: false }]}
|
initialSorting={[{ id: 'username', desc: false }]}
|
||||||
|
tableLayout={{
|
||||||
|
dense: true,
|
||||||
|
columnsResizable: true,
|
||||||
|
columnsMovable: true,
|
||||||
|
columnsVisibility: true,
|
||||||
|
columnsPinnable: false,
|
||||||
|
rowsPinnable: true,
|
||||||
|
}}
|
||||||
|
pinLastColumn={false}
|
||||||
emptyState={{
|
emptyState={{
|
||||||
title: 'Нет пользователей',
|
title: 'Нет пользователей',
|
||||||
description: 'Создайте первый аккаунт Telemt.',
|
description: 'Создайте первый аккаунт Telemt.',
|
||||||
|
|||||||
Reference in New Issue
Block a user