Skip to content

Commit

Permalink
feat: implement nested view
Browse files Browse the repository at this point in the history
  • Loading branch information
amlannandy committed Dec 19, 2024
1 parent 2023006 commit 7c82a11
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 41 deletions.
218 changes: 191 additions & 27 deletions frontend/src/container/InfraMonitoringK8s/Nodes/K8sNodesList.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
/* eslint-disable no-restricted-syntax */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import '../InfraMonitoringK8s.styles.scss';

import { LoadingOutlined } from '@ant-design/icons';
import {
Skeleton,
Button,
Spin,
Table,
TablePaginationConfig,
TableProps,
Typography,
} from 'antd';
import { SorterResult } from 'antd/es/table/interface';
import { ColumnType, 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 { ChevronDown, ChevronRight } from 'lucide-react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';

import K8sHeader from '../K8sHeader';
import LoadingContainer from '../LoadingContainer';
import { dummyColumnConfig } from '../utils';
import {
defaultAddedColumns,
formatDataForTable,
Expand Down Expand Up @@ -57,10 +61,68 @@ function K8sNodesList({

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

const [selectedRowData, setSelectedRowData] = useState<K8sNodesRowData | null>(
null,
);

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

const createFiltersForSelectedRowData = (
selectedRowData: K8sNodesRowData,
): IBuilderQuery['filters'] => {
const baseFilters: IBuilderQuery['filters'] = {
items: [],
op: 'and',
};

if (!selectedRowData) return baseFilters;

const { groupedByMeta } = selectedRowData;

for (const key of Object.keys(groupedByMeta)) {
baseFilters.items.push({
key: {
key,
},
op: '=',
value: groupedByMeta[key],
});
}

return baseFilters;
};

const fetchGroupedByRowDataQuery = useMemo(() => {
if (!selectedRowData) return null;

const baseQuery = getK8sNodesListQuery();

const filters = createFiltersForSelectedRowData(selectedRowData);

return {
...baseQuery,
limit: 10,
offset: 0,
filters,
start: Math.floor(minTime / 1000000),
end: Math.floor(maxTime / 1000000),
orderBy,
};
}, [minTime, maxTime, orderBy, selectedRowData]);

const {
data: groupedByRowData,
isFetching: isFetchingGroupedByRowData,
isLoading: isLoadingGroupedByRowData,
isError: isErrorGroupedByRowData,
refetch: fetchGroupedByRowData,
} = useGetK8sNodesList(fetchGroupedByRowDataQuery as K8sNodesListPayload, {
queryKey: ['nodeList', fetchGroupedByRowDataQuery],
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
});

const { currentQuery } = useQueryBuilder();

const {
Expand Down Expand Up @@ -106,10 +168,16 @@ function K8sNodesList({
return queryPayload;
}, [currentPage, minTime, maxTime, orderBy, groupBy, queryFilters]);

const formattedGroupedByNodesData = useMemo(
() =>
formatDataForTable(groupedByRowData?.payload?.data?.records || [], groupBy),
[groupedByRowData, groupBy],
);

const { data, isFetching, isLoading, isError } = useGetK8sNodesList(
query as K8sNodesListPayload,
{
queryKey: ['hostList', query],
queryKey: ['nodeList', query],
enabled: !!query,
},
);
Expand All @@ -124,6 +192,16 @@ function K8sNodesList({

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

const handleGroupByRowClick = (record: K8sNodesRowData): void => {
setSelectedRowData(record);
};

useEffect(() => {
if (selectedRowData) {
fetchGroupedByRowData();
}
}, [selectedRowData, fetchGroupedByRowData]);

const handleTableChange: TableProps<K8sNodesRowData>['onChange'] = useCallback(
(
pagination: TablePaginationConfig,
Expand Down Expand Up @@ -174,13 +252,117 @@ function K8sNodesList({
// }, [selectedNodeUID, nodesData]);

const handleRowClick = (record: K8sNodesRowData): void => {
// setselectedNodeUID(record.nodeUID);
if (groupBy.length === 0) {
setSelectedRowData(null);
// setselectedNodeUID(record.nodeUID);
} else {
handleGroupByRowClick(record);
}

logEvent('Infra Monitoring: K8s node list item clicked', {
nodeUID: record.nodeUID,
});
};

const nestedColumns = useMemo(() => {
const nestedColumns = getK8sNodesListColumns([]);
return [dummyColumnConfig, ...nestedColumns];
}, []);

const isGroupedByAttribute = groupBy.length > 0;

const handleExpandedRowViewAllClick = (): void => {
if (!selectedRowData) return;

const filters = createFiltersForSelectedRowData(selectedRowData);

handleFiltersChange(filters);

setCurrentPage(1);
setSelectedRowData(null);
setGroupBy([]);
setOrderBy(null);
};

const expandedRowRender = (): JSX.Element => (
<div className="expanded-table-container">
{isErrorGroupedByRowData && (
<Typography>{groupedByRowData?.error || 'Something went wrong'}</Typography>
)}
{isFetchingGroupedByRowData || isLoadingGroupedByRowData ? (
<LoadingContainer />
) : (
<div className="expanded-table">
<Table
columns={nestedColumns as ColumnType<K8sNodesRowData>[]}
dataSource={formattedGroupedByNodesData}
pagination={false}
scroll={{ x: true }}
tableLayout="fixed"
size="small"
loading={{
spinning: isFetchingGroupedByRowData || isLoadingGroupedByRowData,
indicator: <Spin indicator={<LoadingOutlined size={14} spin />} />,
}}
/>

{groupedByRowData?.payload?.data?.total &&
groupedByRowData?.payload?.data?.total > 10 && (
<div className="expanded-table-footer">
<Button
type="default"
size="small"
className="periscope-btn secondary"
onClick={handleExpandedRowViewAllClick}
>
View All
</Button>
</div>
)}
</div>
)}
</div>
);

const expandRowIconRenderer = ({
expanded,
onExpand,
record,
}: {
expanded: boolean;
onExpand: (
record: K8sNodesRowData,
e: React.MouseEvent<HTMLButtonElement>,
) => void;
record: K8sNodesRowData;
}): JSX.Element | null => {
if (!isGroupedByAttribute) {
return null;
}

return expanded ? (
<Button
className="periscope-btn ghost"
onClick={(e: React.MouseEvent<HTMLButtonElement>): void =>
onExpand(record, e)
}
role="button"
>
<ChevronDown size={14} />
</Button>
) : (
<Button
className="periscope-btn ghost"
onClick={(e: React.MouseEvent<HTMLButtonElement>): void =>
onExpand(record, e)
}
role="button"
>
<ChevronRight size={14} />
</Button>
);
};

// const handleCloseNodeDetail = (): void => {
// setselectedNodeUID(null);
// };
Expand Down Expand Up @@ -259,28 +441,7 @@ function K8sNodesList({
</div>
)}

{(isFetching || isLoading) && (
<div className="k8s-list-loading-state">
<Skeleton.Input
className="k8s-list-loading-state-item"
size="large"
block
active
/>
<Skeleton.Input
className="k8s-list-loading-state-item"
size="large"
block
active
/>
<Skeleton.Input
className="k8s-list-loading-state-item"
size="large"
block
active
/>
</div>
)}
{(isFetching || isLoading) && <LoadingContainer />}

{showsNodesTable && (
<Table
Expand All @@ -300,12 +461,15 @@ function K8sNodesList({
indicator: <Spin indicator={<LoadingOutlined size={14} spin />} />,
}}
tableLayout="fixed"
rowKey={(record): string => record.nodeUID}
onChange={handleTableChange}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => handleRowClick(record),
className: 'clickable-row',
})}
expandable={{
expandedRowRender: isGroupedByAttribute ? expandedRowRender : undefined,
expandIcon: expandRowIconRenderer,
}}
/>
)}
{/* TODO - Handle Node Details flow */}
Expand Down
28 changes: 14 additions & 14 deletions frontend/src/container/InfraMonitoringK8s/Nodes/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Button, Tag } from 'antd';
import { Tag } from 'antd';
import { ColumnType } from 'antd/es/table';
import {
K8sNodesData,
K8sNodesListPayload,
} from 'api/infraMonitoring/getK8sNodesList';
import { ChevronRight, Group } from 'lucide-react';
import { Group } from 'lucide-react';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';

import { IEntityColumn } from '../utils';
Expand Down Expand Up @@ -51,15 +51,16 @@ export const defaultAddedColumns: IEntityColumn[] = [
export interface K8sNodesRowData {
key: string;
nodeUID: string;
nodeName: string;
nodeName: React.ReactNode;
clusterName: string;
cpuUtilization: React.ReactNode;
cpuAllocatable: React.ReactNode;
memoryUtilization: React.ReactNode;
memoryAllocatable: React.ReactNode;
groupedByMeta?: any;
}

const podGroupColumnConfig = {
const nodeGroupColumnConfig = {
title: (
<div className="column-header node-group-header">
<Group size={14} /> NODE GROUP
Expand Down Expand Up @@ -141,7 +142,7 @@ export const getK8sNodesListColumns = (
const filteredColumns = [...columnsConfig].filter(
(column) => column.key !== 'nodeName',
);
filteredColumns.unshift(podGroupColumnConfig);
filteredColumns.unshift(nodeGroupColumnConfig);
return filteredColumns as ColumnType<K8sNodesRowData>[];
}

Expand All @@ -160,14 +161,6 @@ const getGroupByEle = (

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}
Expand All @@ -184,11 +177,18 @@ export const formatDataForTable = (
data.map((node, index) => ({
key: `${node.nodeUID}-${index}`,
nodeUID: node.nodeUID || '',
nodeName: node.meta.k8s_node_name,
nodeName: (
<div className="pod-name-container">
<div className="pod-name">{node.meta.k8s_node_name || ''}</div>
</div>
),
clusterName: node.meta.k8s_cluster_name,
cpuUtilization: node.nodeCPUUsage,
memoryUtilization: node.nodeMemoryUsage,
cpuAllocatable: node.nodeCPUAllocatable,
memoryAllocatable: node.nodeMemoryAllocatable,
nodeGroup: getGroupByEle(node, groupBy),
meta: node.meta,
...node.meta,
groupedByMeta: node.meta,
}));

0 comments on commit 7c82a11

Please sign in to comment.