Repository: RedHatInsights/insights-remediations-frontend
Pull request #480: Implement selection, page select, and sorting on overviewPage
Author: login: adonispuente
Created: 2025-03-14T19:02:04Z | Merged: 2025-03-24T17:00:05Z

# Description

Associated Jira ticket: # (issue)
[RHINENG-16094](https://issues.redhat.com/browse/RHINENG-16094) 
and partially [RHINENG-16095](https://issues.redhat.com/browse/RHINENG-16094)

In the overview page you should be able to sort on each column -> some options not supported by the BE but the api request should look valid

You can also select items on the list


# How to test the PR

Run the PR on stage env, FF is already on, its on the home page so as so as you navigate to the app youll see the table. Just start clicking the columns and verify the table and request indicate sorted data.



Diff:
diff --git a/src/Frameworks/AsyncTableTools/AsyncTableTools/hooks/useTableSort/useTableSort.js b/src/Frameworks/AsyncTableTools/AsyncTableTools/hooks/useTableSort/useTableSort.js
index 1152d532..83a2c870 100644
--- a/src/Frameworks/AsyncTableTools/AsyncTableTools/hooks/useTableSort/useTableSort.js
+++ b/src/Frameworks/AsyncTableTools/AsyncTableTools/hooks/useTableSort/useTableSort.js
@@ -77,7 +77,6 @@ const useTableSort = (columns, options = {}) => {
     ...sortBy,
     index: sortBy?.index + offset,
   };
-
   return {
     tableProps: {
       onSort,
diff --git a/src/Frameworks/AsyncTableTools/AsyncTableTools/hooks/useTableView/useTableView.test.js b/src/Frameworks/AsyncTableTools/AsyncTableTools/hooks/useTableView/useTableView.test.js
index bd79545d..d0e508a7 100644
--- a/src/Frameworks/AsyncTableTools/AsyncTableTools/hooks/useTableView/useTableView.test.js
+++ b/src/Frameworks/AsyncTableTools/AsyncTableTools/hooks/useTableView/useTableView.test.js
@@ -44,6 +44,12 @@ describe('useTableView', () => {
       expect(result.current.table.tableProps.rows.length).toEqual(100)
     );
   });
+  //TODO: Add empty state
+  // it('returns an empty state if it is loaded, but has no items', () => {
+  //   const { result } = renderHook(
+  //     () => useRowsView([], columns),
+  //     DEFAULT_RENDER_OPTIONS
+  //   );
 
   // it('returns an empty state if it is loaded, but has no items', () => {
   //   const { result } = renderHook(
diff --git a/src/Utilities/Hooks/api/helpers.js b/src/Utilities/Hooks/api/helpers.js
new file mode 100644
index 00000000..43f41ea4
--- /dev/null
+++ b/src/Utilities/Hooks/api/helpers.js
@@ -0,0 +1,32 @@
+export const compileResult = (fetchResult, params) => {
+  const data = fetchResult.data?.data || fetchResult.data;
+  const meta = fetchResult.data?.meta;
+
+  return {
+    data,
+    meta: {
+      ...params,
+      ...(meta || {}),
+    },
+  };
+};
+
+const defaultCompileResult = (fetchResult) => fetchResult;
+
+export const fetchResult = async (
+  fn,
+  params,
+  convertToArray,
+  compileResult = defaultCompileResult
+) => {
+  const convertedParams =
+    (convertToArray && !Array.isArray(params)
+      ? convertToArray(params)
+      : params) || [];
+
+  if (Array.isArray(convertedParams)) {
+    return compileResult(await fn(...convertedParams), params);
+  } else {
+    return compileResult(await fn(convertedParams), params);
+  }
+};
diff --git a/src/Utilities/Hooks/api/useRemediations.js b/src/Utilities/Hooks/api/useRemediations.js
index e286d3b8..c805fb26 100644
--- a/src/Utilities/Hooks/api/useRemediations.js
+++ b/src/Utilities/Hooks/api/useRemediations.js
@@ -1,5 +1,6 @@
 import useRemediationsQuery from '../../../api/useRemediationsQuery';
 
+//TODO: This is currently not used, but will be when upgraded to JS Clients
 const convertToArray = (params) => {
   if (Array.isArray(params)) {
     return params;
diff --git a/src/Utilities/Hooks/useQuery.js b/src/Utilities/Hooks/useQuery.js
index 82a1e785..fcb3e591 100644
--- a/src/Utilities/Hooks/useQuery.js
+++ b/src/Utilities/Hooks/useQuery.js
@@ -1,5 +1,6 @@
 import { useRef, useState } from 'react';
 import { useDeepCompareEffect, useDeepCompareCallback } from 'use-deep-compare';
+import { fetchResult } from './api/helpers';
 
 /**
  *
@@ -19,6 +20,7 @@ import { useDeepCompareEffect, useDeepCompareCallback } from 'use-deep-compare';
  *  @param   {object}         [options]                Includes options like params and skip
  *  @param   {Array | object} [options.params]         Parameters passed to the request to make. If an array is passed it will be spread as arguments!
  *  @param   {boolean}        [options.convertToArray] A function to use to convert a params object into an arguments array to pass to the fetch function
+ *  @param   {boolean}        [options.compileResult]  A function to use to convert the request response
  *  @param   {boolean}        [options.skip]           Wether or not to skip the request
  *
  *  @returns {useQueryReturn}                          An object containing a data, loading and error state, as well as a fetch and refetch function.
@@ -35,68 +37,45 @@ import { useDeepCompareEffect, useDeepCompareCallback } from 'use-deep-compare';
  *
  */
 const useQuery = (fn, options = {}) => {
-  const { params = {}, skip = false, convertToArray } = options;
+  // TODO Maybe both convertToArray and compileResult might be more useComplianceQuerys responsibility?
+  const { params = {}, skip = false, convertToArray, compileResult } = options;
   const mounted = useRef(true);
   // TODO we do not clear the data before we perform a request
   // This can under certain conditions cause odd behaviour.
   // For example, in tables when paginating, data from one page will be displayed as long as the next page is not loaded.
   // Sometimes this is wanted, but other times this might cause unwanted behaviour. We should be able to control that.
   const [data, setData] = useState(undefined);
-  const [meta, setMeta] = useState(undefined);
   const [error, setError] = useState(undefined);
   const [loading, setLoading] = useState(false);
-  console.log(params, 'params outside useQuery here');
 
   const fetchFn = useDeepCompareCallback(
     async (fn, params, setDataState = true) => {
-      console.log(params, 'params in useQuery here');
-      if (loading) {
-        return;
-      }
-
       if (setDataState) {
-        setLoading(true);
-        try {
-          const data = await (async (params) => {
-            const convertedParams = convertToArray
-              ? convertToArray(params)
-              : params;
+        if (!loading) {
+          setLoading(true);
+          try {
+            const result = await fetchResult(
+              fn,
+              params,
+              convertToArray,
+              compileResult
+            );
 
-            if (Array.isArray(convertedParams)) {
-              return await fn(...convertedParams);
-            } else {
-              return await fn(convertedParams);
+            if (mounted.current) {
+              setData(result);
+              setLoading(false);
+            }
+          } catch (e) {
+            console.log(e);
+            if (mounted.current) {
+              setError(e);
+              setLoading(false);
             }
-          })(params);
-
-          if (mounted.current) {
-            console.log(data, 'data inside the mount');
-            setData(data?.data || data);
-            setMeta(data?.meta || null);
-            setLoading(false);
-          }
-        } catch (e) {
-          console.log(e);
-          if (mounted.current) {
-            setError(e);
-            setLoading(false);
           }
         }
       } else {
         try {
-          const data = await (async (params) => {
-            const convertedParams = convertToArray
-              ? convertToArray(params)
-              : params;
-
-            if (Array.isArray(convertedParams)) {
-              return await fn(...convertedParams);
-            } else {
-              return await fn(convertedParams);
-            }
-          })(params);
-
-          return data?.data || data;
+          return await fetchResult(fn, params, convertToArray, compileResult);
         } catch (e) {
           console.log(e);
           throw e;
@@ -107,7 +86,7 @@ const useQuery = (fn, options = {}) => {
   );
 
   const fetch = useDeepCompareCallback(
-    async (fetchParams, setDataState) =>
+    async (fetchParams) =>
       await fetchFn(
         fn,
         !Array.isArray(fetchParams) && !Array.isArray(params)
@@ -116,12 +95,12 @@ const useQuery = (fn, options = {}) => {
               ...fetchParams,
             }
           : fetchParams,
-        setDataState
+        false
       ),
     [fn, fetchFn, params]
   );
   const refetch = useDeepCompareCallback(
-    async () => fetchFn(fn, params),
+    async () => await fetchFn(fn, params),
     [fetchFn, fn, params]
   );
 
@@ -134,9 +113,7 @@ const useQuery = (fn, options = {}) => {
       mounted.current = false;
     };
   }, [skip, params, typeof refetch !== 'undefined']);
-
-  console.log(data, 'data here in useQuery');
-  return { data, meta, error, loading, fetch, refetch };
+  return { result: data, error, loading, fetch, refetch };
 };
 
 export default useQuery;
diff --git a/src/api/useRemediationTableState.js b/src/api/useRemediationTableState.js
index dbec369e..772242bc 100644
--- a/src/api/useRemediationTableState.js
+++ b/src/api/useRemediationTableState.js
@@ -12,19 +12,16 @@ const useRemediationTableState = (useTableState, paramsOption) => {
   const params = useDeepCompareMemo(() => {
     if (!Array.isArray(paramsOption)) {
       const { filter: filterParam, ...paramsParam } = paramsOption || {};
-
-      const filter =
-        filterParam && filterState
-          ? `(${filterParam}) AND (${filterState})`
-          : filterState || filterParam;
+      const sort = sortBy;
 
       return useTableState
         ? {
             ...paramsParam,
             limit,
             offset,
-            sortBy,
-            ...(filter ? { filter } : {}),
+            sort,
+            ...filterParam,
+            ...filterState,
           }
         : paramsOption;
     } else {
diff --git a/src/api/useRemediationsQuery.js b/src/api/useRemediationsQuery.js
index 41fabad9..dea82770 100644
--- a/src/api/useRemediationsQuery.js
+++ b/src/api/useRemediationsQuery.js
@@ -11,7 +11,7 @@ import useFetchTotalBatched from '../Utilities/Hooks/useFetchTotalBatched';
  *  @property {object} [pagination]        API pagination params
  *  @property {object} [pagination.offset] Pagination offset
  *  @property {object} [pagination.limit]  Pagination limit (maximum 100)
- *  @property {object} [sortBy]            SortBy string for the API (usually 'attribute:desc')
+ *  @property {object} [sortBy]            SortBy string for the API (usually 'attribute=desc')
  *
  */
 
@@ -25,6 +25,7 @@ import useFetchTotalBatched from '../Utilities/Hooks/useFetchTotalBatched';
  *  @param   {object}                   [options]               Options for useRemediationsQuery & useQuery
  *  @param   {useRemediationQueryParams} [options.params]        API endpoint params
  *  @param   {boolean}                  [options.useTableState] Use the serialised table state
+ *  @param   {boolean}                  [options.onlyTotal]      Enables a predefined "compileResult" function for the useQuery to only return the meta.total as the `data`
  *
  *  @returns {useQueryReturn}                                   An object containing a data, loading and error state, as well as a fetch and refetch function.
  *
@@ -53,17 +54,15 @@ const useRemediationsQuery = (
   const skip = !!(useTableState && !hasState) || !!skipOption;
 
   const {
-    data: queryData,
+    result: queryData,
     error: queryError,
     loading: queryLoading,
     fetch: queryFetch,
-    meta: metaData,
   } = useQuery(endpoint, {
     skip: batched ? true : skip,
     ...options,
     params,
   });
-
   const fetchForBatch = useCallback(
     async (offset, limit, params) =>
       await queryFetch({ limit, offset, ...params }, false),
@@ -72,7 +71,7 @@ const useRemediationsQuery = (
 
   const {
     loading: batchedLoading,
-    data: batchedData,
+    result: batchedData,
     error: batchedError,
     fetch: batchedFetch,
   } = useFetchTotalBatched(fetchForBatch, {
@@ -84,19 +83,17 @@ const useRemediationsQuery = (
 
   const fetchAllIds = async () =>
     (await batchedFetch()).data.map(({ id }) => id);
-
   return {
     ...(batched
       ? {
-          data: batchedData,
+          result: batchedData,
           error: batchedError,
           loading: batchedLoading,
         }
       : {
-          data: queryData,
+          result: queryData,
           error: queryError,
           loading: queryLoading,
-          meta: metaData,
         }),
     fetch: queryFetch,
     fetchBatched: batchedFetch,
diff --git a/src/components/RemediationsTable/RemediationsTable.js b/src/components/RemediationsTable/RemediationsTable.js
index 5c2a8142..4cdbeaf1 100644
--- a/src/components/RemediationsTable/RemediationsTable.js
+++ b/src/components/RemediationsTable/RemediationsTable.js
@@ -1,9 +1,5 @@
 import React from 'react';
-import {
-  filtersSerialiser,
-  paginationSerialiser,
-  sortSerialiser,
-} from './serealisers';
+import { filtersSerialiser, sortSerialiser } from './serealisers';
 import propTypes from 'prop-types';
 import AsyncTableToolsTable from '../../Frameworks/AsyncTableTools/AsyncTableTools/components/AsyncTableToolsTable';
 
@@ -24,7 +20,6 @@ const RemediationsTable = (props) => {
       {...props}
       options={{
         serialisers: {
-          pagination: paginationSerialiser,
           filters: filtersSerialiser,
           sort: sortSerialiser,
         },
diff --git a/src/components/RemediationsTable/serealisers.js b/src/components/RemediationsTable/serealisers.js
index 1203858c..9ebd6c2c 100644
--- a/src/components/RemediationsTable/serealisers.js
+++ b/src/components/RemediationsTable/serealisers.js
@@ -1,28 +1,7 @@
-// TODO correct the serialiser to transform state put into the tablestate to be API consumable
-export const paginationSerialiser = (state) => {
-  if (state) {
-    const offset = (state.page - 1) * state.perPage;
-    const limit = state.perPage;
-
-    return { offset, limit };
-  }
-};
-
-const textFilterSerialiser = (filterConfigItem, value) =>
-  `${filterConfigItem.filterAttribute} ~ "${value}"`;
-
-const checkboxFilterSerialiser = (filterConfigItem, values) =>
-  `${filterConfigItem.filterAttribute} ^ (${values
-    .map((value) => `${value}`)
-    .join(' ')})`;
-
-const raidoFilterSerialiser = (filterConfigItem, values) =>
-  `${filterConfigItem.filterAttribute} = "${values[0]}"`;
-
 const filterSerialisers = {
-  text: textFilterSerialiser,
-  checkbox: checkboxFilterSerialiser,
-  radio: raidoFilterSerialiser,
+  text: (_config, [value]) => value,
+  radio: (_config, [value]) => value,
+  checkbox: (_config, values) => values.join(','),
 };
 
 const findFilterSerialiser = (filterConfigItem) => {
@@ -48,25 +27,22 @@ const findFilterSerialiser = (filterConfigItem) => {
  *  @tutorial filter-serialiser
  *
  */
-export const filtersSerialiser = (state, filters) => {
-  const queryParts = Object.entries(state || {}).reduce(
-    (filterQueryParts, [filterId, value]) => {
-      const filterConfigItem = filters.find((filter) => filter.id === filterId);
-      const filterSerialiser = findFilterSerialiser(filterConfigItem);
-
-      return [
-        ...filterQueryParts,
-        ...(filterSerialiser
-          ? [filterSerialiser(filterConfigItem, value)]
-          : []),
-      ];
-    },
-    []
-  );
-
-  return queryParts.length > 0 ? queryParts.join(' AND ') : undefined;
-};
+export const filtersSerialiser = (state, filters) =>
+  Object.entries(state || {}).reduce((allFilters, [filterId, value]) => {
+    const filterConfigItem = filters.find((filter) => filter.id === filterId);
+    const filterSerialiser = findFilterSerialiser(filterConfigItem);
+    const serialiseValue = filterSerialiser
+      ? filterSerialiser(filterConfigItem, value)
+      : value;
+    console.log(filterConfigItem);
 
+    const filterParams = {
+      ...allFilters,
+      [`filter[${filterConfigItem.filterAttribute}]`]: serialiseValue,
+    };
+    console.log(filterParams);
+    return filterParams;
+  }, {});
 /**
  * Returns a string consumable by the Remediations API as a "sort_by" parameter for a given column and direction
  * For columns to be sortable they need to have a "sortable" prop, which corresponds to the field name in the Remediations API
@@ -90,5 +66,9 @@ export const filtersSerialiser = (state, filters) => {
  *  ];
  *
  */
-export const sortSerialiser = ({ index, direction } = {}, columns) =>
-  columns[index]?.sortable && `${columns[index].sortable}:${direction}`; // TODO: columns and index are not matching if the first column serves for expandable button
+export const sortSerialiser = ({ index, direction } = {}, columns) => {
+  return (
+    columns[index]?.sortable &&
+    `${direction === 'desc' ? '-' : ''}${columns[index].sortable}`
+  );
+};
diff --git a/src/routes/Columns.js b/src/routes/Columns.js
index c19e627e..9bb2382b 100644
--- a/src/routes/Columns.js
+++ b/src/routes/Columns.js
@@ -8,6 +8,7 @@ import {
   CreatedCell,
   LastModifiedCell,
 } from './Cells.js';
+import { fitContent } from '@patternfly/react-table';
 
 // eslint-disable-next-line react/display-name
 export const renderComponent = (Component, props) => (_data, _id, entity) =>
@@ -15,6 +16,7 @@ export const renderComponent = (Component, props) => (_data, _id, entity) =>
 
 export const Name = {
   title: 'Name',
+  transforms: [fitContent],
   sortable: 'name',
   props: {
     width: 10,
@@ -25,27 +27,30 @@ export const Name = {
 
 export const LastExecuted = {
   title: 'Last executed',
-  sortable: 'LastExecuted',
+  transforms: [fitContent],
+  sortable: 'last_run_at',
   props: {
     width: 30,
   },
-  exportKey: 'LastExecuted',
+  exportKey: 'last_run_at',
   renderFunc: renderComponent(LastExecutedCell),
 };
 
 export const ExecutionStatus = {
   title: 'Execution status',
-  sortable: 'ExecutionStatus',
+  transforms: [fitContent],
+  sortable: 'status',
   props: {
     width: 30,
   },
-  exportKey: 'ExecutionStatus',
+  exportKey: 'status',
   renderFunc: renderComponent(ExecutionStatusCell),
 };
 
 export const Actions = {
   title: 'Actions',
-  sortable: 'Actions',
+  transforms: [fitContent],
+  // sortable: 'Actions',
   props: {
     width: 30,
   },
@@ -55,31 +60,34 @@ export const Actions = {
 
 export const Systems = {
   title: 'Systems',
-  sortable: 'Systems',
+  transforms: [fitContent],
+  sortable: 'system_count',
   props: {
     width: 30,
   },
-  exportKey: 'Systems',
+  exportKey: 'system_count',
   renderFunc: renderComponent(SystemsCell),
 };
 
 export const Created = {
   title: 'Created',
-  sortable: 'Created',
+  transforms: [fitContent],
+  sortable: 'created_at',
   props: {
     width: 30,
   },
-  exportKey: 'Created',
+  exportKey: 'created_at',
   renderFunc: renderComponent(CreatedCell),
 };
 
 export const LastModified = {
   title: 'Last modified',
-  sortable: 'LastModified',
+  transforms: [fitContent],
+  sortable: 'updated_at',
   props: {
     width: 30,
   },
-  exportKey: 'LastModified',
+  exportKey: 'updated_at',
   renderFunc: renderComponent(LastModifiedCell),
 };
 
diff --git a/src/routes/Filters.js b/src/routes/Filters.js
index 84bf8a12..973d605b 100644
--- a/src/routes/Filters.js
+++ b/src/routes/Filters.js
@@ -4,7 +4,7 @@ export const remediationNameFilter = [
   {
     type: conditionalFilterType.text,
     label: 'Remediation',
-    filterAttribute: 'title',
+    filterAttribute: 'name',
   },
 ];
 
@@ -12,15 +12,18 @@ export const LastExecutedFilter = [
   {
     type: conditionalFilterType.text,
     label: 'Last Executed',
-    filterAttribute: 'LastExecuted',
+    filterAttribute: 'last_run_after',
   },
 ];
 
 export const ExecutionStatusFilter = [
   {
-    type: conditionalFilterType.text,
+    type: conditionalFilterType.checkbox,
     label: 'Execution Status',
-    filterAttribute: 'LastExecuted',
+    filterAttribute: 'status',
+    items: ['running', 'success', 'failure', 'timeout', 'cancelled'].map(
+      (value) => ({ label: value, value })
+    ),
   },
 ];
 
diff --git a/src/routes/OverViewPage.js b/src/routes/OverViewPage.js
index d253df0d..fd763e51 100644
--- a/src/routes/OverViewPage.js
+++ b/src/routes/OverViewPage.js
@@ -23,7 +23,7 @@ const getRemediations = (axios) => (params) => {
 export const OverViewPage = () => {
   const axios = useAxiosWithPlatformInterceptors();
   const [selectedItems, setSelectedItems] = useState([]);
-  const { data, meta, fetchAllIds } = useRemediationsQuery(
+  const { result, /*loading, error,*/ fetchAllIds } = useRemediationsQuery(
     getRemediations(axios),
     {
       useTableState: true,
@@ -35,8 +35,8 @@ export const OverViewPage = () => {
 
   return (
     <RemediationsTable
-      items={data}
-      total={meta?.total}
+      items={result?.data}
+      total={result?.meta?.total}
       columns={[...columns]}
       filters={{
         filterConfig: [
@@ -51,12 +51,15 @@ export const OverViewPage = () => {
       }}
       selectedItems={selectedItems}
       options={{
+        sortBy: {
+          index: 6,
+          direction: 'desc',
+        },
         itemIdsInTable: fetchAllIds,
         manageColumns: true,
         onSelect: handleSelectionChange,
-        itemIdsOnPage: data,
-        identifier: 'id',
-        preselected: data && data[0]?.id,
+        itemIdsOnPage: result?.data.map(({ id }) => id),
+        total: result?.meta?.total,
       }}
     />
   );


Reviews:
user: login: bastilian
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T10:12:01Z
user: login: bastilian
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T00:03:40Z
user: login: bastilian
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T00:05:55Z
user: login: bastilian
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T00:06:01Z
user: login: bastilian
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T00:10:16Z
user: login: bastilian
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T00:25:16Z
user: login: adonispuente
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T00:25:59Z
user: login: adonispuente
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T00:26:38Z
user: login: bastilian
body: LGTM! Thank you @adonispuente!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-21T11:13:28Z

Review comments:
diff_hunk: @@ -34,7 +34,7 @@ import { TABLE_STATE_NAMESPACE } from './constants';
  *
  */
 const useTableSort = (columns, options = {}) => {
-  const { sortBy: initialSortBy, serialisers, onSort: onSortOption } = options;
+  const { filter: initialSortBy, serialisers, onSort: onSortOption } = options;
path: src/Frameworks/AsyncTableTools/AsyncTableTools/hooks/useTableSort/useTableSort.js
user: login: bastilian
body: let's try and not do that. :D 
created_at: 2025-03-17T10:12:01Z
author_association: MEMBER
diff_hunk: @@ -97,6 +98,7 @@ const useRemediationsQuery = (
           error: queryError,
           loading: queryLoading,
           meta: metaData,
+          availableIDs: availableIDs,
path: src/api/useRemediationsQuery.js
user: login: bastilian
body: ~~Line 85 is what you are looking for. ;) The `fetchAllIds` function can be passed directly to the Table options as `itemIdsInTable`.~~
created_at: 2025-03-18T00:03:40Z
author_association: MEMBER
diff_hunk: @@ -51,12 +51,15 @@ export const OverViewPage = () => {
       }}
       selectedItems={selectedItems}
       options={{
+        sortBy: {
+          index: 6,
+          direction: 'desc',
+        },
         itemIdsInTable: fetchAllIds,
         manageColumns: true,
         onSelect: handleSelectionChange,
-        itemIdsOnPage: data,
-        identifier: 'id',
-        preselected: data && data[0]?.id,
+        itemIdsOnPage: availableIDs,
path: src/routes/OverViewPage.js
user: login: bastilian
body: Just do `itemIdsOnPage: data?.map(({id}) => id)`. No `availableIDs` thingy needed. :) 
created_at: 2025-03-18T00:05:55Z
author_association: MEMBER
diff_hunk: @@ -97,6 +98,7 @@ const useRemediationsQuery = (
           error: queryError,
           loading: queryLoading,
           meta: metaData,
+          availableIDs: availableIDs,
path: src/api/useRemediationsQuery.js
user: login: bastilian
body: oh, now I get what you want. see below. 
created_at: 2025-03-18T00:06:01Z
author_association: MEMBER
diff_hunk: @@ -45,11 +45,9 @@ const useQuery = (fn, options = {}) => {
   const [meta, setMeta] = useState(undefined);
   const [error, setError] = useState(undefined);
   const [loading, setLoading] = useState(false);
-  console.log(params, 'params outside useQuery here');
-
+  const [availableIDs, setAvailableIDs] = useState([]);
path: src/Utilities/Hooks/useQuery.js
user: login: bastilian
body: Remember what I said earlier today. The `useQuery` should only be concerned with calling a function and returning the results either as a state or directly.

It is not meant to alter, separate out or do anything with the result.
It shouldn't know about `meta` either. These are things that if anything the `useRemediationsQuery` should be responsible for. 
created_at: 2025-03-18T00:10:15Z
author_association: MEMBER
diff_hunk: @@ -56,7 +56,7 @@ const useTableSort = (columns, options = {}) => {
   const [sortBy, setSortBy] = useTableState(
     TABLE_STATE_NAMESPACE,
     initialSortBy || {
-      index: 0,
+      index: 1,
path: src/Frameworks/AsyncTableTools/AsyncTableTools/hooks/useTableSort/useTableSort.js
user: login: bastilian
body: I think this can be discarded. I think I typed `1`, because I forgot for a second indexes start with 0...
created_at: 2025-03-18T00:25:16Z
author_association: MEMBER
diff_hunk: @@ -45,11 +45,9 @@ const useQuery = (fn, options = {}) => {
   const [meta, setMeta] = useState(undefined);
   const [error, setError] = useState(undefined);
   const [loading, setLoading] = useState(false);
-  console.log(params, 'params outside useQuery here');
-
+  const [availableIDs, setAvailableIDs] = useState([]);
path: src/Utilities/Hooks/useQuery.js
user: login: adonispuente
body: Ahh youre right, I was about to change it to also just bring back data, instead of data.data, and then use meta with data.meta, but im gonna go ahead with the compileResult thing instead
created_at: 2025-03-18T00:25:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -56,7 +56,7 @@ const useTableSort = (columns, options = {}) => {
   const [sortBy, setSortBy] = useTableState(
     TABLE_STATE_NAMESPACE,
     initialSortBy || {
-      index: 0,
+      index: 1,
path: src/Frameworks/AsyncTableTools/AsyncTableTools/hooks/useTableSort/useTableSort.js
user: login: adonispuente
body: I'll slap this up too
created_at: 2025-03-18T00:26:38Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov-commenter
created_at: 2025-03-17T23:39:22Z
body: ## [Codecov](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) Report
:x: Patch coverage is `0%` with `53 lines` in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 46.97%. Comparing base ([`db1a31c`](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/commit/db1a31cda30715cc39b475a111707110c6df83d3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights)) to head ([`4f04080`](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/commit/4f04080ca64574e661958af9f450c223e9589fba?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights)).
:warning: Report is 204 commits behind head on master.

| [Files with missing lines](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) | Patch % | Lines |
|---|---|---|
| [src/components/RemediationsTable/serealisers.js](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&filepath=src%2Fcomponents%2FRemediationsTable%2Fserealisers.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights#diff-c3JjL2NvbXBvbmVudHMvUmVtZWRpYXRpb25zVGFibGUvc2VyZWFsaXNlcnMuanM=) | 0.00% | [18 Missing :warning: ](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) |
| [src/Utilities/Hooks/api/helpers.js](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&filepath=src%2FUtilities%2FHooks%2Fapi%2Fhelpers.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights#diff-c3JjL1V0aWxpdGllcy9Ib29rcy9hcGkvaGVscGVycy5qcw==) | 0.00% | [16 Missing :warning: ](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) |
| [src/Utilities/Hooks/useQuery.js](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&filepath=src%2FUtilities%2FHooks%2FuseQuery.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights#diff-c3JjL1V0aWxpdGllcy9Ib29rcy91c2VRdWVyeS5qcw==) | 0.00% | [15 Missing :warning: ](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) |
| [src/routes/OverViewPage.js](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&filepath=src%2Froutes%2FOverViewPage.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights#diff-c3JjL3JvdXRlcy9PdmVyVmlld1BhZ2UuanM=) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) |
| [src/api/useRemediationTableState.js](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&filepath=src%2Fapi%2FuseRemediationTableState.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights#diff-c3JjL2FwaS91c2VSZW1lZGlhdGlvblRhYmxlU3RhdGUuanM=) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) |
| [src/routes/Filters.js](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&filepath=src%2Froutes%2FFilters.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights#diff-c3JjL3JvdXRlcy9GaWx0ZXJzLmpz) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) |

<details><summary>Additional details and impacted files</summary>



```diff
@@            Coverage Diff             @@
##           master     #480      +/-   ##
==========================================
+ Coverage   46.81%   46.97%   +0.16%     
==========================================
  Files         169      170       +1     
  Lines        3779     3766      -13     
  Branches     1123     1114       -9     
==========================================
  Hits         1769     1769              
+ Misses       2010     1997      -13     
```

| [Flag](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) | Coverage Δ | |
|---|---|---|
| [combined](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) | `46.97% <0.00%> (+0.16%)` | :arrow_up: |
| [cypress](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) | `100.00% <ø> (ø)` | |
| [jest](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights) | `46.76% <0.00%> (+0.16%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights#carryforward-flags-in-the-pull-request-comment) to find out more.
</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/RedHatInsights/insights-remediations-frontend/pull/480?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RedHatInsights).
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: bastilian
created_at: 2025-03-18T00:19:44Z
body: @adonispuente I'd recommend to grab the `useQuery` & `useFetchTotalBatched` from compliance (once this PR: https://github.com/RedHatInsights/compliance-frontend/pull/2433 is merged, or grab it from there) and let's try to use the `compileResult` feature to get a consistent format or the responses results. You probably can just grab the `compileResults` function from compliance as well and it should theoretically work. 

You can then have your api hook calls be like this

```js 
const { result, loading, error } = useRemediationsQuery(
```

with the `result` being `{ data: [...], meta: {} }`. always. 


author_association: MEMBER
user: login: adonispuente
created_at: 2025-03-18T01:40:04Z
body: @bastilian Thanks for all the feedback, I just sent up all the improvements!
author_association: CONTRIBUTOR