fix: added support for group by sorting in endpoints table

This commit is contained in:
sawhil 2025-04-24 01:01:54 +05:30 committed by Sahil Khan
parent 6504f2565b
commit 7edb047c0c
3 changed files with 121 additions and 82 deletions

View File

@ -1,5 +1,13 @@
import { LoadingOutlined } from '@ant-design/icons'; import { LoadingOutlined } from '@ant-design/icons';
import { Select, Spin, Table, Typography } from 'antd'; import {
Select,
Spin,
Table,
TablePaginationConfig,
TableProps,
Typography,
} from 'antd';
import { SorterResult } from 'antd/lib/table/interface';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import { ENTITY_VERSION_V4 } from 'constants/app'; import { ENTITY_VERSION_V4 } from 'constants/app';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
@ -53,6 +61,11 @@ function AllEndPoints({
{ value: string; label: string }[] { value: string; label: string }[]
>([]); >([]);
const [orderBy, setOrderBy] = useState<{
columnName: string;
order: 'asc' | 'desc';
} | null>(null);
const [expandedRowKeys, setExpandedRowKeys] = useState<React.Key[]>([]); const [expandedRowKeys, setExpandedRowKeys] = useState<React.Key[]>([]);
const handleGroupByChange = useCallback( const handleGroupByChange = useCallback(
@ -137,6 +150,7 @@ function AllEndPoints({
selectedRowData={record} selectedRowData={record}
setSelectedEndPointName={setSelectedEndPointName} setSelectedEndPointName={setSelectedEndPointName}
setSelectedView={setSelectedView} setSelectedView={setSelectedView}
orderBy={orderBy}
/> />
); );
@ -158,13 +172,34 @@ function AllEndPoints({
} }
}; };
const handleTableChange: TableProps<EndPointsTableRowData>['onChange'] = useCallback(
(
_pagination: TablePaginationConfig,
_filters: Record<string, (string | number | boolean)[] | null>,
sorter:
| SorterResult<EndPointsTableRowData>
| SorterResult<EndPointsTableRowData>[],
): void => {
if ('field' in sorter && sorter.order) {
setOrderBy({
columnName: sorter.field as string,
order: sorter.order === 'ascend' ? 'asc' : 'desc',
});
} else {
setOrderBy(null);
}
},
[],
);
const formattedEndPointsData = useMemo( const formattedEndPointsData = useMemo(
() => () =>
formatEndPointsDataForTable( formatEndPointsDataForTable(
allEndPointsData?.payload?.data?.result[0]?.table?.rows, allEndPointsData?.payload?.data?.result[0]?.table?.rows,
groupBy, groupBy,
orderBy,
), ),
[groupBy, allEndPointsData], [groupBy, allEndPointsData, orderBy],
); );
if (isError) { if (isError) {
@ -232,6 +267,7 @@ function AllEndPoints({
rowClassName={(_, index): string => rowClassName={(_, index): string =>
index % 2 === 0 ? 'table-row-dark' : 'table-row-light' index % 2 === 0 ? 'table-row-dark' : 'table-row-light'
} }
onChange={handleTableChange}
/> />
</div> </div>
</div> </div>

View File

@ -19,6 +19,7 @@ import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { SuccessResponse } from 'types/api'; import { SuccessResponse } from 'types/api';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { OrderByPayload } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import { VIEW_TYPES, VIEWS } from '../constants'; import { VIEW_TYPES, VIEWS } from '../constants';
@ -28,11 +29,13 @@ function ExpandedRow({
selectedRowData, selectedRowData,
setSelectedEndPointName, setSelectedEndPointName,
setSelectedView, setSelectedView,
orderBy,
}: { }: {
domainName: string; domainName: string;
selectedRowData: EndPointsTableRowData; selectedRowData: EndPointsTableRowData;
setSelectedEndPointName: (name: string) => void; setSelectedEndPointName: (name: string) => void;
setSelectedView: (view: VIEWS) => void; setSelectedView: (view: VIEWS) => void;
orderBy: OrderByPayload | null;
}): JSX.Element { }): JSX.Element {
const nestedColumns = useMemo(() => getEndPointsColumnsConfig(false, []), []); const nestedColumns = useMemo(() => getEndPointsColumnsConfig(false, []), []);
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>( const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
@ -100,6 +103,7 @@ function ExpandedRow({
? formatEndPointsDataForTable( ? formatEndPointsDataForTable(
groupedByRowQuery.data?.payload.data.result[0].table?.rows, groupedByRowQuery.data?.payload.data.result[0].table?.rows,
[], [],
orderBy,
) )
: [] : []
} }

View File

@ -24,6 +24,7 @@ import {
} from 'types/api/queryBuilder/queryAutocompleteResponse'; } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { import {
IBuilderQuery, IBuilderQuery,
OrderByPayload,
TagFilterItem, TagFilterItem,
} from 'types/api/queryBuilder/queryBuilderData'; } from 'types/api/queryBuilder/queryBuilderData';
import { QueryData } from 'types/api/widgets/getQuery'; import { QueryData } from 'types/api/widgets/getQuery';
@ -676,16 +677,6 @@ export const getEndPointsColumnsConfig = (
key: 'callCount', key: 'callCount',
width: 180, width: 180,
ellipsis: true, ellipsis: true,
sorter: (
rowA: EndPointsTableRowData,
rowB: EndPointsTableRowData,
): number => {
const callCountA =
rowA.callCount === '-' || rowA.callCount === 'n/a' ? 0 : rowA.callCount;
const callCountB =
rowB.callCount === '-' || rowB.callCount === 'n/a' ? 0 : rowB.callCount;
return Number(callCountA) - Number(callCountB);
},
align: 'right', align: 'right',
className: `column`, className: `column`,
}, },
@ -698,18 +689,7 @@ export const getEndPointsColumnsConfig = (
dataIndex: 'errorRate', dataIndex: 'errorRate',
key: 'errorRate', key: 'errorRate',
width: 120, width: 120,
sorter: ( sorter: true,
rowA: EndPointsTableRowData,
rowB: EndPointsTableRowData,
// eslint-disable-next-line sonarjs/no-identical-functions
): number => {
const errorRateA =
rowA.errorRate === '-' || rowA.errorRate === 'n/a' ? 0 : rowA.errorRate;
const errorRateB =
rowB.errorRate === '-' || rowB.errorRate === 'n/a' ? 0 : rowB.errorRate;
return Number(errorRateA) - Number(errorRateB);
},
align: 'right', align: 'right',
className: `column`, className: `column`,
render: ( render: (
@ -746,17 +726,7 @@ export const getEndPointsColumnsConfig = (
dataIndex: 'latency', dataIndex: 'latency',
key: 'latency', key: 'latency',
width: 120, width: 120,
sorter: ( sorter: true,
rowA: EndPointsTableRowData,
rowB: EndPointsTableRowData,
// eslint-disable-next-line sonarjs/no-identical-functions
): number => {
const latencyA =
rowA.latency === '-' || rowA.latency === 'n/a' ? 0 : rowA.latency;
const latencyB =
rowB.latency === '-' || rowB.latency === 'n/a' ? 0 : rowB.latency;
return Number(latencyA) - Number(latencyB);
},
align: 'right', align: 'right',
className: `column`, className: `column`,
}, },
@ -765,22 +735,7 @@ export const getEndPointsColumnsConfig = (
dataIndex: 'lastUsed', dataIndex: 'lastUsed',
key: 'lastUsed', key: 'lastUsed',
width: 120, width: 120,
sorter: ( sorter: true,
rowA: EndPointsTableRowData,
rowB: EndPointsTableRowData,
// eslint-disable-next-line sonarjs/no-identical-functions
): number => {
const dateA =
rowA.lastUsed === '-' || rowA.lastUsed === 'n/a'
? new Date(0).toISOString()
: rowA.lastUsed;
const dateB =
rowB.lastUsed === '-' || rowB.lastUsed === 'n/a'
? new Date(0).toISOString()
: rowB.lastUsed;
return new Date(dateB).getTime() - new Date(dateA).getTime();
},
align: 'right', align: 'right',
className: `column`, className: `column`,
// eslint-disable-next-line sonarjs/no-identical-functions // eslint-disable-next-line sonarjs/no-identical-functions
@ -794,12 +749,16 @@ export const getEndPointsColumnsConfig = (
export const formatEndPointsDataForTable = ( export const formatEndPointsDataForTable = (
data: EndPointsResponseRow[] | undefined, data: EndPointsResponseRow[] | undefined,
groupBy: BaseAutocompleteData[], groupBy: BaseAutocompleteData[],
orderBy?: OrderByPayload | null,
// eslint-disable-next-line sonarjs/cognitive-complexity // eslint-disable-next-line sonarjs/cognitive-complexity
): EndPointsTableRowData[] => { ): EndPointsTableRowData[] => {
if (!data) return []; if (!data) return [];
const isGroupedByAttribute = groupBy.length > 0; const isGroupedByAttribute = groupBy.length > 0;
let formattedData: EndPointsTableRowData[] = [];
if (!isGroupedByAttribute) { if (!isGroupedByAttribute) {
return data?.map((endpoint) => { formattedData = data?.map((endpoint) => {
const { port } = extractPortAndEndpoint( const { port } = extractPortAndEndpoint(
(endpoint.data['http.url'] as string) || '', (endpoint.data['http.url'] as string) || '',
); );
@ -826,39 +785,79 @@ export const formatEndPointsDataForTable = (
: Number(endpoint.data.F1), : Number(endpoint.data.F1),
}; };
}); });
} else {
const groupedByAttributeData = groupBy.map((attribute) => attribute.key);
formattedData = data?.map((endpoint) => {
const newEndpointName = groupedByAttributeData
.map((attribute) => endpoint.data[attribute])
.join(',');
return {
key: v4(),
endpointName: newEndpointName,
callCount:
endpoint.data.A === 'n/a' || endpoint.data.A === undefined
? '-'
: endpoint.data.A,
latency:
endpoint.data.B === 'n/a' || endpoint.data.B === undefined
? '-'
: Math.round(Number(endpoint.data.B) / 1000000), // Convert from nanoseconds to milliseconds
lastUsed:
endpoint.data.C === 'n/a' || endpoint.data.C === undefined
? '-'
: getLastUsedRelativeTime(Math.floor(Number(endpoint.data.C) / 1000000)), // Convert from nanoseconds to milliseconds
errorRate:
endpoint.data.D === 'n/a' || endpoint.data.D === undefined
? 0
: Number(endpoint.data.D),
groupedByMeta: groupedByAttributeData.reduce((acc, attribute) => {
acc[attribute] = endpoint.data[attribute] || '';
return acc;
}, {} as Record<string, string | number>),
};
});
} }
const groupedByAttributeData = groupBy.map((attribute) => attribute.key); // Apply sorting if orderBy is provided
if (orderBy) {
formattedData.sort((a, b) => {
let valueA: number | string = a[
orderBy.columnName as keyof EndPointsTableRowData
] as number | string;
let valueB: number | string = b[
orderBy.columnName as keyof EndPointsTableRowData
] as number | string;
return data?.map((endpoint) => { // Handle special cases for each column type
const newEndpointName = groupedByAttributeData if (
.map((attribute) => endpoint.data[attribute]) orderBy.columnName === 'callCount' ||
.join(','); orderBy.columnName === 'latency' ||
return { orderBy.columnName === 'errorRate'
key: v4(), ) {
endpointName: newEndpointName, valueA = valueA === '-' || valueA === 'n/a' ? 0 : Number(valueA);
callCount: valueB = valueB === '-' || valueB === 'n/a' ? 0 : Number(valueB);
endpoint.data.A === 'n/a' || endpoint.data.A === undefined } else if (orderBy.columnName === 'lastUsed') {
? '-' // confirm once the implication of this
: endpoint.data.A, valueA =
latency: valueA === '-' || valueA === 'n/a'
endpoint.data.B === 'n/a' || endpoint.data.B === undefined ? new Date(0).getTime()
? '-' : new Date(valueA as string).getTime();
: Math.round(Number(endpoint.data.B) / 1000000), // Convert from nanoseconds to milliseconds valueB =
lastUsed: valueB === '-' || valueB === 'n/a'
endpoint.data.C === 'n/a' || endpoint.data.C === undefined ? new Date(0).getTime()
? '-' : new Date(valueB as string).getTime();
: getLastUsedRelativeTime(Math.floor(Number(endpoint.data.C) / 1000000)), // Convert from nanoseconds to milliseconds }
errorRate:
endpoint.data.D === 'n/a' || endpoint.data.D === undefined // Apply sort direction
? 0 if (orderBy.order === 'asc') {
: Number(endpoint.data.D), return valueA > valueB ? 1 : -1;
groupedByMeta: groupedByAttributeData.reduce((acc, attribute) => { }
acc[attribute] = endpoint.data[attribute] || ''; return valueA < valueB ? 1 : -1;
return acc; });
}, {} as Record<string, string | number>), }
};
}); return formattedData;
}; };
export const createFiltersForSelectedRowData = ( export const createFiltersForSelectedRowData = (