mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-14 05:21:33 +08:00
654 lines
18 KiB
TypeScript
654 lines
18 KiB
TypeScript
/* eslint-disable no-restricted-syntax */
|
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
|
import '../InfraMonitoringK8s.styles.scss';
|
|
import './K8sNamespacesList.styles.scss';
|
|
|
|
import { LoadingOutlined } from '@ant-design/icons';
|
|
import {
|
|
Button,
|
|
Spin,
|
|
Table,
|
|
TablePaginationConfig,
|
|
TableProps,
|
|
Typography,
|
|
} from 'antd';
|
|
import { ColumnType, SorterResult } from 'antd/es/table/interface';
|
|
import logEvent from 'api/common/logEvent';
|
|
import { K8sNamespacesListPayload } from 'api/infraMonitoring/getK8sNamespacesList';
|
|
import { InfraMonitoringEvents } from 'constants/events';
|
|
import { useGetK8sNamespacesList } from 'hooks/infraMonitoring/useGetK8sNamespacesList';
|
|
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 { useSearchParams } from 'react-router-dom-v5-compat';
|
|
import { AppState } from 'store/reducers';
|
|
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
|
|
|
import { getOrderByFromParams } from '../commonUtils';
|
|
import {
|
|
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
|
K8sCategory,
|
|
K8sEntityToAggregateAttributeMapping,
|
|
} from '../constants';
|
|
import K8sHeader from '../K8sHeader';
|
|
import LoadingContainer from '../LoadingContainer';
|
|
import { usePageSize } from '../utils';
|
|
import NamespaceDetails from './NamespaceDetails';
|
|
import {
|
|
defaultAddedColumns,
|
|
formatDataForTable,
|
|
getK8sNamespacesListColumns,
|
|
getK8sNamespacesListQuery,
|
|
K8sNamespacesRowData,
|
|
} from './utils';
|
|
|
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
function K8sNamespacesList({
|
|
isFiltersVisible,
|
|
handleFilterVisibilityChange,
|
|
quickFiltersLastUpdated,
|
|
}: {
|
|
isFiltersVisible: boolean;
|
|
handleFilterVisibilityChange: () => void;
|
|
quickFiltersLastUpdated: number;
|
|
}): JSX.Element {
|
|
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
|
(state) => state.globalTime,
|
|
);
|
|
|
|
const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
|
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
|
|
const [currentPage, setCurrentPage] = useState(() => {
|
|
const page = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE);
|
|
if (page) {
|
|
return parseInt(page, 10);
|
|
}
|
|
return 1;
|
|
});
|
|
const [filtersInitialised, setFiltersInitialised] = useState(false);
|
|
|
|
useEffect(() => {
|
|
setSearchParams({
|
|
...Object.fromEntries(searchParams.entries()),
|
|
[INFRA_MONITORING_K8S_PARAMS_KEYS.CURRENT_PAGE]: currentPage.toString(),
|
|
});
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [currentPage]);
|
|
|
|
const [orderBy, setOrderBy] = useState<{
|
|
columnName: string;
|
|
order: 'asc' | 'desc';
|
|
} | null>(() => getOrderByFromParams(searchParams, true));
|
|
|
|
const [selectedNamespaceUID, setselectedNamespaceUID] = useState<
|
|
string | null
|
|
>(() => {
|
|
const namespaceUID = searchParams.get(
|
|
INFRA_MONITORING_K8S_PARAMS_KEYS.NAMESPACE_UID,
|
|
);
|
|
if (namespaceUID) {
|
|
return namespaceUID;
|
|
}
|
|
return null;
|
|
});
|
|
|
|
const { pageSize, setPageSize } = usePageSize(K8sCategory.NAMESPACES);
|
|
|
|
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
|
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
|
if (groupBy) {
|
|
const decoded = decodeURIComponent(groupBy);
|
|
const parsed = JSON.parse(decoded);
|
|
return parsed as IBuilderQuery['groupBy'];
|
|
}
|
|
return [];
|
|
});
|
|
|
|
const [
|
|
selectedRowData,
|
|
setSelectedRowData,
|
|
] = useState<K8sNamespacesRowData | null>(null);
|
|
|
|
const [groupByOptions, setGroupByOptions] = useState<
|
|
{ value: string; label: string }[]
|
|
>([]);
|
|
|
|
const { currentQuery } = useQueryBuilder();
|
|
|
|
const queryFilters = useMemo(
|
|
() =>
|
|
currentQuery?.builder?.queryData[0]?.filters || {
|
|
items: [],
|
|
op: 'and',
|
|
},
|
|
[currentQuery?.builder?.queryData],
|
|
);
|
|
|
|
// Reset pagination every time quick filters are changed
|
|
useEffect(() => {
|
|
if (quickFiltersLastUpdated !== -1) {
|
|
setCurrentPage(1);
|
|
}
|
|
}, [quickFiltersLastUpdated]);
|
|
|
|
const createFiltersForSelectedRowData = (
|
|
selectedRowData: K8sNamespacesRowData,
|
|
groupBy: IBuilderQuery['groupBy'],
|
|
): IBuilderQuery['filters'] => {
|
|
const baseFilters: IBuilderQuery['filters'] = {
|
|
items: [...queryFilters.items],
|
|
op: 'and',
|
|
};
|
|
|
|
if (!selectedRowData) return baseFilters;
|
|
|
|
const { groupedByMeta } = selectedRowData;
|
|
|
|
for (const key of groupBy) {
|
|
baseFilters.items.push({
|
|
key: {
|
|
key: key.key,
|
|
type: null,
|
|
},
|
|
op: '=',
|
|
value: groupedByMeta[key.key],
|
|
id: key.key,
|
|
});
|
|
}
|
|
|
|
return baseFilters;
|
|
};
|
|
|
|
const fetchGroupedByRowDataQuery = useMemo(() => {
|
|
if (!selectedRowData) return null;
|
|
|
|
const baseQuery = getK8sNamespacesListQuery();
|
|
|
|
const filters = createFiltersForSelectedRowData(selectedRowData, groupBy);
|
|
|
|
return {
|
|
...baseQuery,
|
|
limit: 10,
|
|
offset: 0,
|
|
filters,
|
|
start: Math.floor(minTime / 1000000),
|
|
end: Math.floor(maxTime / 1000000),
|
|
orderBy,
|
|
};
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [minTime, maxTime, orderBy, selectedRowData, groupBy]);
|
|
|
|
const {
|
|
data: groupedByRowData,
|
|
isFetching: isFetchingGroupedByRowData,
|
|
isLoading: isLoadingGroupedByRowData,
|
|
isError: isErrorGroupedByRowData,
|
|
refetch: fetchGroupedByRowData,
|
|
} = useGetK8sNamespacesList(
|
|
fetchGroupedByRowDataQuery as K8sNamespacesListPayload,
|
|
{
|
|
queryKey: ['namespaceList', fetchGroupedByRowDataQuery],
|
|
enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData,
|
|
},
|
|
);
|
|
|
|
const {
|
|
data: groupByFiltersData,
|
|
isLoading: isLoadingGroupByFilters,
|
|
} = useGetAggregateKeys(
|
|
{
|
|
dataSource: currentQuery.builder.queryData[0].dataSource,
|
|
aggregateAttribute:
|
|
K8sEntityToAggregateAttributeMapping[K8sCategory.NAMESPACES],
|
|
aggregateOperator: 'noop',
|
|
searchText: '',
|
|
tagType: '',
|
|
},
|
|
{
|
|
queryKey: [currentQuery.builder.queryData[0].dataSource, 'noop'],
|
|
},
|
|
true,
|
|
K8sCategory.NODES,
|
|
);
|
|
|
|
const query = useMemo(() => {
|
|
const baseQuery = getK8sNamespacesListQuery();
|
|
const queryPayload = {
|
|
...baseQuery,
|
|
limit: pageSize,
|
|
offset: (currentPage - 1) * pageSize,
|
|
filters: queryFilters,
|
|
start: Math.floor(minTime / 1000000),
|
|
end: Math.floor(maxTime / 1000000),
|
|
orderBy,
|
|
};
|
|
if (groupBy.length > 0) {
|
|
queryPayload.groupBy = groupBy;
|
|
}
|
|
return queryPayload;
|
|
}, [pageSize, currentPage, queryFilters, minTime, maxTime, orderBy, groupBy]);
|
|
|
|
const formattedGroupedByNamespacesData = useMemo(
|
|
() =>
|
|
formatDataForTable(groupedByRowData?.payload?.data?.records || [], groupBy),
|
|
[groupedByRowData, groupBy],
|
|
);
|
|
|
|
const { data, isFetching, isLoading, isError } = useGetK8sNamespacesList(
|
|
query as K8sNamespacesListPayload,
|
|
{
|
|
queryKey: ['namespaceList', query],
|
|
enabled: !!query,
|
|
},
|
|
);
|
|
|
|
const namespacesData = useMemo(() => data?.payload?.data?.records || [], [
|
|
data,
|
|
]);
|
|
const totalCount = data?.payload?.data?.total || 0;
|
|
|
|
const formattedNamespacesData = useMemo(
|
|
() => formatDataForTable(namespacesData, groupBy),
|
|
[namespacesData, groupBy],
|
|
);
|
|
|
|
const nestedNamespacesData = useMemo(() => {
|
|
if (!selectedRowData || !groupedByRowData?.payload?.data.records) return [];
|
|
return groupedByRowData?.payload?.data?.records || [];
|
|
}, [groupedByRowData, selectedRowData]);
|
|
|
|
const columns = useMemo(() => getK8sNamespacesListColumns(groupBy), [groupBy]);
|
|
|
|
const handleGroupByRowClick = (record: K8sNamespacesRowData): void => {
|
|
setSelectedRowData(record);
|
|
|
|
if (expandedRowKeys.includes(record.key)) {
|
|
setExpandedRowKeys(expandedRowKeys.filter((key) => key !== record.key));
|
|
} else {
|
|
setExpandedRowKeys([record.key]);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (selectedRowData) {
|
|
fetchGroupedByRowData();
|
|
}
|
|
}, [selectedRowData, fetchGroupedByRowData]);
|
|
|
|
const handleTableChange: TableProps<K8sNamespacesRowData>['onChange'] = useCallback(
|
|
(
|
|
pagination: TablePaginationConfig,
|
|
_filters: Record<string, (string | number | boolean)[] | null>,
|
|
sorter:
|
|
| SorterResult<K8sNamespacesRowData>
|
|
| SorterResult<K8sNamespacesRowData>[],
|
|
): void => {
|
|
if (pagination.current) {
|
|
setCurrentPage(pagination.current);
|
|
logEvent(InfraMonitoringEvents.PageNumberChanged, {
|
|
entity: InfraMonitoringEvents.K8sEntity,
|
|
page: InfraMonitoringEvents.ListPage,
|
|
category: InfraMonitoringEvents.Namespace,
|
|
});
|
|
}
|
|
|
|
if ('field' in sorter && sorter.order) {
|
|
const currentOrderBy = {
|
|
columnName: sorter.field as string,
|
|
order: (sorter.order === 'ascend' ? 'asc' : 'desc') as 'asc' | 'desc',
|
|
};
|
|
setOrderBy(currentOrderBy);
|
|
setSearchParams({
|
|
...Object.fromEntries(searchParams.entries()),
|
|
[INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(
|
|
currentOrderBy,
|
|
),
|
|
});
|
|
} else {
|
|
setOrderBy(null);
|
|
setSearchParams({
|
|
...Object.fromEntries(searchParams.entries()),
|
|
[INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null),
|
|
});
|
|
}
|
|
},
|
|
[searchParams, setSearchParams],
|
|
);
|
|
|
|
const { handleChangeQueryData } = useQueryOperations({
|
|
index: 0,
|
|
query: currentQuery.builder.queryData[0],
|
|
entityVersion: '',
|
|
});
|
|
|
|
const handleFiltersChange = useCallback(
|
|
(value: IBuilderQuery['filters']): void => {
|
|
handleChangeQueryData('filters', value);
|
|
if (filtersInitialised) {
|
|
setCurrentPage(1);
|
|
} else {
|
|
setFiltersInitialised(true);
|
|
}
|
|
|
|
if (value.items.length > 0) {
|
|
logEvent(InfraMonitoringEvents.FilterApplied, {
|
|
entity: InfraMonitoringEvents.K8sEntity,
|
|
page: InfraMonitoringEvents.ListPage,
|
|
category: InfraMonitoringEvents.Namespace,
|
|
});
|
|
}
|
|
},
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
[],
|
|
);
|
|
|
|
useEffect(() => {
|
|
logEvent(InfraMonitoringEvents.PageVisited, {
|
|
entity: InfraMonitoringEvents.K8sEntity,
|
|
page: InfraMonitoringEvents.ListPage,
|
|
category: InfraMonitoringEvents.Namespace,
|
|
total: data?.payload?.data?.total,
|
|
});
|
|
}, [data?.payload?.data?.total]);
|
|
|
|
const selectedNamespaceData = useMemo(() => {
|
|
if (!selectedNamespaceUID) return null;
|
|
if (groupBy.length > 0) {
|
|
// If grouped by, return the namespace from the formatted grouped by namespaces data
|
|
return (
|
|
nestedNamespacesData.find(
|
|
(namespace) => namespace.namespaceName === selectedNamespaceUID,
|
|
) || null
|
|
);
|
|
}
|
|
// If not grouped by, return the node from the nodes data
|
|
return (
|
|
namespacesData.find(
|
|
(namespace) => namespace.namespaceName === selectedNamespaceUID,
|
|
) || null
|
|
);
|
|
}, [
|
|
selectedNamespaceUID,
|
|
groupBy.length,
|
|
namespacesData,
|
|
nestedNamespacesData,
|
|
]);
|
|
|
|
const handleRowClick = (record: K8sNamespacesRowData): void => {
|
|
if (groupBy.length === 0) {
|
|
setSelectedRowData(null);
|
|
setselectedNamespaceUID(record.namespaceUID);
|
|
setSearchParams({
|
|
...Object.fromEntries(searchParams.entries()),
|
|
[INFRA_MONITORING_K8S_PARAMS_KEYS.NAMESPACE_UID]: record.namespaceUID,
|
|
});
|
|
} else {
|
|
handleGroupByRowClick(record);
|
|
}
|
|
|
|
logEvent(InfraMonitoringEvents.ItemClicked, {
|
|
entity: InfraMonitoringEvents.K8sEntity,
|
|
page: InfraMonitoringEvents.ListPage,
|
|
category: InfraMonitoringEvents.Namespace,
|
|
});
|
|
};
|
|
|
|
const nestedColumns = useMemo(() => getK8sNamespacesListColumns([]), []);
|
|
|
|
const isGroupedByAttribute = groupBy.length > 0;
|
|
|
|
const handleExpandedRowViewAllClick = (): void => {
|
|
if (!selectedRowData) return;
|
|
|
|
const filters = createFiltersForSelectedRowData(selectedRowData, groupBy);
|
|
|
|
handleFiltersChange(filters);
|
|
|
|
setCurrentPage(1);
|
|
setSelectedRowData(null);
|
|
setGroupBy([]);
|
|
setOrderBy(null);
|
|
setSearchParams({
|
|
...Object.fromEntries(searchParams.entries()),
|
|
[INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify([]),
|
|
[INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(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<K8sNamespacesRowData>[]}
|
|
dataSource={formattedGroupedByNamespacesData}
|
|
pagination={false}
|
|
scroll={{ x: true }}
|
|
tableLayout="fixed"
|
|
size="small"
|
|
loading={{
|
|
spinning: isFetchingGroupedByRowData || isLoadingGroupedByRowData,
|
|
indicator: <Spin indicator={<LoadingOutlined size={14} spin />} />,
|
|
}}
|
|
showHeader={false}
|
|
onRow={(record): { onClick: () => void; className: string } => ({
|
|
onClick: (): void => {
|
|
setselectedNamespaceUID(record.namespaceUID);
|
|
},
|
|
className: 'expanded-clickable-row',
|
|
})}
|
|
/>
|
|
|
|
{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>
|
|
) : null}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
|
|
const expandRowIconRenderer = ({
|
|
expanded,
|
|
onExpand,
|
|
record,
|
|
}: {
|
|
expanded: boolean;
|
|
onExpand: (
|
|
record: K8sNamespacesRowData,
|
|
e: React.MouseEvent<HTMLButtonElement>,
|
|
) => void;
|
|
record: K8sNamespacesRowData;
|
|
}): 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 handleCloseNamespaceDetail = (): void => {
|
|
setselectedNamespaceUID(null);
|
|
setSearchParams({
|
|
...Object.fromEntries(
|
|
Array.from(searchParams.entries()).filter(
|
|
([key]) =>
|
|
![
|
|
INFRA_MONITORING_K8S_PARAMS_KEYS.NAMESPACE_UID,
|
|
INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW,
|
|
INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS,
|
|
INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS,
|
|
INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS,
|
|
].includes(key),
|
|
),
|
|
),
|
|
});
|
|
};
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Reset pagination on switching to groupBy
|
|
setCurrentPage(1);
|
|
setGroupBy(groupBy);
|
|
setExpandedRowKeys([]);
|
|
setSearchParams({
|
|
...Object.fromEntries(searchParams.entries()),
|
|
[INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify(groupBy),
|
|
});
|
|
|
|
logEvent(InfraMonitoringEvents.GroupByChanged, {
|
|
entity: InfraMonitoringEvents.K8sEntity,
|
|
page: InfraMonitoringEvents.ListPage,
|
|
category: InfraMonitoringEvents.Namespace,
|
|
});
|
|
},
|
|
[groupByFiltersData, searchParams, setSearchParams],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (groupByFiltersData?.payload) {
|
|
setGroupByOptions(
|
|
groupByFiltersData?.payload?.attributeKeys?.map((filter) => ({
|
|
value: filter.key,
|
|
label: filter.key,
|
|
})) || [],
|
|
);
|
|
}
|
|
}, [groupByFiltersData]);
|
|
|
|
const onPaginationChange = (page: number, pageSize: number): void => {
|
|
setCurrentPage(page);
|
|
setPageSize(pageSize);
|
|
logEvent(InfraMonitoringEvents.PageNumberChanged, {
|
|
entity: InfraMonitoringEvents.K8sEntity,
|
|
page: InfraMonitoringEvents.ListPage,
|
|
category: InfraMonitoringEvents.Namespace,
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="k8s-list">
|
|
<K8sHeader
|
|
isFiltersVisible={isFiltersVisible}
|
|
handleFilterVisibilityChange={handleFilterVisibilityChange}
|
|
defaultAddedColumns={defaultAddedColumns}
|
|
handleFiltersChange={handleFiltersChange}
|
|
groupByOptions={groupByOptions}
|
|
isLoadingGroupByFilters={isLoadingGroupByFilters}
|
|
handleGroupByChange={handleGroupByChange}
|
|
selectedGroupBy={groupBy}
|
|
entity={K8sCategory.NODES}
|
|
/>
|
|
{isError && <Typography>{data?.error || 'Something went wrong'}</Typography>}
|
|
|
|
<Table
|
|
className="k8s-list-table namespaces-list-table"
|
|
dataSource={isFetching || isLoading ? [] : formattedNamespacesData}
|
|
columns={columns}
|
|
pagination={{
|
|
current: currentPage,
|
|
pageSize,
|
|
total: totalCount,
|
|
showSizeChanger: true,
|
|
hideOnSinglePage: false,
|
|
onChange: onPaginationChange,
|
|
}}
|
|
scroll={{ x: true }}
|
|
loading={{
|
|
spinning: isFetching || isLoading,
|
|
indicator: <Spin indicator={<LoadingOutlined size={14} spin />} />,
|
|
}}
|
|
locale={{
|
|
emptyText:
|
|
isFetching || isLoading ? null : (
|
|
<div className="no-filtered-hosts-message-container">
|
|
<div className="no-filtered-hosts-message-content">
|
|
<img
|
|
src="/Icons/emptyState.svg"
|
|
alt="thinking-emoji"
|
|
className="empty-state-svg"
|
|
/>
|
|
|
|
<Typography.Text className="no-filtered-hosts-message">
|
|
This query had no results. Edit your query and try again!
|
|
</Typography.Text>
|
|
</div>
|
|
</div>
|
|
),
|
|
}}
|
|
tableLayout="fixed"
|
|
onChange={handleTableChange}
|
|
onRow={(record): { onClick: () => void; className: string } => ({
|
|
onClick: (): void => handleRowClick(record),
|
|
className: 'clickable-row',
|
|
})}
|
|
expandable={{
|
|
expandedRowRender: isGroupedByAttribute ? expandedRowRender : undefined,
|
|
expandIcon: expandRowIconRenderer,
|
|
expandedRowKeys,
|
|
}}
|
|
/>
|
|
<NamespaceDetails
|
|
namespace={selectedNamespaceData}
|
|
isModalTimeSelection
|
|
onClose={handleCloseNamespaceDetail}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default K8sNamespacesList;
|