Skip to content

Commit fba3527

Browse files
authored
ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI (#708)
* ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI
1 parent 1abb136 commit fba3527

18 files changed

Lines changed: 3418 additions & 419 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { renderHook } from '@testing-library/react';
19+
import { useVirtualization } from '../useVirtualization';
20+
21+
describe('useVirtualization', () => {
22+
it('should return empty values when items array is empty', () => {
23+
const { result } = renderHook(() =>
24+
useVirtualization({ items: [], scrollTop: 0 })
25+
);
26+
27+
expect(result.current).toEqual({
28+
visibleItems: [],
29+
paddingTop: 0,
30+
paddingBottom: 0,
31+
startIndex: 0,
32+
});
33+
});
34+
35+
it('should calculate visible items and padding correctly for initial state', () => {
36+
const items = Array.from({ length: 100 }, (_, i) => i);
37+
const { result } = renderHook(() =>
38+
useVirtualization({ items, scrollTop: 0, itemHeight: 37, overscan: 10, visibleCount: 40 })
39+
);
40+
41+
// Initial state (scrollTop = 0)
42+
// startIndex = max(0, 0 - 10) = 0
43+
// endIndex = min(99, 0 + 40 + 10) = 50
44+
// visibleItems = items.slice(0, 51)
45+
46+
expect(result.current.startIndex).toBe(0);
47+
expect(result.current.visibleItems.length).toBe(51);
48+
expect(result.current.paddingTop).toBe(0);
49+
50+
// paddingBottom = (100 - 1 - 50) * 37 = 49 * 37 = 1813
51+
expect(result.current.paddingBottom).toBe(1813);
52+
});
53+
54+
it('should calculate visible items correctly when scrolled down', () => {
55+
const items = Array.from({ length: 100 }, (_, i) => i);
56+
// Scrolled 20 items down: 20 * 37 = 740
57+
const { result } = renderHook(() =>
58+
useVirtualization({ items, scrollTop: 740, itemHeight: 37, overscan: 10, visibleCount: 40 })
59+
);
60+
61+
// startIndex = max(0, 20 - 10) = 10
62+
// endIndex = min(99, 20 + 40 + 10) = 70
63+
64+
expect(result.current.startIndex).toBe(10);
65+
expect(result.current.visibleItems.length).toBe(61); // 70 - 10 + 1
66+
67+
// paddingTop = 10 * 37 = 370
68+
expect(result.current.paddingTop).toBe(370);
69+
70+
// paddingBottom = (100 - 1 - 70) * 37 = 29 * 37 = 1073
71+
expect(result.current.paddingBottom).toBe(1073);
72+
});
73+
74+
it('should cap end index at total items length', () => {
75+
const items = Array.from({ length: 50 }, (_, i) => i);
76+
// Scrolled way past the bottom
77+
const { result } = renderHook(() =>
78+
useVirtualization({ items, scrollTop: 5000, itemHeight: 37, overscan: 10, visibleCount: 40 })
79+
);
80+
81+
// startIndex = max(0, 135 - 10) = 125
82+
// endIndex = min(49, 135 + 40 + 10) = 49
83+
// Wait, if startIndex > endIndex, slice will return empty array
84+
85+
expect(result.current.startIndex).toBe(125);
86+
expect(result.current.visibleItems.length).toBe(0);
87+
expect(result.current.paddingBottom).toBe(0);
88+
});
89+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { useMemo } from 'react';
19+
import { virtualizeList } from '../utils/Utils';
20+
21+
interface UseVirtualizationProps<T> {
22+
items: T[];
23+
scrollTop: number;
24+
itemHeight?: number;
25+
overscan?: number;
26+
visibleCount?: number;
27+
}
28+
29+
export const useVirtualization = <T>(options: UseVirtualizationProps<T>) => {
30+
return useMemo(() => {
31+
return virtualizeList(options);
32+
}, [options.items, options.scrollTop, options.itemHeight, options.overscan, options.visibleCount]);
33+
};

dashboard/src/utils/Enum.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,20 @@ export const auditAction: { [key: string]: string } = {
111111
AUTO_PURGE : "Auto Purged Entities"
112112
};
113113

114+
115+
export enum AuditOperation {
116+
PURGE = "PURGE",
117+
AUTO_PURGE = "AUTO_PURGE",
118+
IMPORT = "IMPORT",
119+
EXPORT = "EXPORT"
120+
}
121+
122+
export enum PurgeActiveView {
123+
NONE = "none",
124+
REQUESTED = "requested",
125+
PURGED = "purged"
126+
}
127+
114128
export const stats: any = {
115129
generalData: {
116130
collectionTime: "day"

dashboard/src/utils/Utils.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,45 @@ const getNavigate = () => {
801801
return backNavigate;
802802
};
803803

804+
const virtualizeList = (options: {
805+
items: any[];
806+
scrollTop: number;
807+
itemHeight?: number;
808+
overscan?: number;
809+
visibleCount?: number;
810+
}) => {
811+
const items = options.items || [];
812+
const scrollTop = options.scrollTop || 0;
813+
const itemHeight = options.itemHeight || 37;
814+
const overscan = options.overscan || 10;
815+
const visibleCount = options.visibleCount || 40;
816+
817+
const totalItems = items.length;
818+
if (totalItems === 0) {
819+
return {
820+
visibleItems: [],
821+
paddingTop: 0,
822+
paddingBottom: 0,
823+
startIndex: 0,
824+
};
825+
}
826+
827+
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
828+
const endIndex = Math.min(totalItems - 1, Math.floor(scrollTop / itemHeight) + visibleCount + overscan);
829+
830+
const visibleItems = items.slice(startIndex, endIndex + 1);
831+
832+
const paddingTop = startIndex * itemHeight;
833+
const paddingBottom = Math.max(0, (totalItems - 1 - endIndex) * itemHeight);
834+
835+
return {
836+
visibleItems,
837+
paddingTop,
838+
paddingBottom,
839+
startIndex,
840+
};
841+
};
842+
804843
const globalSearchFilterInitialQuery: any = {
805844
query: {},
806845
setQuery: (newQuery: any) => {
@@ -857,5 +896,7 @@ export {
857896
setNavigate,
858897
getNavigate,
859898
globalSearchParams,
860-
globalSearchFilterInitialQuery
899+
globalSearchFilterInitialQuery,
900+
virtualizeList
861901
};
902+

dashboard/src/views/Administrator/Audits/AdminAuditTable.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,33 @@ const AdminAuditTable = () => {
5454
const limit = pageSize || 25;
5555
const offset = (pageIndex || 0) * limit;
5656

57-
let params: any = {
58-
auditFilters: !isEmpty(queryApiObj) ? queryApiObj : null,
57+
let auditFilters = !isEmpty(queryApiObj) ? JSON.parse(JSON.stringify(queryApiObj)) : null;
58+
59+
if (auditFilters) {
60+
const filtersStr = JSON.stringify(auditFilters);
61+
if (filtersStr.includes('"attributeName":"runId"')) {
62+
// Remove any existing auditRowKind to prevent duplicates/conflicts
63+
const removeAuditRowKind = (node: Record<string, any>) => {
64+
if (node && node.criterion) {
65+
node.criterion = node.criterion.filter((c: Record<string, any>) => c.attributeName !== 'auditRowKind');
66+
node.criterion.forEach(removeAuditRowKind);
67+
}
68+
};
69+
removeAuditRowKind(auditFilters);
70+
71+
// Force append SUMMARY by wrapping the existing filter
72+
auditFilters = {
73+
condition: "AND",
74+
criterion: [
75+
auditFilters,
76+
{ attributeName: "auditRowKind", operator: "eq", attributeValue: "SUMMARY" }
77+
]
78+
};
79+
}
80+
}
81+
82+
let params: Record<string, unknown> = {
83+
auditFilters: auditFilters,
5984
limit: limit,
6085
sortOrder: "DESCENDING",
6186
offset: offset,
@@ -65,10 +90,10 @@ const AdminAuditTable = () => {
6590
try {
6691
setLoader(true);
6792
let searchResp = await getAuditData(params);
68-
setAuditData(searchResp.data);
93+
setAuditData(searchResp.data || []);
6994
setLoader(false);
70-
} catch (error: any) {
71-
console.error("Error fetching data:", error.response.data.errorMessage);
95+
} catch (error: unknown) {
96+
console.error("Error fetching data:", (error as any)?.response?.data?.errorMessage || (error as any)?.message);
7297
toast.dismiss(toastId.current);
7398
serverError(error, toastId);
7499
setLoader(false);

0 commit comments

Comments
 (0)