refactor(ui): move sorting to server-side and fix filtered resource count

This commit is contained in:
alejandrobailo
2026-04-08 12:43:11 +02:00
parent 2db2641611
commit ea42e4374b
6 changed files with 19 additions and 21 deletions

View File

@@ -61,10 +61,12 @@ function normalizeFindingGroupResourceFilters(
return normalized;
}
const DEFAULT_FINDING_GROUPS_SORT = "-severity,-delta,-fail_count,-last_seen_at";
export const getFindingGroups = async ({
page = 1,
pageSize = 10,
sort = "",
sort = DEFAULT_FINDING_GROUPS_SORT,
filters = {},
}) => {
const headers = await getAuthHeaders({ contentType: false });
@@ -91,7 +93,7 @@ export const getFindingGroups = async ({
export const getLatestFindingGroups = async ({
page = 1,
pageSize = 10,
sort = "",
sort = DEFAULT_FINDING_GROUPS_SORT,
filters = {},
}) => {
const headers = await getAuthHeaders({ contentType: false });
@@ -135,8 +137,7 @@ export const getFindingGroupResources = async ({
if (page) url.searchParams.append("page[number]", page.toString());
if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
// Keep FAIL-first ordering when multiple statuses are returned.
url.searchParams.append("sort", "-status");
url.searchParams.append("sort", "-severity,-delta,-last_seen_at");
appendSanitizedProviderFilters(url, normalizedFilters);
@@ -172,8 +173,7 @@ export const getLatestFindingGroupResources = async ({
if (page) url.searchParams.append("page[number]", page.toString());
if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
// Keep FAIL-first ordering when multiple statuses are returned.
url.searchParams.append("sort", "-status");
url.searchParams.append("sort", "-severity,-delta,-last_seen_at");
appendSanitizedProviderFilters(url, normalizedFilters);

View File

@@ -379,6 +379,7 @@ export const getLatestFindingsByResourceUid = async ({
);
url.searchParams.append("filter[resource_uid]", resourceUid);
url.searchParams.append("sort", "-severity,status,-updated_at");
if (page) url.searchParams.append("page[number]", page.toString());
if (pageSize) url.searchParams.append("page[size]", pageSize.toString());

View File

@@ -120,12 +120,8 @@ const SSRDataTable = async ({
}) => {
const page = parseInt(searchParams.page?.toString() || "1", 10);
const pageSize = parseInt(searchParams.pageSize?.toString() || "10", 10);
const defaultSort = "-fail_count,-severity,-last_seen_at";
const { encodedSort } = extractSortAndKey({
...searchParams,
sort: searchParams.sort ?? defaultSort,
});
const { encodedSort } = extractSortAndKey(searchParams);
// Check if the searchParams contain any date or scan filter
const hasDateOrScan = hasDateOrScanFilter(searchParams);
@@ -135,7 +131,7 @@ const SSRDataTable = async ({
const findingGroupsData = await fetchFindingGroups({
page,
sort: encodedSort,
...(encodedSort && { sort: encodedSort }),
filters,
pageSize,
});

View File

@@ -83,7 +83,7 @@ export function FindingsGroupDrillDown({
setIsLoading(loading);
};
const { sentinelRef, refresh, loadMore } = useInfiniteResources({
const { sentinelRef, refresh, loadMore, totalCount } = useInfiniteResources({
checkId: group.checkId,
hasDateOrScanFilter: hasDateOrScan,
filters,
@@ -96,7 +96,7 @@ export function FindingsGroupDrillDown({
const drawer = useResourceDetailDrawer({
resources,
checkId: group.checkId,
totalResourceCount: group.resourcesTotal,
totalResourceCount: totalCount ?? group.resourcesTotal,
onRequestMoreResources: loadMore,
});

View File

@@ -181,7 +181,7 @@ export function InlineResourceContainer({
setIsLoading(loading);
};
const { sentinelRef, refresh, loadMore } = useInfiniteResources({
const { sentinelRef, refresh, loadMore, totalCount } = useInfiniteResources({
checkId: group.checkId,
hasDateOrScanFilter: hasDateOrScan,
filters,
@@ -195,7 +195,7 @@ export function InlineResourceContainer({
const drawer = useResourceDetailDrawer({
resources,
checkId: group.checkId,
totalResourceCount: group.resourcesTotal,
totalResourceCount: totalCount ?? group.resourcesTotal,
onRequestMoreResources: loadMore,
});

View File

@@ -32,6 +32,8 @@ interface UseInfiniteResourcesReturn {
refresh: () => void;
/** Imperatively load the next page (e.g. from drawer navigation). */
loadMore: () => void;
/** Total number of resources matching current filters (from API pagination). */
totalCount: number | null;
}
/**
@@ -60,6 +62,7 @@ export function useInfiniteResources({
const currentCheckIdRef = useRef(checkId);
const controllerRef = useRef<AbortController | null>(null);
const observerRef = useRef<IntersectionObserver | null>(null);
const totalCountRef = useRef<number | null>(null);
// Store latest values in refs so the fetch function always reads current values
// without being recreated on every render
@@ -104,12 +107,10 @@ export function useInfiniteResources({
return;
}
const resources = adaptFindingGroupResourcesResponse(
response,
forCheckId,
);
const resources = adaptFindingGroupResourcesResponse(response, forCheckId);
const totalPages = response?.meta?.pagination?.pages ?? 1;
const hasMore = page < totalPages;
totalCountRef.current = response?.meta?.pagination?.count ?? null;
// Commit the page number only after a successful (non-aborted) fetch.
// This prevents a premature pageRef increment from loadNextPage being
@@ -209,5 +210,5 @@ export function useInfiniteResources({
fetchPage(1, false, currentCheckIdRef.current, controller.signal);
}
return { sentinelRef, refresh, loadMore: loadNextPage };
return { sentinelRef, refresh, loadMore: loadNextPage, totalCount: totalCountRef.current };
}