/* eslint-disable no-restricted-syntax */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import '../InfraMonitoringK8s.styles.scss'; import './K8sVolumesList.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 { K8sVolumesListPayload } from 'api/infraMonitoring/getK8sVolumesList'; import classNames from 'classnames'; import { useGetK8sVolumesList } from 'hooks/infraMonitoring/useGetK8sVolumesList'; 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 { K8sCategory, K8sEntityToAggregateAttributeMapping, } from '../constants'; import K8sHeader from '../K8sHeader'; import LoadingContainer from '../LoadingContainer'; import { usePageSize } from '../utils'; import { defaultAddedColumns, formatDataForTable, getK8sVolumesListColumns, getK8sVolumesListQuery, K8sVolumesRowData, } from './utils'; import VolumeDetails from './VolumeDetails'; // eslint-disable-next-line sonarjs/cognitive-complexity function K8sVolumesList({ isFiltersVisible, handleFilterVisibilityChange, quickFiltersLastUpdated, }: { isFiltersVisible: boolean; handleFilterVisibilityChange: () => void; quickFiltersLastUpdated: number; }): JSX.Element { const { maxTime, minTime } = useSelector( (state) => state.globalTime, ); const [currentPage, setCurrentPage] = useState(1); const [expandedRowKeys, setExpandedRowKeys] = useState([]); const [orderBy, setOrderBy] = useState<{ columnName: string; order: 'asc' | 'desc'; } | null>(null); const [selectedVolumeUID, setselectedVolumeUID] = useState( null, ); const { pageSize, setPageSize } = usePageSize(K8sCategory.VOLUMES); const [groupBy, setGroupBy] = useState([]); const [ selectedRowData, setSelectedRowData, ] = useState(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(() => { setCurrentPage(1); }, [quickFiltersLastUpdated]); const createFiltersForSelectedRowData = ( selectedRowData: K8sVolumesRowData, 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 = getK8sVolumesListQuery(); 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, } = useGetK8sVolumesList(fetchGroupedByRowDataQuery as K8sVolumesListPayload, { queryKey: ['volumeList', fetchGroupedByRowDataQuery], enabled: !!fetchGroupedByRowDataQuery && !!selectedRowData, }); const { data: groupByFiltersData, isLoading: isLoadingGroupByFilters, } = useGetAggregateKeys( { dataSource: currentQuery.builder.queryData[0].dataSource, aggregateAttribute: K8sEntityToAggregateAttributeMapping[K8sCategory.NODES], aggregateOperator: 'noop', searchText: '', tagType: '', }, { queryKey: [currentQuery.builder.queryData[0].dataSource, 'noop'], }, true, K8sCategory.NODES, ); const query = useMemo(() => { const baseQuery = getK8sVolumesListQuery(); 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 formattedGroupedByVolumesData = useMemo( () => formatDataForTable(groupedByRowData?.payload?.data?.records || [], groupBy), [groupedByRowData, groupBy], ); const { data, isFetching, isLoading, isError } = useGetK8sVolumesList( query as K8sVolumesListPayload, { queryKey: ['volumeList', query], enabled: !!query, }, ); const volumesData = useMemo(() => data?.payload?.data?.records || [], [data]); const totalCount = data?.payload?.data?.total || 0; const formattedVolumesData = useMemo( () => formatDataForTable(volumesData, groupBy), [volumesData, groupBy], ); const columns = useMemo(() => getK8sVolumesListColumns(groupBy), [groupBy]); const handleGroupByRowClick = (record: K8sVolumesRowData): 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['onChange'] = useCallback( ( pagination: TablePaginationConfig, _filters: Record, sorter: SorterResult | SorterResult[], ): void => { if (pagination.current) { setCurrentPage(pagination.current); } if ('field' in sorter && sorter.order) { setOrderBy({ columnName: sorter.field as string, order: sorter.order === 'ascend' ? 'asc' : 'desc', }); } else { setOrderBy(null); } }, [], ); const { handleChangeQueryData } = useQueryOperations({ index: 0, query: currentQuery.builder.queryData[0], entityVersion: '', }); const handleFiltersChange = useCallback( (value: IBuilderQuery['filters']): void => { handleChangeQueryData('filters', value); setCurrentPage(1); logEvent('Infra Monitoring: K8s list filters applied', { filters: value, }); }, [handleChangeQueryData], ); useEffect(() => { logEvent('Infra Monitoring: K8s list page visited', {}); }, []); const selectedVolumeData = useMemo(() => { if (!selectedVolumeUID) return null; return ( volumesData.find( (volume) => volume.persistentVolumeClaimName === selectedVolumeUID, ) || null ); }, [selectedVolumeUID, volumesData]); const handleRowClick = (record: K8sVolumesRowData): void => { if (groupBy.length === 0) { setSelectedRowData(null); setselectedVolumeUID(record.volumeUID); } else { handleGroupByRowClick(record); } logEvent('Infra Monitoring: K8s volume list item clicked', { volumeUID: record.volumeUID, }); }; const nestedColumns = useMemo(() => getK8sVolumesListColumns([]), []); 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); }; const expandedRowRender = (): JSX.Element => (
{isErrorGroupedByRowData && ( {groupedByRowData?.error || 'Something went wrong'} )} {isFetchingGroupedByRowData || isLoadingGroupedByRowData ? ( ) : (
[]} dataSource={formattedGroupedByVolumesData} pagination={false} scroll={{ x: true }} tableLayout="fixed" size="small" loading={{ spinning: isFetchingGroupedByRowData || isLoadingGroupedByRowData, indicator: } />, }} showHeader={false} /> {groupedByRowData?.payload?.data?.total && groupedByRowData?.payload?.data?.total > 10 ? (
) : null} )} ); const expandRowIconRenderer = ({ expanded, onExpand, record, }: { expanded: boolean; onExpand: ( record: K8sVolumesRowData, e: React.MouseEvent, ) => void; record: K8sVolumesRowData; }): JSX.Element | null => { if (!isGroupedByAttribute) { return null; } return expanded ? ( ) : ( ); }; const handleCloseVolumeDetail = (): void => { setselectedVolumeUID(null); }; 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); } } setCurrentPage(1); setGroupBy(groupBy); setExpandedRowKeys([]); }, [groupByFiltersData], ); useEffect(() => { if (groupByFiltersData?.payload) { setGroupByOptions( groupByFiltersData?.payload?.attributeKeys?.map((filter) => ({ value: filter.key, label: filter.key, })) || [], ); } }, [groupByFiltersData]); return (
{isError && {data?.error || 'Something went wrong'}}
{ setCurrentPage(page); setPageSize(pageSize); }, }} scroll={{ x: true }} loading={{ spinning: isFetching || isLoading, indicator: } />, }} locale={{ emptyText: isFetching || isLoading ? null : (
thinking-emoji This query had no results. Edit your query and try again!
), }} 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, }} /> ); } export default K8sVolumesList;