Skip to content

Commit

Permalink
feat: implement group-by and quick select in node list table
Browse files Browse the repository at this point in the history
  • Loading branch information
amlannandy committed Dec 18, 2024
1 parent cb06024 commit f8b95d1
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
StatefulsetsQuickFiltersConfig,
VolumesQuickFiltersConfig,
} from './constants';
import K8sNodesList from './Nodes/K8sNodesList';
import K8sPodLists from './Pods/K8sPodLists';
import Volumes from './Volumes/Volumes';

Expand Down Expand Up @@ -358,6 +359,13 @@ export default function InfraMonitoringK8s(): JSX.Element {
/>
)}

{selectedCategory === K8sCategories.NODES && (
<K8sNodesList
isFiltersVisible={showFilters}
handleFilterVisibilityChange={handleFilterVisibilityChange}
/>
)}

{selectedCategory === K8sCategories.VOLUMES && <Volumes />}
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/container/InfraMonitoringK8s/K8sHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ function K8sHeader({
<Button
type="text"
className="periscope-btn ghost"
disabled={selectedGroupBy.length > 0}
disabled={selectedGroupBy?.length > 0}
onClick={(): void => setIsFiltersSidePanelOpen(true)}
>
<SlidersHorizontal size={14} />
Expand Down
122 changes: 99 additions & 23 deletions frontend/src/container/InfraMonitoringK8s/Nodes/K8sNodesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { SorterResult } from 'antd/es/table/interface';
import logEvent from 'api/common/logEvent';
import { K8sNodesListPayload } from 'api/infraMonitoring/getK8sNodesList';
import { useGetK8sNodesList } from 'hooks/infraMonitoring/useGetK8sNodesList';
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
Expand Down Expand Up @@ -43,11 +46,6 @@ function K8sNodesList({

const [currentPage, setCurrentPage] = useState(1);

const [filters, setFilters] = useState<IBuilderQuery['filters']>({
items: [],
op: 'and',
});

const [orderBy, setOrderBy] = useState<{
columnName: string;
order: 'asc' | 'desc';
Expand All @@ -57,18 +55,56 @@ function K8sNodesList({

const pageSize = 10;

const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>([]);

const [groupByOptions, setGroupByOptions] = useState<
{ value: string; label: string }[]
>([]);

const { currentQuery } = useQueryBuilder();

const {
data: groupByFiltersData,
isLoading: isLoadingGroupByFilters,
} = useGetAggregateKeys(
{
dataSource: currentQuery.builder.queryData[0].dataSource,
aggregateAttribute: '',
aggregateOperator: 'noop',
searchText: '',
tagType: '',
},
{
queryKey: [currentQuery.builder.queryData[0].dataSource, 'noop'],
},
true,
);

const queryFilters = useMemo(
() =>
currentQuery?.builder?.queryData[0]?.filters || {
items: [],
op: 'and',
},
[currentQuery?.builder?.queryData],
);

const query = useMemo(() => {
const baseQuery = getK8sNodesListQuery();
return {
const queryPayload = {
...baseQuery,
limit: pageSize,
offset: (currentPage - 1) * pageSize,
filters,
filters: queryFilters,
start: Math.floor(minTime / 1000000),
end: Math.floor(maxTime / 1000000),
orderBy,
};
}, [currentPage, filters, minTime, maxTime, orderBy]);
if (groupBy.length > 0) {
queryPayload.groupBy = groupBy;
}
return queryPayload;
}, [currentPage, minTime, maxTime, orderBy, groupBy, queryFilters]);

const { data, isFetching, isLoading, isError } = useGetK8sNodesList(
query as K8sNodesListPayload,
Expand All @@ -81,11 +117,12 @@ function K8sNodesList({
const nodesData = useMemo(() => data?.payload?.data?.records || [], [data]);
const totalCount = data?.payload?.data?.total || 0;

const formattedNodesData = useMemo(() => formatDataForTable(nodesData), [
nodesData,
]);
const formattedNodesData = useMemo(
() => formatDataForTable(nodesData, groupBy),
[nodesData, groupBy],
);

const columns = useMemo(() => getK8sNodesListColumns(), []);
const columns = useMemo(() => getK8sNodesListColumns(groupBy), [groupBy]);

const handleTableChange: TableProps<K8sNodesRowData>['onChange'] = useCallback(
(
Expand All @@ -109,19 +146,22 @@ function K8sNodesList({
[],
);

const { handleChangeQueryData } = useQueryOperations({
index: 0,
query: currentQuery.builder.queryData[0],
entityVersion: '',
});

const handleFiltersChange = useCallback(
(value: IBuilderQuery['filters']): void => {
const isNewFilterAdded = value.items.length !== filters.items.length;
if (isNewFilterAdded) {
setFilters(value);
setCurrentPage(1);
handleChangeQueryData('filters', value);
setCurrentPage(1);

logEvent('Infra Monitoring: K8s list filters applied', {
filters: value,
});
}
logEvent('Infra Monitoring: K8s list filters applied', {
filters: value,
});
},
[filters],
[handleChangeQueryData],
);

useEffect(() => {
Expand Down Expand Up @@ -149,13 +189,45 @@ function K8sNodesList({
!isError &&
!isLoading &&
!isFetching &&
!(formattedNodesData.length === 0 && filters.items.length > 0);
!(formattedNodesData.length === 0 && queryFilters.items.length > 0);

const showNoFilteredNodesMessage =
!isFetching &&
!isLoading &&
formattedNodesData.length === 0 &&
filters.items.length > 0;
queryFilters.items.length > 0;

const handleGroupByChange = useCallback(
(value: IBuilderQuery['groupBy']) => {
const groupBy = [];

for (let index = 0; index < value.length; index++) {
const element = (value[index] as unknown) as string;

const key = groupByFiltersData?.payload?.attributeKeys?.find(
(key) => key.key === element,
);

if (key) {
groupBy.push(key);
}
}

setGroupBy(groupBy);
},
[groupByFiltersData],
);

useEffect(() => {
if (groupByFiltersData?.payload) {
setGroupByOptions(
groupByFiltersData?.payload?.attributeKeys?.map((filter) => ({
value: filter.key,
label: filter.key,
})) || [],
);
}
}, [groupByFiltersData]);

return (
<div className="k8s-list">
Expand All @@ -164,6 +236,10 @@ function K8sNodesList({
handleFilterVisibilityChange={handleFilterVisibilityChange}
defaultAddedColumns={defaultAddedColumns}
handleFiltersChange={handleFiltersChange}
groupByOptions={groupByOptions}
isLoadingGroupByFilters={isLoadingGroupByFilters}
handleGroupByChange={handleGroupByChange}
selectedGroupBy={groupBy}
/>
{isError && <Typography>{data?.error || 'Something went wrong'}</Typography>}

Expand Down
67 changes: 64 additions & 3 deletions frontend/src/container/InfraMonitoringK8s/Nodes/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Button, Tag } from 'antd';
import { ColumnType } from 'antd/es/table';
import {
K8sNodesData,
K8sNodesListPayload,
} from 'api/infraMonitoring/getK8sNodesList';
import { ChevronRight, Group } from 'lucide-react';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';

import { IEntityColumn } from '../utils';

Expand Down Expand Up @@ -56,6 +59,20 @@ export interface K8sNodesRowData {
memoryAllocatable: React.ReactNode;
}

const podGroupColumnConfig = {
title: (
<div className="column-header node-group-header">
<Group size={14} /> NODE GROUP
</div>
),
dataIndex: 'nodeGroup',
key: 'nodeGroup',
ellipsis: true,
width: 150,
align: 'left',
sorter: false,
};

export const getK8sNodesListQuery = (): K8sNodesListPayload => ({
filters: {
items: [],
Expand Down Expand Up @@ -117,10 +134,53 @@ const columnsConfig = [
},
];

export const getK8sNodesListColumns = (): ColumnType<K8sNodesRowData>[] =>
columnsConfig as ColumnType<K8sNodesRowData>[];
export const getK8sNodesListColumns = (
groupBy: IBuilderQuery['groupBy'],
): ColumnType<K8sNodesRowData>[] => {
if (groupBy.length > 0) {
const filteredColumns = [...columnsConfig].filter(
(column) => column.key !== 'nodeName',
);
filteredColumns.unshift(podGroupColumnConfig);
return filteredColumns as ColumnType<K8sNodesRowData>[];
}

return columnsConfig as ColumnType<K8sNodesRowData>[];
};

const getGroupByEle = (
node: K8sNodesData,
groupBy: IBuilderQuery['groupBy'],
): React.ReactNode => {
const groupByValues: string[] = [];

groupBy.forEach((group) => {
groupByValues.push(node.meta[group.key as keyof typeof node.meta]);
});

return (
<div className="pod-group">
<div className="expand-group">
<Button
type="text"
className="expand-group-icon periscope-btn ghost"
icon={<ChevronRight size={14} />}
/>
</div>

{groupByValues.map((value) => (
<Tag key={value} color="#1D212D" className="pod-group-tag-item">
{value === '' ? '<no-value>' : value}
</Tag>
))}
</div>
);
};

export const formatDataForTable = (data: K8sNodesData[]): K8sNodesRowData[] =>
export const formatDataForTable = (
data: K8sNodesData[],
groupBy: IBuilderQuery['groupBy'],
): K8sNodesRowData[] =>
data.map((node, index) => ({
key: `${node.nodeUID}-${index}`,
nodeUID: node.nodeUID || '',
Expand All @@ -130,4 +190,5 @@ export const formatDataForTable = (data: K8sNodesData[]): K8sNodesRowData[] =>
memoryUtilization: node.nodeMemoryUsage,
cpuAllocatable: node.nodeCPUAllocatable,
memoryAllocatable: node.nodeMemoryAllocatable,
nodeGroup: getGroupByEle(node, groupBy),
}));
21 changes: 20 additions & 1 deletion frontend/src/container/InfraMonitoringK8s/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,33 @@ export const PodsQuickFiltersConfig: IQuickFiltersConfig[] = [
export const NodesQuickFiltersConfig: IQuickFiltersConfig[] = [
{
type: FiltersType.CHECKBOX,
title: 'Nodes',
title: 'Node Name',
attributeKey: {
key: 'k8s_node_name',
dataType: DataTypes.String,
type: 'resource',
isColumn: false,
isJSON: false,
// id: 'k8s_pod_name--string--tag--true',
},
aggregateOperator: 'noop',
aggregateAttribute: 'k8s_pod_cpu_utilization',
dataSource: DataSource.METRICS,
defaultOpen: true,
},
{
type: FiltersType.CHECKBOX,
title: 'Cluster Name',
attributeKey: {
key: 'k8s_cluster_name',
dataType: DataTypes.String,
type: 'resource',
isColumn: false,
isJSON: false,
},
aggregateOperator: 'noop',
aggregateAttribute: 'k8s_pod_cpu_utilization',
dataSource: DataSource.METRICS,
defaultOpen: true,
},
];
Expand Down

0 comments on commit f8b95d1

Please sign in to comment.