diff --git a/src/components/PaginatedTable/PaginatedTable.tsx b/src/components/PaginatedTable/PaginatedTable.tsx index f874d4a55..cb2fec99c 100644 --- a/src/components/PaginatedTable/PaginatedTable.tsx +++ b/src/components/PaginatedTable/PaginatedTable.tsx @@ -23,14 +23,15 @@ import {useScrollBasedChunks} from './useScrollBasedChunks'; import './PaginatedTable.scss'; -export interface PaginatedTableProps { +export interface PaginatedTableProps { limit: number; initialEntitiesCount?: number; - fetchData: FetchData; - filters?: F; + fetchData: FetchData; + filters?: Filters; + dataFieldsRequired?: DataFieldType[]; tableName: string; - columns: Column[]; - getRowClassName?: GetRowClassName; + columns: Column[]; + getRowClassName?: GetRowClassName; rowHeight?: number; parentRef?: React.RefObject; initialSortParams?: SortParams; @@ -41,11 +42,12 @@ export interface PaginatedTableProps { containerClassName?: string; } -export const PaginatedTable = ({ +export const PaginatedTable = ({ limit, initialEntitiesCount, fetchData, filters, + dataFieldsRequired, tableName, columns, getRowClassName, @@ -57,7 +59,7 @@ export const PaginatedTable = ({ renderErrorMessage, renderEmptyDataMessage, containerClassName, -}: PaginatedTableProps) => { +}: PaginatedTableProps) => { const initialTotal = initialEntitiesCount || limit; const initialFound = initialEntitiesCount || 0; @@ -108,7 +110,7 @@ export const PaginatedTable = ({ const chunksCount = Math.ceil(totalLength / limit); return getArray(chunksCount).map((value) => ( - + key={value} id={value} limit={limit} @@ -117,6 +119,7 @@ export const PaginatedTable = ({ columns={columns} fetchData={fetchData} filters={filters} + dataFieldsRequired={dataFieldsRequired} tableName={tableName} sortParams={sortParams} getRowClassName={getRowClassName} diff --git a/src/components/PaginatedTable/ResizeablePaginatedTable.tsx b/src/components/PaginatedTable/ResizeablePaginatedTable.tsx index b0e082ea0..4b6314fe6 100644 --- a/src/components/PaginatedTable/ResizeablePaginatedTable.tsx +++ b/src/components/PaginatedTable/ResizeablePaginatedTable.tsx @@ -7,22 +7,25 @@ import {PaginatedTable} from './PaginatedTable'; import {b} from './shared'; import type {Column} from './types'; -function updateColumnsWidth(columns: Column[], columnsWidthSetup: ColumnWidthByName) { +function updateColumnsWidth( + columns: Column[], + columnsWidthSetup: ColumnWidthByName, +) { return columns.map((column) => { return {...column, width: columnsWidthSetup[column.name] ?? column.width}; }); } -interface ResizeablePaginatedTableProps - extends Omit, 'onColumnsResize'> { +interface ResizeablePaginatedTableProps + extends Omit, 'onColumnsResize'> { columnsWidthLSKey: string; } -export function ResizeablePaginatedTable({ +export function ResizeablePaginatedTable({ columnsWidthLSKey, columns, ...props -}: ResizeablePaginatedTableProps) { +}: ResizeablePaginatedTableProps) { const [tableColumnsWidth, setTableColumnsWidth] = useTableResize(columnsWidthLSKey); const updatedColumns = updateColumnsWidth(columns, tableColumnsWidth); diff --git a/src/components/PaginatedTable/TableChunk.tsx b/src/components/PaginatedTable/TableChunk.tsx index 7fd2124b4..e7c4f23ad 100644 --- a/src/components/PaginatedTable/TableChunk.tsx +++ b/src/components/PaginatedTable/TableChunk.tsx @@ -11,25 +11,26 @@ import type {Column, FetchData, GetRowClassName, SortParams} from './types'; const DEBOUNCE_TIMEOUT = 200; -interface TableChunkProps { +interface TableChunkProps { id: number; limit: number; totalLength: number; rowHeight: number; - columns: Column[]; - filters?: F; + columns: Column[]; + filters?: Filters; + dataFieldsRequired?: DataFieldType[]; sortParams?: SortParams; isActive: boolean; tableName: string; - fetchData: FetchData; - getRowClassName?: GetRowClassName; + fetchData: FetchData; + getRowClassName?: GetRowClassName; renderErrorMessage?: (error: IResponseError) => React.ReactNode; onDataFetched: (total: number, found: number) => void; } // Memoisation prevents chunks rerenders that could cause perfomance issues on big tables -export const TableChunk = ({ +export const TableChunk = ({ id, limit, totalLength, @@ -38,20 +39,22 @@ export const TableChunk = ({ fetchData, tableName, filters, + dataFieldsRequired, sortParams, getRowClassName, renderErrorMessage, onDataFetched, isActive, -}: TableChunkProps) => { +}: TableChunkProps) => { const [isTimeoutActive, setIsTimeoutActive] = React.useState(true); const [autoRefreshInterval] = useAutoRefreshInterval(); const queryParams = { offset: id * limit, limit, - fetchData: fetchData as FetchData, + fetchData: fetchData as FetchData, filters, + dataFieldsRequired, sortParams, tableName, }; @@ -123,7 +126,7 @@ export const TableChunk = ({ { +export interface Chunk { active: boolean; loading: boolean; wasLoaded: boolean; - data?: T[]; + data?: EntityType[]; error?: IResponseError; } @@ -23,36 +23,42 @@ export type OnSort = (params: SortParams) => void; export type HandleTableColumnsResize = (columnId: string, width: number) => void; -export interface Column { +export interface Column { name: string; header?: React.ReactNode; className?: string; sortable?: boolean; resizeable?: boolean; - render: (props: {row: T; index: number}) => React.ReactNode; + render: (props: {row: EntityType; index: number}) => React.ReactNode; width?: number; resizeMaxWidth?: number; resizeMinWidth?: number; align: AlignType; } -export interface PaginatedTableData { - data: T[]; +export interface PaginatedTableData { + data: EntityType[]; total: number; found: number; } -type FetchDataParams = { +type FetchDataParams = { limit: number; offset: number; - filters?: F; + filters?: Filters; + dataFieldsRequired?: DataFieldType[]; sortParams?: SortParams; signal?: AbortSignal; -} & E; +} & AdditionalParams; -export type FetchData = ( - params: FetchDataParams, -) => Promise>; +export type FetchData< + EntityType, + Filters = undefined, + DataFieldType = undefined, + AdditionalParams = {}, +> = ( + params: FetchDataParams, +) => Promise>; export type OnError = (error?: IResponseError) => void; @@ -66,4 +72,4 @@ export type RenderControls = (params: ControlsParams) => React.ReactNode; export type RenderEmptyDataMessage = () => React.ReactNode; export type RenderErrorMessage = (error: IResponseError) => React.ReactNode; -export type GetRowClassName = (row: T) => string | undefined; +export type GetRowClassName = (row: EntityType) => string | undefined; diff --git a/src/components/nodesColumns/columns.tsx b/src/components/nodesColumns/columns.tsx index 1b1175a58..68e0fac85 100644 --- a/src/components/nodesColumns/columns.tsx +++ b/src/components/nodesColumns/columns.tsx @@ -7,6 +7,7 @@ import {valueIsDefined} from '../../utils'; import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants'; import {formatStorageValuesToGb} from '../../utils/dataFormatters/dataFormatters'; import {getSpaceUsageSeverity} from '../../utils/storage'; +import type {Column} from '../../utils/tableUtils/types'; import {CellWithPopover} from '../CellWithPopover/CellWithPopover'; import {NodeHostWrapper} from '../NodeHostWrapper/NodeHostWrapper'; import type {NodeHostData} from '../NodeHostWrapper/NodeHostWrapper'; @@ -16,7 +17,7 @@ import {TabletsStatistic} from '../TabletsStatistic'; import {UsageLabel} from '../UsageLabel/UsageLabel'; import {NODES_COLUMNS_IDS, NODES_COLUMNS_TITLES} from './constants'; -import type {Column, GetNodesColumnsParams} from './types'; +import type {GetNodesColumnsParams} from './types'; export function getNodeIdColumn(): Column { return { diff --git a/src/components/nodesColumns/constants.ts b/src/components/nodesColumns/constants.ts index e66c24212..97813b90f 100644 --- a/src/components/nodesColumns/constants.ts +++ b/src/components/nodesColumns/constants.ts @@ -1,3 +1,4 @@ +import type {NodesRequiredField} from '../../types/api/nodes'; import type {ValueOf} from '../../types/common'; import i18n from './i18n'; @@ -21,6 +22,7 @@ export const NODES_COLUMNS_IDS = { TotalSessions: 'TotalSessions', Missing: 'Missing', Tablets: 'Tablets', + PDisks: 'PDisks', } as const; export type NodesColumnId = ValueOf; @@ -76,4 +78,29 @@ export const NODES_COLUMNS_TITLES = { get Tablets() { return i18n('tablets'); }, + get PDisks() { + return i18n('pdisks'); + }, } as const satisfies Record; + +// Although columns ids mostly similar to backend fields, there might be some difference +// Also for some columns we may use more than one field +export const NODES_COLUMNS_TO_DATA_FIELDS: Record = { + NodeId: ['NodeId'], + Host: ['Host', 'Rack', 'Database', 'SystemState'], + NodeName: ['NodeName'], + DC: ['DC'], + Rack: ['Rack'], + Version: ['Version'], + Uptime: ['Uptime'], + Memory: ['Memory'], + CPU: ['CPU'], + LoadAverage: ['LoadAverage'], + Load: ['LoadAverage'], + DiskSpaceUsage: ['DiskSpaceUsage'], + SharedCacheUsage: ['SystemState'], + TotalSessions: ['SystemState'], + Missing: ['Missing'], + Tablets: ['Tablets', 'Database'], + PDisks: ['PDisks'], +}; diff --git a/src/components/nodesColumns/i18n/en.json b/src/components/nodesColumns/i18n/en.json index 5b6f99f95..0638d8ea5 100644 --- a/src/components/nodesColumns/i18n/en.json +++ b/src/components/nodesColumns/i18n/en.json @@ -14,5 +14,6 @@ "load": "Load", "caches": "Caches", "sessions": "Sessions", - "missing": "Missing" + "missing": "Missing", + "pdisks": "PDisks" } diff --git a/src/components/nodesColumns/types.ts b/src/components/nodesColumns/types.ts index 8bf42cd9e..f09990a20 100644 --- a/src/components/nodesColumns/types.ts +++ b/src/components/nodesColumns/types.ts @@ -1,9 +1,4 @@ -import type {Column as DataTableColumn} from '@gravity-ui/react-data-table'; - import type {GetNodeRefFunc} from '../../types/additionalProps'; -import type {Column as PaginatedTableColumn} from '../PaginatedTable'; - -export type Column = PaginatedTableColumn & DataTableColumn; export interface GetNodesColumnsParams { getNodeRef?: GetNodeRefFunc; diff --git a/src/containers/Nodes/PaginatedNodes.tsx b/src/containers/Nodes/PaginatedNodes.tsx index 105552126..01d3a5de2 100644 --- a/src/containers/Nodes/PaginatedNodes.tsx +++ b/src/containers/Nodes/PaginatedNodes.tsx @@ -64,10 +64,11 @@ export const PaginatedNodes = ({path, database, parentRef, additionalNodesProps} return {path, database, searchValue, problemFilter, uptimeFilter}; }, [path, database, searchValue, problemFilter, uptimeFilter]); - const {columnsToShow, columnsToSelect, setColumns} = useNodesSelectedColumns({ - getNodeRef: additionalNodesProps?.getNodeRef, - database, - }); + const {columnsToShow, columnsToSelect, setColumns, dataFieldsRequired} = + useNodesSelectedColumns({ + getNodeRef: additionalNodesProps?.getNodeRef, + database, + }); const getRowClassName: GetRowClassName = (row) => { return b('node', {unavailable: isUnavailableNode(row)}); @@ -144,6 +145,7 @@ export const PaginatedNodes = ({path, database, parentRef, additionalNodesProps} renderEmptyDataMessage={renderEmptyDataMessage} getRowClassName={getRowClassName} filters={tableFilters} + dataFieldsRequired={dataFieldsRequired} tableName="nodes" /> ); diff --git a/src/containers/Nodes/columns/columns.tsx b/src/containers/Nodes/columns/columns.tsx index d81ef4057..8041fa647 100644 --- a/src/containers/Nodes/columns/columns.tsx +++ b/src/containers/Nodes/columns/columns.tsx @@ -11,9 +11,10 @@ import { getUptimeColumn, getVersionColumn, } from '../../../components/nodesColumns/columns'; -import type {Column, GetNodesColumnsParams} from '../../../components/nodesColumns/types'; +import type {GetNodesColumnsParams} from '../../../components/nodesColumns/types'; import type {NodesPreparedEntity} from '../../../store/reducers/nodes/types'; import {isSortableNodesProperty} from '../../../utils/nodes'; +import type {Column} from '../../../utils/tableUtils/types'; export function getNodesColumns(params: GetNodesColumnsParams): Column[] { const columns = [ diff --git a/src/containers/Nodes/columns/hooks.ts b/src/containers/Nodes/columns/hooks.ts index fde6a8167..9d23e5d8a 100644 --- a/src/containers/Nodes/columns/hooks.ts +++ b/src/containers/Nodes/columns/hooks.ts @@ -1,8 +1,12 @@ import React from 'react'; -import {NODES_COLUMNS_TITLES} from '../../../components/nodesColumns/constants'; +import { + NODES_COLUMNS_TITLES, + NODES_COLUMNS_TO_DATA_FIELDS, +} from '../../../components/nodesColumns/constants'; import type {GetNodesColumnsParams} from '../../../components/nodesColumns/types'; import {useSelectedColumns} from '../../../utils/hooks/useSelectedColumns'; +import {getRequiredDataFields} from '../../../utils/tableUtils/getRequiredDataFields'; import {getNodesColumns} from './columns'; import { @@ -16,11 +20,22 @@ export function useNodesSelectedColumns(params: GetNodesColumnsParams) { return getNodesColumns(params); }, [params]); - return useSelectedColumns( + const {columnsToSelect, columnsToShow, setColumns} = useSelectedColumns( columns, NODES_TABLE_SELECTED_COLUMNS_LS_KEY, NODES_COLUMNS_TITLES, DEFAULT_NODES_COLUMNS, REQUIRED_NODES_COLUMNS, ); + + const dataFieldsRequired = React.useMemo(() => { + return getRequiredDataFields(columnsToShow, NODES_COLUMNS_TO_DATA_FIELDS); + }, [columnsToShow]); + + return { + columnsToSelect, + columnsToShow, + setColumns, + dataFieldsRequired, + }; } diff --git a/src/containers/Nodes/getNodes.ts b/src/containers/Nodes/getNodes.ts index 340fd3a2a..deeddb9f1 100644 --- a/src/containers/Nodes/getNodes.ts +++ b/src/containers/Nodes/getNodes.ts @@ -1,7 +1,7 @@ import type {FetchData} from '../../components/PaginatedTable'; import type {NodesFilters, NodesPreparedEntity} from '../../store/reducers/nodes/types'; import {prepareNodesData} from '../../store/reducers/nodes/utils'; -import type {NodesRequestParams} from '../../types/api/nodes'; +import type {NodesRequestParams, NodesRequiredField} from '../../types/api/nodes'; import {prepareSortValue} from '../../utils/filters'; import { getProblemParamValue, @@ -16,6 +16,7 @@ const getConcurrentId = (limit?: number, offset?: number) => { export const getNodes: FetchData< NodesPreparedEntity, NodesFilters, + NodesRequiredField, Pick > = async (params) => { const { @@ -26,6 +27,7 @@ export const getNodes: FetchData< offset, sortParams, filters, + dataFieldsRequired, } = params; const {sortOrder, columnId} = sortParams ?? {}; @@ -48,6 +50,7 @@ export const getNodes: FetchData< filter: searchValue, problems_only: getProblemParamValue(problemFilter), uptime: getUptimeParamValue(uptimeFilter), + fieldsRequired: dataFieldsRequired, }, {concurrentId: getConcurrentId(limit, offset), signal: params.signal}, ); diff --git a/src/containers/Storage/PaginatedStorageGroups.tsx b/src/containers/Storage/PaginatedStorageGroups.tsx index 03735f3e1..6d8604fdd 100644 --- a/src/containers/Storage/PaginatedStorageGroups.tsx +++ b/src/containers/Storage/PaginatedStorageGroups.tsx @@ -62,10 +62,11 @@ function StorageGroupsComponent({ const storageGroupsHandlerHasGroupping = useStorageGroupsHandlerHasGrouping(); - const {columnsToShow, columnsToSelect, setColumns} = useStorageGroupsSelectedColumns({ - visibleEntities, - viewContext, - }); + const {columnsToShow, columnsToSelect, setColumns, dataFieldsRequired} = + useStorageGroupsSelectedColumns({ + visibleEntities, + viewContext, + }); const renderControls: RenderControls = ({totalEntities, foundEntities, inited}) => { return ( @@ -87,6 +88,7 @@ function StorageGroupsComponent({ nodeId={nodeId} groupId={groupId} pDiskId={pDiskId} + dataFieldsRequired={dataFieldsRequired} searchValue={searchValue} visibleEntities={visibleEntities} onShowAll={handleShowAllGroups} @@ -111,10 +113,11 @@ function GroupedStorageGroupsComponent({ const {searchValue, storageGroupsGroupByParam, visibleEntities, handleShowAllGroups} = useStorageQueryParams(); - const {columnsToShow, columnsToSelect, setColumns} = useStorageGroupsSelectedColumns({ - visibleEntities, - viewContext, - }); + const {columnsToShow, columnsToSelect, setColumns, dataFieldsRequired} = + useStorageGroupsSelectedColumns({ + visibleEntities, + viewContext, + }); const {currentData, isFetching, error} = storageApi.useGetStorageGroupsInfoQuery( { @@ -170,6 +173,7 @@ function GroupedStorageGroupsComponent({ nodeId={nodeId} groupId={groupId} pDiskId={pDiskId} + dataFieldsRequired={dataFieldsRequired} filterGroup={name} filterGroupBy={storageGroupsGroupByParam} searchValue={searchValue} diff --git a/src/containers/Storage/PaginatedStorageNodes.tsx b/src/containers/Storage/PaginatedStorageNodes.tsx index df496b73a..4a11ead3a 100644 --- a/src/containers/Storage/PaginatedStorageNodes.tsx +++ b/src/containers/Storage/PaginatedStorageNodes.tsx @@ -70,10 +70,11 @@ function StorageNodesComponent({ const viewerNodesHandlerHasGrouping = useViewerNodesHandlerHasGrouping(); - const {columnsToShow, columnsToSelect, setColumns} = useStorageNodesColumnsToSelect({ - database, - viewContext, - }); + const {columnsToShow, columnsToSelect, setColumns, dataFieldsRequired} = + useStorageNodesColumnsToSelect({ + database, + viewContext, + }); const renderControls: RenderControls = ({totalEntities, foundEntities, inited}) => { return ( @@ -97,6 +98,7 @@ function StorageNodesComponent({ searchValue={searchValue} visibleEntities={visibleEntities} nodesUptimeFilter={nodesUptimeFilter} + dataFieldsRequired={dataFieldsRequired} onShowAll={handleShowAllNodes} parentRef={parentRef} renderControls={renderControls} @@ -117,10 +119,11 @@ function GroupedStorageNodesComponent({ const {searchValue, storageNodesGroupByParam, handleShowAllNodes} = useStorageQueryParams(); - const {columnsToShow, columnsToSelect, setColumns} = useStorageNodesColumnsToSelect({ - database, - viewContext, - }); + const {columnsToShow, columnsToSelect, setColumns, dataFieldsRequired} = + useStorageNodesColumnsToSelect({ + database, + viewContext, + }); const {currentData, isFetching, error} = storageApi.useGetStorageNodesInfoQuery( { @@ -176,6 +179,7 @@ function GroupedStorageNodesComponent({ searchValue={searchValue} visibleEntities={'all'} nodesUptimeFilter={NodesUptimeFilterValues.All} + dataFieldsRequired={dataFieldsRequired} onShowAll={handleShowAllNodes} filterGroup={name} filterGroupBy={storageNodesGroupByParam} diff --git a/src/containers/Storage/StorageGroups/PaginatedStorageGroupsTable.tsx b/src/containers/Storage/StorageGroups/PaginatedStorageGroupsTable.tsx index 063052557..eb6854818 100644 --- a/src/containers/Storage/StorageGroups/PaginatedStorageGroupsTable.tsx +++ b/src/containers/Storage/StorageGroups/PaginatedStorageGroupsTable.tsx @@ -9,7 +9,7 @@ import { } from '../../../store/reducers/capabilities/hooks'; import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants'; import type {VisibleEntities} from '../../../store/reducers/storage/types'; -import type {GroupsGroupByField} from '../../../types/api/storage'; +import type {GroupsGroupByField, GroupsRequiredField} from '../../../types/api/storage'; import {StorageGroupsEmptyDataMessage} from './StorageGroupsEmptyDataMessage'; import {STORAGE_GROUPS_COLUMNS_WIDTH_LS_KEY} from './columns/constants'; @@ -28,6 +28,8 @@ interface PaginatedStorageGroupsTableProps { filterGroup?: string; filterGroupBy?: GroupsGroupByField; + dataFieldsRequired?: GroupsRequiredField[]; + searchValue: string; visibleEntities: VisibleEntities; onShowAll: VoidFunction; @@ -44,6 +46,7 @@ export const PaginatedStorageGroupsTable = ({ nodeId, groupId, pDiskId, + dataFieldsRequired, filterGroup, filterGroupBy, searchValue, @@ -107,6 +110,7 @@ export const PaginatedStorageGroupsTable = ({ renderErrorMessage={renderErrorMessage} renderEmptyDataMessage={renderEmptyDataMessage} filters={tableFilters} + dataFieldsRequired={dataFieldsRequired} tableName="storage-groups" /> diff --git a/src/containers/Storage/StorageGroups/columns/constants.ts b/src/containers/Storage/StorageGroups/columns/constants.ts index d084adada..b6862fbad 100644 --- a/src/containers/Storage/StorageGroups/columns/constants.ts +++ b/src/containers/Storage/StorageGroups/columns/constants.ts @@ -1,7 +1,7 @@ import type {SelectOption} from '@gravity-ui/uikit'; import {z} from 'zod'; -import type {GroupsGroupByField} from '../../../../types/api/storage'; +import type {GroupsGroupByField, GroupsRequiredField} from '../../../../types/api/storage'; import type {ValueOf} from '../../../../types/common'; import i18n from './i18n'; @@ -31,7 +31,7 @@ export const STORAGE_GROUPS_COLUMNS_IDS = { State: 'State', } as const; -type StorageGroupsColumnId = ValueOf; +export type StorageGroupsColumnId = ValueOf; export const DEFAULT_STORAGE_GROUPS_COLUMNS: StorageGroupsColumnId[] = [ 'GroupId', @@ -136,3 +136,29 @@ export const storageGroupsGroupByParamSchema = z GroupsGroupByField | undefined >((value) => STORAGE_GROUPS_GROUP_BY_PARAMS.includes(value)) .catch(undefined); + +// Although columns ids mostly similar to backend fields, there might be some difference +// Also for some columns we may use more than one field +export const GROUPS_COLUMNS_TO_DATA_FIELDS: Record = { + GroupId: ['GroupId'], + PoolName: ['PoolName'], + // We display MediaType and Encryption in one Type column + MediaType: ['MediaType', 'Encryption'], + Encryption: ['Encryption'], + Erasure: ['Erasure'], + Used: ['Used'], + Limit: ['Limit'], + Usage: ['Usage'], + DiskSpaceUsage: ['DiskSpaceUsage'], + DiskSpace: ['State'], + Read: ['Read'], + Write: ['Write'], + Latency: ['Latency'], + AllocationUnits: ['AllocationUnits'], + // Read and Write fields make backend to return Whiteboard data + VDisks: ['VDisk', 'PDisk', 'Read', 'Write'], + VDisksPDisks: ['VDisk', 'PDisk', 'Read', 'Write'], + MissingDisks: ['MissingDisks'], + Degraded: ['MissingDisks'], + State: ['State'], +}; diff --git a/src/containers/Storage/StorageGroups/columns/hooks.ts b/src/containers/Storage/StorageGroups/columns/hooks.ts index bb0948a07..3e7b9e518 100644 --- a/src/containers/Storage/StorageGroups/columns/hooks.ts +++ b/src/containers/Storage/StorageGroups/columns/hooks.ts @@ -2,11 +2,13 @@ import React from 'react'; import {VISIBLE_ENTITIES} from '../../../../store/reducers/storage/constants'; import {useSelectedColumns} from '../../../../utils/hooks/useSelectedColumns'; +import {getRequiredDataFields} from '../../../../utils/tableUtils/getRequiredDataFields'; import type {StorageViewContext} from '../../types'; import {getStorageGroupsColumns} from './columns'; import { DEFAULT_STORAGE_GROUPS_COLUMNS, + GROUPS_COLUMNS_TO_DATA_FIELDS, REQUIRED_STORAGE_GROUPS_COLUMNS, STORAGE_GROUPS_COLUMNS_IDS, STORAGE_GROUPS_COLUMNS_TITLES, @@ -38,11 +40,22 @@ export function useStorageGroupsSelectedColumns({ return REQUIRED_STORAGE_GROUPS_COLUMNS; }, [visibleEntities]); - return useSelectedColumns( + const {columnsToSelect, columnsToShow, setColumns} = useSelectedColumns( columns, STORAGE_GROUPS_SELECTED_COLUMNS_LS_KEY, STORAGE_GROUPS_COLUMNS_TITLES, DEFAULT_STORAGE_GROUPS_COLUMNS, requiredColumns, ); + + const dataFieldsRequired = React.useMemo(() => { + return getRequiredDataFields(columnsToShow, GROUPS_COLUMNS_TO_DATA_FIELDS); + }, [columnsToShow]); + + return { + columnsToSelect, + columnsToShow, + setColumns, + dataFieldsRequired, + }; } diff --git a/src/containers/Storage/StorageGroups/columns/types.ts b/src/containers/Storage/StorageGroups/columns/types.ts index 8697d4423..03c615cd6 100644 --- a/src/containers/Storage/StorageGroups/columns/types.ts +++ b/src/containers/Storage/StorageGroups/columns/types.ts @@ -1,11 +1,8 @@ -import type {Column as DataTableColumn} from '@gravity-ui/react-data-table'; - -import type {Column as PaginatedTableColumn} from '../../../../components/PaginatedTable'; import type {PreparedStorageGroup, VisibleEntities} from '../../../../store/reducers/storage/types'; +import type {Column} from '../../../../utils/tableUtils/types'; import type {StorageViewContext} from '../../types'; -export type StorageGroupsColumn = PaginatedTableColumn & - DataTableColumn; +export type StorageGroupsColumn = Column; export interface GetStorageColumnsData { viewContext: StorageViewContext; diff --git a/src/containers/Storage/StorageGroups/getGroups.ts b/src/containers/Storage/StorageGroups/getGroups.ts index 11f496453..310f5ec79 100644 --- a/src/containers/Storage/StorageGroups/getGroups.ts +++ b/src/containers/Storage/StorageGroups/getGroups.ts @@ -6,15 +6,20 @@ import type { PreparedStorageGroup, PreparedStorageGroupFilters, } from '../../../store/reducers/storage/types'; +import type {GroupsRequiredField} from '../../../types/api/storage'; import {prepareSortValue} from '../../../utils/filters'; import {isSortableStorageProperty} from '../../../utils/storage'; -type GetStorageGroups = FetchData; +type GetStorageGroups = FetchData< + PreparedStorageGroup, + PreparedStorageGroupFilters, + GroupsRequiredField +>; export function useGroupsGetter(shouldUseGroupsHandler: boolean) { const fetchData: GetStorageGroups = React.useCallback( async (params) => { - const {limit, offset, sortParams, filters} = params; + const {limit, offset, sortParams, filters, dataFieldsRequired} = params; const {sortOrder, columnId} = sortParams ?? {}; const { searchValue, @@ -43,6 +48,7 @@ export function useGroupsGetter(shouldUseGroupsHandler: boolean) { pDiskId, filter_group: filterGroup, filter_group_by: filterGroupBy, + fieldsRequired: dataFieldsRequired, shouldUseGroupsHandler, }); diff --git a/src/containers/Storage/StorageNodes/PaginatedStorageNodesTable.tsx b/src/containers/Storage/StorageNodes/PaginatedStorageNodesTable.tsx index 44985f5d6..47d8092ec 100644 --- a/src/containers/Storage/StorageNodes/PaginatedStorageNodesTable.tsx +++ b/src/containers/Storage/StorageNodes/PaginatedStorageNodesTable.tsx @@ -4,7 +4,7 @@ import type {RenderControls, RenderErrorMessage} from '../../../components/Pagin import {ResizeablePaginatedTable} from '../../../components/PaginatedTable'; import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants'; import type {VisibleEntities} from '../../../store/reducers/storage/types'; -import type {NodesGroupByField} from '../../../types/api/nodes'; +import type {NodesGroupByField, NodesRequiredField} from '../../../types/api/nodes'; import {NodesUptimeFilterValues} from '../../../utils/nodes'; import {StorageNodesEmptyDataMessage} from './StorageNodesEmptyDataMessage'; @@ -24,6 +24,8 @@ interface PaginatedStorageNodesTableProps { filterGroup?: string; filterGroupBy?: NodesGroupByField; + dataFieldsRequired?: NodesRequiredField[]; + searchValue: string; visibleEntities: VisibleEntities; nodesUptimeFilter: NodesUptimeFilterValues; @@ -45,6 +47,7 @@ export const PaginatedStorageNodesTable = ({ searchValue, visibleEntities, nodesUptimeFilter, + dataFieldsRequired, onShowAll, parentRef, renderControls, @@ -104,6 +107,7 @@ export const PaginatedStorageNodesTable = ({ renderEmptyDataMessage={renderEmptyDataMessage} getRowClassName={getRowUnavailableClassName} filters={tableFilters} + dataFieldsRequired={dataFieldsRequired} tableName="storage-nodes" /> ); diff --git a/src/containers/Storage/StorageNodes/columns/columns.tsx b/src/containers/Storage/StorageNodes/columns/columns.tsx index 8368df464..5f72e4123 100644 --- a/src/containers/Storage/StorageNodes/columns/columns.tsx +++ b/src/containers/Storage/StorageNodes/columns/columns.tsx @@ -13,12 +13,15 @@ import { getUptimeColumn, getVersionColumn, } from '../../../../components/nodesColumns/columns'; +import { + NODES_COLUMNS_IDS, + NODES_COLUMNS_TITLES, +} from '../../../../components/nodesColumns/constants'; import type {PreparedStorageNode} from '../../../../store/reducers/storage/types'; import {cn} from '../../../../utils/cn'; import {isSortableNodesProperty} from '../../../../utils/nodes'; import {PDisk} from '../../PDisk/PDisk'; -import {STORAGE_NODES_COLUMNS_IDS, STORAGE_NODES_COLUMNS_TITLES} from './constants'; import type {GetStorageNodesColumnsParams, StorageNodesColumn} from './types'; import './StorageNodesColumns.scss'; @@ -27,8 +30,8 @@ const b = cn('ydb-storage-nodes-columns'); const getPDisksColumn = ({viewContext}: GetStorageNodesColumnsParams): StorageNodesColumn => { return { - name: STORAGE_NODES_COLUMNS_IDS.PDisks, - header: STORAGE_NODES_COLUMNS_TITLES.PDisks, + name: NODES_COLUMNS_IDS.PDisks, + header: NODES_COLUMNS_TITLES.PDisks, className: b('pdisks-column'), render: ({row}) => { return ( diff --git a/src/containers/Storage/StorageNodes/columns/constants.ts b/src/containers/Storage/StorageNodes/columns/constants.ts index dbf89c581..30e81ca73 100644 --- a/src/containers/Storage/StorageNodes/columns/constants.ts +++ b/src/containers/Storage/StorageNodes/columns/constants.ts @@ -1,26 +1,14 @@ import type {SelectOption} from '@gravity-ui/uikit'; import {z} from 'zod'; -import { - NODES_COLUMNS_IDS as BASE_NODES_COLUMNS_IDS, - NODES_COLUMNS_TITLES as BASE_NODES_COLUMNS_TITLES, -} from '../../../../components/nodesColumns/constants'; +import type {NodesColumnId} from '../../../../components/nodesColumns/constants'; +import {NODES_COLUMNS_TITLES} from '../../../../components/nodesColumns/constants'; import type {NodesGroupByField} from '../../../../types/api/nodes'; -import type {ValueOf} from '../../../../types/common'; - -import i18n from './i18n'; export const STORAGE_NODES_COLUMNS_WIDTH_LS_KEY = 'storageNodesColumnsWidth'; export const STORAGE_NODES_SELECTED_COLUMNS_LS_KEY = 'storageNodesSelectedColumns'; -export const STORAGE_NODES_COLUMNS_IDS = { - ...BASE_NODES_COLUMNS_IDS, - PDisks: 'PDisks', -} as const; - -type StorageNodesColumnId = ValueOf; - -export const DEFAULT_STORAGE_NODES_COLUMNS: StorageNodesColumnId[] = [ +export const DEFAULT_STORAGE_NODES_COLUMNS: NodesColumnId[] = [ 'NodeId', 'Host', 'DC', @@ -29,16 +17,7 @@ export const DEFAULT_STORAGE_NODES_COLUMNS: StorageNodesColumnId[] = [ 'Uptime', 'PDisks', ]; -export const REQUIRED_STORAGE_NODES_COLUMNS: StorageNodesColumnId[] = ['NodeId']; - -// This code is running when module is initialized and correct language may not be set yet -// get functions guarantee that i18n fields will be inited on render with current render language -export const STORAGE_NODES_COLUMNS_TITLES = { - ...BASE_NODES_COLUMNS_TITLES, - get PDisks() { - return i18n('pdisks'); - }, -} as const satisfies Record; +export const REQUIRED_STORAGE_NODES_COLUMNS: NodesColumnId[] = ['NodeId']; const STORAGE_NODES_GROUP_BY_PARAMS = [ 'Host', @@ -54,7 +33,7 @@ export const STORAGE_NODES_GROUP_BY_OPTIONS: SelectOption[] = STORAGE_NODES_GROU (param) => { return { value: param, - content: STORAGE_NODES_COLUMNS_TITLES[param], + content: NODES_COLUMNS_TITLES[param], }; }, ); diff --git a/src/containers/Storage/StorageNodes/columns/hooks.ts b/src/containers/Storage/StorageNodes/columns/hooks.ts index 694238e1e..d7981b496 100644 --- a/src/containers/Storage/StorageNodes/columns/hooks.ts +++ b/src/containers/Storage/StorageNodes/columns/hooks.ts @@ -1,14 +1,18 @@ import React from 'react'; +import { + NODES_COLUMNS_IDS, + NODES_COLUMNS_TITLES, + NODES_COLUMNS_TO_DATA_FIELDS, +} from '../../../../components/nodesColumns/constants'; import {VISIBLE_ENTITIES} from '../../../../store/reducers/storage/constants'; import {useSelectedColumns} from '../../../../utils/hooks/useSelectedColumns'; +import {getRequiredDataFields} from '../../../../utils/tableUtils/getRequiredDataFields'; import {getStorageNodesColumns} from './columns'; import { DEFAULT_STORAGE_NODES_COLUMNS, REQUIRED_STORAGE_NODES_COLUMNS, - STORAGE_NODES_COLUMNS_IDS, - STORAGE_NODES_COLUMNS_TITLES, STORAGE_NODES_SELECTED_COLUMNS_LS_KEY, } from './constants'; import type {GetStorageNodesColumnsParams} from './types'; @@ -25,16 +29,27 @@ export function useStorageNodesSelectedColumns({ const requiredColumns = React.useMemo(() => { if (visibleEntities === VISIBLE_ENTITIES.missing) { - return [...REQUIRED_STORAGE_NODES_COLUMNS, STORAGE_NODES_COLUMNS_IDS.Missing]; + return [...REQUIRED_STORAGE_NODES_COLUMNS, NODES_COLUMNS_IDS.Missing]; } return REQUIRED_STORAGE_NODES_COLUMNS; }, [visibleEntities]); - return useSelectedColumns( + const {columnsToSelect, columnsToShow, setColumns} = useSelectedColumns( columns, STORAGE_NODES_SELECTED_COLUMNS_LS_KEY, - STORAGE_NODES_COLUMNS_TITLES, + NODES_COLUMNS_TITLES, DEFAULT_STORAGE_NODES_COLUMNS, requiredColumns, ); + + const dataFieldsRequired = React.useMemo(() => { + return getRequiredDataFields(columnsToShow, NODES_COLUMNS_TO_DATA_FIELDS); + }, [columnsToShow]); + + return { + columnsToSelect, + columnsToShow, + setColumns, + dataFieldsRequired, + }; } diff --git a/src/containers/Storage/StorageNodes/columns/i18n/en.json b/src/containers/Storage/StorageNodes/columns/i18n/en.json deleted file mode 100644 index ce968874b..000000000 --- a/src/containers/Storage/StorageNodes/columns/i18n/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pdisks": "PDisks" -} diff --git a/src/containers/Storage/StorageNodes/columns/i18n/index.ts b/src/containers/Storage/StorageNodes/columns/i18n/index.ts deleted file mode 100644 index a40213682..000000000 --- a/src/containers/Storage/StorageNodes/columns/i18n/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {registerKeysets} from '../../../../../utils/i18n'; - -import en from './en.json'; - -const COMPONENT = 'ydb-storage-nodes-columns'; - -export default registerKeysets(COMPONENT, {en}); diff --git a/src/containers/Storage/StorageNodes/columns/types.ts b/src/containers/Storage/StorageNodes/columns/types.ts index 960a33799..049f17a9b 100644 --- a/src/containers/Storage/StorageNodes/columns/types.ts +++ b/src/containers/Storage/StorageNodes/columns/types.ts @@ -1,6 +1,6 @@ -import type {Column} from '../../../../components/nodesColumns/types'; import type {PreparedStorageNode, VisibleEntities} from '../../../../store/reducers/storage/types'; import type {AdditionalNodesProps} from '../../../../types/additionalProps'; +import type {Column} from '../../../../utils/tableUtils/types'; import type {StorageViewContext} from '../../types'; export type StorageNodesColumn = Column; diff --git a/src/containers/Storage/StorageNodes/getNodes.ts b/src/containers/Storage/StorageNodes/getNodes.ts index 8efd58a91..39c6f5101 100644 --- a/src/containers/Storage/StorageNodes/getNodes.ts +++ b/src/containers/Storage/StorageNodes/getNodes.ts @@ -4,16 +4,25 @@ import type { PreparedStorageNodeFilters, } from '../../../store/reducers/storage/types'; import {prepareStorageNodesResponse} from '../../../store/reducers/storage/utils'; -import type {NodesRequestParams} from '../../../types/api/nodes'; +import type {NodesRequestParams, NodesRequiredField} from '../../../types/api/nodes'; import {prepareSortValue} from '../../../utils/filters'; import {getUptimeParamValue, isSortableNodesProperty} from '../../../utils/nodes'; export const getStorageNodes: FetchData< PreparedStorageNode, PreparedStorageNodeFilters, + NodesRequiredField, Pick > = async (params) => { - const {type = 'static', storage = true, limit, offset, sortParams, filters} = params; + const { + type = 'static', + storage = true, + limit, + offset, + sortParams, + filters, + dataFieldsRequired, + } = params; const { searchValue, nodesUptimeFilter, @@ -44,6 +53,7 @@ export const getStorageNodes: FetchData< group_id: groupId, filter_group: filterGroup, filter_group_by: filterGroupBy, + fieldsRequired: dataFieldsRequired, }); const preparedResponse = prepareStorageNodesResponse(response); return { diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx index 4d4a030e7..dafab258a 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx @@ -1,3 +1,5 @@ +import type {Column} from '@gravity-ui/react-data-table'; + import { getHostColumn, getLoadColumn, @@ -5,7 +7,7 @@ import { getVersionColumn, } from '../../../../../components/nodesColumns/columns'; import {NODES_COLUMNS_WIDTH_LS_KEY} from '../../../../../components/nodesColumns/constants'; -import type {Column, GetNodesColumnsParams} from '../../../../../components/nodesColumns/types'; +import type {GetNodesColumnsParams} from '../../../../../components/nodesColumns/types'; import {nodesApi} from '../../../../../store/reducers/nodes/nodes'; import type {NodesPreparedEntity} from '../../../../../store/reducers/nodes/types'; import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants'; diff --git a/src/store/reducers/tableData.ts b/src/store/reducers/tableData.ts index 8797d35c6..bc39359d4 100644 --- a/src/store/reducers/tableData.ts +++ b/src/store/reducers/tableData.ts @@ -4,24 +4,34 @@ import type {FetchData, PaginatedTableData, SortParams} from '../../components/P import {api} from './api'; -interface PaginatedTableParams { +interface PaginatedTableParams { offset: number; - fetchData: FetchData; - filters: F; + fetchData: FetchData; + filters: Filters; + dataFieldsRequired?: DataFieldType[]; limit: number; sortParams?: SortParams; tableName: string; } -function endpoints(build: EndpointBuilder) { +function endpoints( + build: EndpointBuilder, +) { return { - fetchTableChunk: build.query, PaginatedTableParams>({ - queryFn: async ({offset, limit, sortParams, filters, fetchData}, {signal}) => { + fetchTableChunk: build.query< + PaginatedTableData, + PaginatedTableParams + >({ + queryFn: async ( + {offset, limit, sortParams, filters, dataFieldsRequired, fetchData}, + {signal}, + ) => { try { const response = await fetchData({ limit, offset, filters, + dataFieldsRequired, sortParams, signal, }); diff --git a/src/types/api/storage.ts b/src/types/api/storage.ts index 21a92353f..c9d346224 100644 --- a/src/types/api/storage.ts +++ b/src/types/api/storage.ts @@ -284,7 +284,6 @@ export type GroupsRequiredField = | 'MissingDisks' | 'Degraded' | 'State' - | 'Usage' | 'Used' | 'Limit' | 'Usage' diff --git a/src/utils/tableUtils/getRequiredDataFields.ts b/src/utils/tableUtils/getRequiredDataFields.ts new file mode 100644 index 000000000..2befac648 --- /dev/null +++ b/src/utils/tableUtils/getRequiredDataFields.ts @@ -0,0 +1,17 @@ +export function getRequiredDataFields< + Column extends {name: string}, + ColumnId extends string, + RequiredField extends string, +>(columns: Column[], columnsToFields: Record) { + const columnsIds = columns.map((col) => col.name as ColumnId); + const requiredFieldsSet = columnsIds.reduce((fields, column) => { + const columnFields = columnsToFields[column]; + columnFields.forEach((field) => { + fields.add(field); + }); + + return fields; + }, new Set()); + + return Array.from(requiredFieldsSet); +} diff --git a/src/utils/tableUtils/types.ts b/src/utils/tableUtils/types.ts new file mode 100644 index 000000000..23d4d7f7f --- /dev/null +++ b/src/utils/tableUtils/types.ts @@ -0,0 +1,5 @@ +import type {Column as DataTableColumn} from '@gravity-ui/react-data-table'; + +import type {Column as PaginatedTableColumn} from '../../components/PaginatedTable'; + +export type Column = PaginatedTableColumn & DataTableColumn;