diff --git a/src/Components/SmartComponents/CVEDetailsPage/CVEDetailsPage.js b/src/Components/SmartComponents/CVEDetailsPage/CVEDetailsPage.js
index 0888541fe..5c82b7938 100644
--- a/src/Components/SmartComponents/CVEDetailsPage/CVEDetailsPage.js
+++ b/src/Components/SmartComponents/CVEDetailsPage/CVEDetailsPage.js
@@ -53,10 +53,13 @@ const CVEDetailsPage = () => {
dispatch(fetchCveDetails(cveName));
}, [dispatch, cveName]);
- const refreshInventory = () => (
- dispatch(clearInventoryStore()),
- inventoryRef.current.onRefreshData(({ page: 1 }))
- );
+ const refreshInventory = () => {
+ dispatch(clearInventoryStore());
+
+ // timestamp is used to force inventory to refresh
+ // if it wasn't there inventory might ignore request to refresh because parameters are the same
+ inventoryRef.current.onRefreshData(({ timestamp: Date.now(), page: 1 }));
+ };
const showBusinessRiskModal = cves => {
setBusinessRisk(
diff --git a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTable.js b/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTable.js
index 02a28a175..53352dad1 100644
--- a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTable.js
+++ b/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTable.js
@@ -17,9 +17,16 @@ import {
changeExposedSystemsParameters,
clearInventoryStore,
selectRows,
- changeColumnsCveDetail
+ changeColumnsCveDetail,
+ fetchAffectedSystemIdsByCve
} from '../../../Store/Actions/Actions';
-import { SYSTEMS_EXPOSED_HEADER, SYSTEMS_EXPOSED_ALLOWED_PARAMS, PERMISSIONS } from '../../../Helpers/constants';
+import {
+ SYSTEMS_EXPOSED_HEADER,
+ SYSTEMS_EXPOSED_ALLOWED_PARAMS,
+ PERMISSIONS,
+ ANSIBLE_REMEDIATION,
+ RULE_PRESENCE_OPTIONS
+} from '../../../Helpers/constants';
import { TableVariant } from '@patternfly/react-table';
import { InventoryTable } from '@redhat-cloud-services/frontend-components/Inventory';
import ErrorHandler from '../../PresentationalComponents/ErrorHandler/ErrorHandler';
@@ -28,18 +35,22 @@ import {
clearNotifications
} from '@redhat-cloud-services/frontend-components-notifications/redux';
import { EmptyStateNoSystems } from '../../PresentationalComponents/EmptyStates/EmptyStates';
-import { SystemsExposedTableToolbar } from './SystemsExposedTableToolbar';
-import { useColumnManagement, useGetEntities, useRbac } from '../../../Helpers/Hooks';
+import { useBulkSelect, useColumnManagement, useGetEntities, useRbac } from '../../../Helpers/Hooks';
import * as APIHelper from '../../../Helpers/APIHelper';
import Spinner from '@redhat-cloud-services/frontend-components/Spinner';
+import { buildActiveFilters, exportConfig, removeFilters } from '../../../Helpers/TableToolbarHelper';
+import Remediation from '../Remediation/Remediation';
+import securityRuleFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/SecurityRuleFilter';
+import statusFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/StatusFilter';
+import useSearchFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/SearchFilter';
+import useOsVersionFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter';
+import remediationFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/RemediationFilter';
const SystemsExposedTable = ({
intl, cveName, cveStatusDetails, filterRuleValues,
hasSecurityRule, canEditPairStatus, canExport, inventoryRef, refreshInventory
}) => {
- const [[
- canReadHostsInventory],
- isLoadingInventory] = useRbac([
+ const [[canReadHostsInventory], isLoadingInventory] = useRbac([
PERMISSIONS.readHosts
], 'inventory');
@@ -130,7 +141,66 @@ const SystemsExposedTable = ({
.map(column => ({ ...inventoryColumns.find(({ key }) => column.key === key), ...column }));
};
- // TODO: let InventoryTable render its own toolbar instead of using custom one
+ const rawData = { data: items, meta: { totalItems }, isLoaded };
+
+ const remediableSystems = selectedRows.filter(system => system.remediation === ANSIBLE_REMEDIATION);
+
+ const kebabOptions = [
+ ...isLoaded ? [
+
+ ] : [],
+ ...canEditPairStatus ? [{
+ label: intl.formatMessage(messages.editStatus),
+ onClick: () => showStatusModal([cveStatusDetails], selectedRows),
+ props: { isDisabled: !selectedRowsCount }
+ }] : [],
+ {
+ label: intl.formatMessage(messages.columnManagementModalTitle),
+ onClick: () => setColumnManagementModalOpen(true)
+ }
+ ];
+
+ const searchFilter = useSearchFilter(
+ 'filter',
+ messages.systemsSearchName,
+ messages.searchFilterByName,
+ parameters.filter,
+ apply
+ );
+
+ const advisoryFilter = useSearchFilter(
+ 'advisory',
+ messages.advisoryName,
+ messages.searchFilterByAdvisoryName,
+ parameters.advisory,
+ apply
+ );
+
+ const osVersionFilter = useOsVersionFilter(
+ parameters.rhel_version,
+ apply
+ );
+
+ const bulkSelectProps = useBulkSelect({
+ rawData,
+ selectedRows,
+ selectedRowsCount,
+ handleSelect,
+ fetchResource: ops => fetchAffectedSystemIdsByCve({ id: cveName, ...parameters, ...ops })
+ });
+
+ if (isAllExpanded) {
+ if (rawData.data.filter(row => row.isOpen === undefined).length > 0) {
+ expandAll(rawData, false);
+ }
+ }
+
return (
@@ -141,9 +211,12 @@ const SystemsExposedTable = ({
- {isLoadingInventory ? :
+ {StatusModal && }
+ {ColumnManagementModal}
+
+ {isLoadingInventory ? :
error?.hasError && !canReadHostsInventory
- ?
+ ?
: {
@@ -191,34 +264,47 @@ const SystemsExposedTable = ({
showActions={totalItems !== 0}
onExpandClick={(_e, _i, isOpen, { id }) => dispatch(expandRow(id, isOpen))}
hideFilters={{ all: true }}
- noSystemsTable={}
- >
-
- {StatusModal && }
-
- { ColumnManagementModal }
-
- }
+ noSystemsTable={}
+ expandAll={hasSecurityRule ? {
+ isAllExpanded,
+ onClick: () => expandAll(rawData, isAllExpanded),
+ isDisabled: totalItems === 0
+ } : undefined}
+ actionsConfig={{
+ actions: kebabOptions,
+ dropdownProps: { ouiaId: 'toolbar-actions' }
+ }}
+ exportConfig={canExport && {
+ isDisabled: totalItems === 0,
+ ouiaId: 'export',
+ ...exportConfig({ downloadReport })
+ }}
+ activeFiltersConfig={{
+ filters: buildActiveFilters({ ...parameters }, filterRuleValues),
+ onDelete: (_, chips) => removeFilters(chips, apply),
+ deleteTitle: intl.formatMessage(messages.resetFilters)
+ }}
+ bulkSelect={bulkSelectProps}
+ filterConfig={{
+ items: [
+ searchFilter,
+ securityRuleFilter(
+ apply,
+ parameters,
+ filterRuleValues,
+ {
+ isDynamic: true,
+ dropdownItems: RULE_PRESENCE_OPTIONS.filter(item => item.value !== 'true')
+ }
+ ),
+ statusFilter(apply, parameters),
+ advisoryFilter,
+ ...osVersionFilter,
+ remediationFilter(apply, parameters)
+ ]
+ }}
+ />
+ }
);
diff --git a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.js b/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.js
deleted file mode 100644
index 948527af1..000000000
--- a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.js
+++ /dev/null
@@ -1,166 +0,0 @@
-import React, { Fragment } from 'react';
-import propTypes from 'prop-types';
-import { useIntl } from 'react-intl';
-import { PrimaryToolbar } from '@redhat-cloud-services/frontend-components/PrimaryToolbar';
-import Remediation from '../Remediation/Remediation';
-import messages from '../../../Messages';
-import securityRuleFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/SecurityRuleFilter';
-import statusFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/StatusFilter';
-import useOsVersionFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter';
-import { exportConfig, buildActiveFilters, removeFilters } from '../../../Helpers/TableToolbarHelper';
-import { dataShape } from '../../../Helpers/MiscHelper';
-import { useBulkSelect } from '../../../Helpers/Hooks';
-import useSearchFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/SearchFilter';
-import {
- fetchAffectedSystemIdsByCve
-} from '../../../Store/Actions/Actions';
-import {
- ANSIBLE_REMEDIATION, RULE_PRESENCE_OPTIONS
-} from '../../../Helpers/constants';
-import remediationFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/RemediationFilter';
-
-export const SystemsExposedTableToolbar = ({
- rawData,
- cveName,
- cveStatusDetails,
- selectedRows,
- selectedRowsCount,
- filterRuleValues,
- expandAll,
- hasSecurityRule,
- isAllExpanded,
- parameters,
- methods,
- canEditPairStatus,
- canExport,
- children
-}) => {
- const intl = useIntl();
- const { apply, showStatusModal, handleSelect, downloadReport, setColumnManagementModalOpen } = methods;
- const { isLoaded, meta } = rawData;
-
- const remediableSystems = selectedRows.filter(system => system.remediation === ANSIBLE_REMEDIATION);
-
- const kebabOptions = ['',
- ...canEditPairStatus ? [{
- label: intl.formatMessage(messages.editStatus),
- onClick: () => showStatusModal([cveStatusDetails], selectedRows),
- props: { isDisabled: !selectedRowsCount }
- }] : [],
- {
- label: intl.formatMessage(messages.columnManagementModalTitle),
- onClick: () => setColumnManagementModalOpen(true)
- }
- ];
-
- const searchFilter = useSearchFilter(
- 'filter',
- messages.systemsSearchName,
- messages.searchFilterByName,
- parameters.filter,
- apply
- );
-
- const advisoryFilter = useSearchFilter(
- 'advisory',
- messages.advisoryName,
- messages.searchFilterByAdvisoryName,
- parameters.advisory,
- apply
- );
-
- const osVersionFilter = useOsVersionFilter(
- parameters.rhel_version,
- apply
- );
-
- const bulkSelectProps = useBulkSelect({
- rawData,
- selectedRows,
- selectedRowsCount,
- handleSelect,
- fetchResource: ops => fetchAffectedSystemIdsByCve({ id: cveName, ...parameters, ...ops })
- });
-
- if (isAllExpanded) {
- if (rawData.data.filter(row => row.isOpen === undefined).length > 0) {
- expandAll(rawData, false);
- }
- }
-
- return
-
- )}
- actionsConfig={{
- actions: kebabOptions,
- dropdownProps: { ouiaId: 'toolbar-actions' }
- }}
- activeFiltersConfig={{
- filters: buildActiveFilters({ ...parameters }, filterRuleValues),
- onDelete: (_, chips) => removeFilters(chips, apply),
- deleteTitle: intl.formatMessage(messages.resetFilters)
- }}
- bulkSelect={bulkSelectProps}
- filterConfig={{
- items: [
- searchFilter,
- securityRuleFilter(
- apply,
- parameters,
- filterRuleValues,
- {
- isDynamic: true,
- dropdownItems: RULE_PRESENCE_OPTIONS.filter(item => item.value !== 'true')
- }
- ),
- statusFilter(apply, parameters),
- advisoryFilter,
- ...osVersionFilter,
- remediationFilter(apply, parameters)
- ]
- }}
- expandAll={hasSecurityRule ? {
- isAllExpanded,
- onClick: () => expandAll(rawData, isAllExpanded),
- isDisabled: meta.totalItems === 0
- } : undefined}
- />
- {children}
- ;
-};
-
-SystemsExposedTableToolbar.propTypes = {
- rawData: dataShape,
- parameters: propTypes.object,
- cveName: propTypes.string,
- cveStatusDetails: propTypes.object,
- filterRuleValues: propTypes.array,
- expandAll: propTypes.func,
- hasSecurityRule: propTypes.bool,
- isAllExpanded: propTypes.bool,
- selectedRows: propTypes.array,
- selectedRowsCount: propTypes.number,
- children: propTypes.node,
- methods: propTypes.shape({
- apply: propTypes.func,
- handleSelect: propTypes.func,
- showStatusModal: propTypes.func,
- downloadReport: propTypes.func,
- setColumnManagementModalOpen: propTypes.func
- }),
- canEditPairStatus: propTypes.bool,
- canExport: propTypes.bool
-};
diff --git a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.test.js b/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.test.js
deleted file mode 100644
index 739ba27ea..000000000
--- a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.test.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { Provider } from "react-redux";
-import { BrowserRouter as Router } from "react-router-dom";
-import configureStore from "redux-mock-store";
-
-import { mountWithIntl } from "../../../Helpers/MiscHelper";
-import { SystemsExposedTableToolbar } from "./SystemsExposedTableToolbar";
-import useOsVersionFilter from "../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter";
-import toJson from "enzyme-to-json";
-import { systems } from "../../../Helpers/fixtures";
-
-const mockStore = configureStore([(store) => (next) => (action) => {}]);
-
-let store = mockStore({});
-
-const applyMock = jest.fn();
-const handleSelectMock = jest.fn();
-const doOptOutMock = jest.fn();
-
-jest.mock(
- "../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter"
-);
-
-let wrapper;
-
-describe("SystemsExpostedTableToolbar component", () => {
- beforeEach(() => {
- wrapper = mountWithIntl(
-
-
-
-
-
- );
- });
-
- it("Renders toolbar", () => {
- expect(
- wrapper.find({
- ouiaId: "PrimaryToolbar",
- })
- ).toHaveLength(1);
- });
-
- it("Toolbar matches snapshot", () => {
- expect(toJson(wrapper.find('Toolbar'))).toMatchSnapshot();
- });
-
- it("The default filter is set by name", () => {
- const container = wrapper.find('button[data-ouia-component-id="ConditionalFilter"]');
- expect(container).toHaveLength(1);
- expect(container.text()).toBe('Name');
- })
-});
diff --git a/src/Components/SmartComponents/SystemsExposedTable/__snapshots__/SystemsExposedTableToolbar.test.js.snap b/src/Components/SmartComponents/SystemsExposedTable/__snapshots__/SystemsExposedTableToolbar.test.js.snap
deleted file mode 100644
index 27cb6105e..000000000
--- a/src/Components/SmartComponents/SystemsExposedTable/__snapshots__/SystemsExposedTableToolbar.test.js.snap
+++ /dev/null
@@ -1,1974 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`SystemsExpostedTableToolbar component Toolbar matches snapshot 1`] = `
-
-
-
-
-
-
-
-
-
-
-
- Export to CSV
- ,
-
- Export to JSON
- ,
- ]
- }
- isOpen={false}
- isPlain={true}
- onSelect={[Function]}
- ouiaId="Export"
- toggle={
-
-
-
- }
- >
-
- Export to CSV
- ,
-
- Export to JSON
- ,
- ]
- }
- isFlipEnabled={true}
- isGrouped={false}
- isOpen={false}
- isPlain={true}
- isText={false}
- menuAppendTo="inline"
- onSelect={[Function]}
- position="left"
- removeFindDomNode={false}
- toggle={
-
-
-
- }
- zIndex={9999}
- >
- ,
- }
- }
- toggleIndicator={null}
- >
-
-
- ,
- }
- }
- toggleVariant="default"
- >
-
-
-
-
-
-
-
-
-
-
-
-
- Manage columns
- ,
- ]
- }
- isOpen={false}
- isPlain={true}
- onSelect={[Function]}
- ouiaId="Actions"
- toggle={
-
- }
- >
-
- Manage columns
- ,
- ]
- }
- isFlipEnabled={true}
- isGrouped={false}
- isOpen={false}
- isPlain={true}
- isText={false}
- menuAppendTo="inline"
- onSelect={[Function]}
- position="left"
- removeFindDomNode={false}
- toggle={
-
- }
- zIndex={9999}
- >
- ,
- }
- }
- >
-
-
- ,
- }
- }
- >
-
-
-
-
-
-
-
-
-
-
-
-
- ,
- }
- }
- id="ins-primary-data-toolbar-expandable-content-6"
- isExpanded={false}
- showClearFiltersButton={false}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Systems with Vulnerability analysis enabled
-
-
- -
-
-
-
-
- Enabled systems
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Reset filters
-
-
-
-
-
-
-
-
-
-
- ,
- }
- }
- id="ins-primary-data-toolbar-expandable-content-7"
- isExpanded={false}
- showClearFiltersButton={false}
- >
-
-
-
-
-
-
- ,
- }
- }
- clearFiltersButtonText="Clear all filters"
- collapseListedFiltersBreakpoint="lg"
- numberOfFilters={0}
- numberOfFiltersText={[Function]}
- showClearFiltersButton={false}
- >
-
-
-
-
-`;
diff --git a/src/Components/SmartComponents/SystemsPage/SystemTableToolbar.test.js b/src/Components/SmartComponents/SystemsPage/SystemTableToolbar.test.js
deleted file mode 100644
index 277160006..000000000
--- a/src/Components/SmartComponents/SystemsPage/SystemTableToolbar.test.js
+++ /dev/null
@@ -1,182 +0,0 @@
-import SystemsTableToolbar from './SystemsTableToolbar';
-import toJson from 'enzyme-to-json';
-import { mountWithIntl } from '../../../Helpers/MiscHelper';
-import { BrowserRouter as Router } from 'react-router-dom';
-import configureStore from 'redux-mock-store';
-import { Provider } from 'react-redux';
-import { intl } from '../../../Utilities/IntlProvider';
-import useOsVersionFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter';
-import { systems } from '../../../Helpers/fixtures';
-
-const mockStore = configureStore([store => next => action => { }]);
-let store = mockStore({});
-
-const applyMock = jest.fn();
-const handleSelectMock = jest.fn();
-const doOptOutMock = jest.fn();
-
-jest.mock('../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter')
-
-let wrapper;
-
-beforeEach(() => {
- wrapper = mountWithIntl(
-
-
-
-
-
- );
-});
-
-describe('SystemsTableToolbar component', () => {
- it("Renders toolbar", () => {
- expect(
- wrapper.find({
- ouiaId: "PrimaryToolbar",
- })
- ).toHaveLength(1);
- });
-
- it('Should match snapshot', () => {
- expect(toJson(wrapper.find('Toolbar'))).toMatchSnapshot();
- });
-
- it('Select all systems', () => {
- const selectAllToggle = wrapper.find('DropdownToggle button').first();
- selectAllToggle.simulate('click');
-
- wrapper.find('Dropdown').first().find("button").at(2).simulate('click');
- const bulkSelectedSystems = systems.data.map((system) => {
- return {
- display_name: system.display_name,
- id: system.id,
- opt_out: system.attributes.opt_out,
- culled_timestamp: system.culled_timestamp,
- cve_count: system.cve_count,
- insights_id: system.insights_id,
- last_evaluation: system.last_evaluation,
- last_upload: system.last_upload,
- os: system.os,
- rules_evaluation: system.rules_evaluation,
- stale_timestamp: system.stale_timestamp,
- stale_warning_timestamp: system.stale_warning_timestamp,
- tags: system.tags,
- type: system.type,
- updated: system.updated,
- remediation: system.remediation,
- selected: true
- };
- });
-
- expect(handleSelectMock).toHaveBeenCalledWith(bulkSelectedSystems, true);
-
- });
-
- it('Should exclude the selected system', () => {
- wrapper = mountWithIntl(
-
-
-
-
-
- );
-
- const actionsToggle = wrapper.find('KebabToggle button');
- actionsToggle.simulate('click');
-
- const exclude = wrapper.find('Dropdown').at(3).find("DropdownItem button").at(0)
- const include = wrapper.find('Dropdown').at(3).find("DropdownItem button").at(1);
-
- exclude.simulate('click');
-
- expect(include.props('disabled')).toBeTruthy();
- expect(exclude.text()).toBe('Disable Vulnerability analysis on system');
- expect(doOptOutMock).toHaveBeenCalled();
- })
-
- it('Should include the excluded selected system', () => {
- wrapper = mountWithIntl(
-
-
-
-
-
- );
-
- const actionsToggle = wrapper.find('KebabToggle button');
- actionsToggle.simulate('click');
-
- const exclude = wrapper.find('Dropdown').at(3).find("DropdownItem button").at(0)
- const include = wrapper.find('Dropdown').at(3).find("DropdownItem button").at(1);
-
- include.simulate('click');
-
- expect(exclude.props('disabled')).toBeTruthy();
- expect(include.text()).toBe('Enable Vulnerability analysis on system');
- expect(doOptOutMock).toHaveBeenCalled();
- })
-
- it("The default filter is set by name", () => {
- const container = wrapper.find('button[data-ouia-component-id="ConditionalFilter"]');
- expect(container).toHaveLength(1);
- expect(container.text()).toBe('Name');
- })
-});
diff --git a/src/Components/SmartComponents/SystemsPage/SystemsPage.js b/src/Components/SmartComponents/SystemsPage/SystemsPage.js
index 928de4abb..197d56da0 100644
--- a/src/Components/SmartComponents/SystemsPage/SystemsPage.js
+++ b/src/Components/SmartComponents/SystemsPage/SystemsPage.js
@@ -1,9 +1,8 @@
-import React, { useEffect, useState, Fragment } from 'react';
+import React, { useEffect, useState, Fragment, useMemo } from 'react';
import { useIntl } from 'react-intl';
import messages from '../../../Messages';
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
-import SystemsTableToolbar from './SystemsTableToolbar';
-import { PERMISSIONS, SYSTEMS_ALLOWED_PARAMS } from '../../../Helpers/constants';
+import { PERMISSIONS, SYSTEMS_ALLOWED_PARAMS, SYSTEMS_DEFAULT_FILTERS, SYSTEMS_FILTER_PARAMS } from '../../../Helpers/constants';
import ReducerRegistry from '../../../Utilities/ReducerRegistry';
import { Main } from '@redhat-cloud-services/frontend-components/Main';
import { systemTableRowActions } from '../../../Helpers/CVEHelper';
@@ -14,36 +13,41 @@ import {
clearSystemStore,
clearInventoryStore,
selectRows,
- changeColumnsSystemList
+ changeColumnsSystemList,
+ fetchSystems,
+ fetchSystemsIds
} from '../../../Store/Actions/Actions';
import { useUrlParams } from '../../../Helpers/MiscHelper';
import { InventoryTable } from '@redhat-cloud-services/frontend-components/Inventory';
import ErrorHandler from '../../PresentationalComponents/ErrorHandler/ErrorHandler';
import { TableVariant } from '@patternfly/react-table';
-import { useColumnManagement, useGetEntities, useOptOutSystems, useRbac } from '../../../Helpers/Hooks';
+import { useBulkSelect, useColumnManagement, useGetEntities, useOptOutSystems, useRbac } from '../../../Helpers/Hooks';
import * as APIHelper from '../../../Helpers/APIHelper';
import { EmptyStateNoSystems } from '../../PresentationalComponents/EmptyStates/EmptyStates';
-import { clearNotifications } from '@redhat-cloud-services/frontend-components-notifications/redux';
+import { addNotification, clearNotifications } from '@redhat-cloud-services/frontend-components-notifications/redux';
import NoAccessPage from '../../PresentationalComponents/StaticPages/NoAccessPage';
import Spinner from '@redhat-cloud-services/frontend-components/Spinner';
+import { buildActiveFilters, isFilterInDefaultState, removeFilters, exportConfig } from '../../../Helpers/TableToolbarHelper';
+import DownloadReport from '../../../Helpers/DownloadReport';
+import useOsVersionFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter';
+import useSearchFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/SearchFilter';
+import excludedFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/ExcludedFilter';
const SystemsPage = () => {
- const [[canReadVulnerabilityResults,
+ const [[
+ canReadVulnerabilityResults,
canSetExcludedIncluded,
canExport,
- canReadExcluded],
- isLoading] = useRbac([
+ canReadExcluded
+ ], isLoading
+ ] = useRbac([
PERMISSIONS.readVulnerabilityResults,
PERMISSIONS.setExcludedIncluded,
PERMISSIONS.basicReporting,
PERMISSIONS.readExcluded
]);
- const [[
- canReadHostsInventory],
- isLoadingInventory] = useRbac([
- PERMISSIONS.readHosts
- ], 'inventory');
+ const [[canReadHostsInventory], isLoadingInventory] = useRbac([PERMISSIONS.readHosts], 'inventory');
const inventoryRef = React.createRef();
const dispatch = useDispatch();
@@ -92,6 +96,7 @@ const SystemsPage = () => {
const onRefreshInventory = () => {
dispatch(clearInventoryStore());
+
// timestamp is used to force inventory to refresh
// if it wasn't there inventory might ignore request to refresh because parameters are the same
inventoryRef.current.onRefreshData(({ timestamp: Date.now(), page: 1 }));
@@ -103,17 +108,82 @@ const SystemsPage = () => {
const [columnCounter, setColumnCount] = useState(0);
useEffect(() => setColumnCount(columnCounter + 1), [columns]);
- // TODO: let InventoryTable render its own toolbar instead of using custom one
+ const downloadReport = format => {
+ let params = { ...parameters };
+ DownloadReport.exec(
+ fetchSystems,
+ params,
+ format,
+ 'system-list',
+ notification => dispatch(
+ addNotification(notification)
+ ),
+ () => dispatch(clearNotifications())
+ );
+ };
+
+ const kebabProps = useMemo(() => {
+ return {
+ selectedExcluded: selectedRows.some(({ opt_out: optOut }) => optOut === true),
+ selectedIncluded: selectedRows.some(({ opt_out: optOut }) => optOut === false)
+ };
+ }, [selectedRows]);
+
+ const kebabOptions = [
+ '',
+ ...canSetExcludedIncluded ? [{
+ label: intl.formatMessage(messages.systemKebabDisableAnalysis, { count: selectedRowsCount }),
+ onClick: () => doOptOut(selectedRows, selectedRows?.[0].display_name, true),
+ props: { isDisabled: !selectedRowsCount || !kebabProps.selectedIncluded }
+ },
+ {
+ label: intl.formatMessage(messages.systemKebabEnableAnalysis, { count: selectedRowsCount }),
+ onClick: () => doOptOut(selectedRows, selectedRows?.[0].display_name, false, selectedRows),
+ props: { isDisabled: !selectedRowsCount || !kebabProps.selectedExcluded }
+ }] : [],
+ {
+ label: intl.formatMessage(messages.columnManagementModalTitle),
+ onClick: () => setColumnManagementModalOpen(true)
+ }
+ ];
+
+ const rawData = { data: items, meta: { totalItems }, isLoaded };
+
+ const bulkSelectProps = useBulkSelect({
+ rawData,
+ selectedRows,
+ selectedRowsCount,
+ handleSelect,
+ fetchResource: ops => fetchSystemsIds({ ...parameters, ...ops })
+ });
+
+ const osVersionFilter = useOsVersionFilter(
+ parameters.rhel_version,
+ apply
+ );
+
+ const filterConfigItems = [
+ useSearchFilter(
+ 'filter',
+ messages.systemsSearchName,
+ messages.searchFilterByName,
+ parameters.filter,
+ apply
+ ),
+ ...(canReadExcluded ? [excludedFilter(apply, parameters)] : []),
+ ...osVersionFilter
+ ];
+
return (
- isLoading ? :
+ isLoading ? :
canReadVulnerabilityResults ?
- { ColumnManagementModal }
+ {ColumnManagementModal}
-
+
- {isLoadingInventory ? :
+ {isLoadingInventory ? :
hasError && !canReadHostsInventory
- ?
+ ?
: (
{
columns={defaultColumns => mergeColumns(defaultColumns)}
getEntities={getEntities}
hideFilters={{ all: true }}
- noSystemsTable={}
- >
-
-
+ showTags
+ noSystemsTable={}
+ bulkSelect={bulkSelectProps}
+ actionsConfig={{
+ actions: kebabOptions,
+ dropdownProps: { ouiaId: 'toolbar-actions' }
+ }}
+ filterConfig={{
+ items: filterConfigItems
+ }}
+ activeFiltersConfig={{
+ filters: buildActiveFilters(parameters),
+ onDelete: (_, chips, reset) =>
+ removeFilters(chips, apply, reset, SYSTEMS_DEFAULT_FILTERS),
+ deleteTitle: intl.formatMessage(messages.resetFilters),
+ showDeleteButton: !isFilterInDefaultState(
+ parameters,
+ canReadExcluded ? SYSTEMS_DEFAULT_FILTERS : {},
+ SYSTEMS_FILTER_PARAMS)
+ }}
+ exportConfig={canExport ? {
+ isDisabled: rawData.meta.totalItems === 0,
+ ouiaId: 'export',
+ ...exportConfig({ downloadReport })
+ } : null}
+ />
)}
- :
+ :
);
};
diff --git a/src/Components/SmartComponents/SystemsPage/SystemsTableToolbar.js b/src/Components/SmartComponents/SystemsPage/SystemsTableToolbar.js
deleted file mode 100644
index 1f0a0a958..000000000
--- a/src/Components/SmartComponents/SystemsPage/SystemsTableToolbar.js
+++ /dev/null
@@ -1,144 +0,0 @@
-import React, { useMemo } from 'react';
-import propTypes from 'prop-types';
-import { injectIntl } from 'react-intl';
-import messages from '../../../Messages';
-import { useDispatch } from 'react-redux';
-import { dataShape } from '../../../Helpers/MiscHelper';
-import { fetchSystems, fetchSystemsIds } from '../../../Store/Actions/Actions';
-import { PrimaryToolbar } from '@redhat-cloud-services/frontend-components/PrimaryToolbar';
-import useSearchFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/SearchFilter';
-import { exportConfig, buildActiveFilters, removeFilters, isFilterInDefaultState } from '../../../Helpers/TableToolbarHelper';
-import DownloadReport from '../../../Helpers/DownloadReport';
-import excludedFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/ExcludedFilter';
-import { SYSTEMS_DEFAULT_FILTERS, SYSTEMS_FILTER_PARAMS } from '../../../Helpers/constants';
-import { useBulkSelect } from '../../../Helpers/Hooks';
-import {
- addNotification,
- clearNotifications
-} from '@redhat-cloud-services/frontend-components-notifications/redux';
-import useOsVersionFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter';
-
-const SystemsTableToolbar = ({
- selectedRows,
- selectedRowsCount,
- intl,
- canExport,
- canSetExcludedIncluded,
- canReadExcluded,
- parameters,
- rawData,
- methods
-}) => {
- const { apply, handleSelect, doOptOut, setColumnManagementModalOpen } = methods;
- const dispatch = useDispatch();
-
- const downloadReport = format => {
- let params = { ...parameters };
- DownloadReport.exec(
- fetchSystems,
- params,
- format,
- 'system-list',
- notification => dispatch(
- addNotification(notification)
- ),
- () => dispatch(clearNotifications())
- );
- };
-
- const kebabProps = useMemo(() => {
- return {
- selectedExcluded: selectedRows.some(({ opt_out: optOut }) => optOut === true),
- selectedIncluded: selectedRows.some(({ opt_out: optOut }) => optOut === false)
- };
- }, [selectedRows]);
-
- const kebabOptions = [
- '',
- ...canSetExcludedIncluded ? [{
- label: intl.formatMessage(messages.systemKebabDisableAnalysis, { count: selectedRowsCount }),
- onClick: () => doOptOut(selectedRows, selectedRows?.[0].display_name, true),
- props: { isDisabled: !selectedRowsCount || !kebabProps.selectedIncluded }
- },
- {
- label: intl.formatMessage(messages.systemKebabEnableAnalysis, { count: selectedRowsCount }),
- onClick: () => doOptOut(selectedRows, selectedRows?.[0].display_name, false, selectedRows),
- props: { isDisabled: !selectedRowsCount || !kebabProps.selectedExcluded }
- }] : [],
- {
- label: intl.formatMessage(messages.columnManagementModalTitle),
- onClick: () => setColumnManagementModalOpen(true)
- }
- ];
-
- const bulkSelectProps = useBulkSelect({
- rawData,
- selectedRows,
- selectedRowsCount,
- handleSelect,
- fetchResource: ops => fetchSystemsIds({ ...parameters, ...ops })
- });
-
- const osVersionFilter = useOsVersionFilter(
- parameters.rhel_version,
- apply
- );
-
- const filterConfigItems = [
- useSearchFilter(
- 'filter',
- messages.systemsSearchName,
- messages.searchFilterByName,
- parameters.filter,
- apply
- ),
- ...(canReadExcluded ? [excludedFilter(apply, parameters)] : []),
- ...osVersionFilter
- ];
-
- return removeFilters(chips, apply, reset, SYSTEMS_DEFAULT_FILTERS),
- deleteTitle: intl.formatMessage(messages.resetFilters),
- showDeleteButton: !isFilterInDefaultState(
- parameters,
- canReadExcluded ? SYSTEMS_DEFAULT_FILTERS : {},
- SYSTEMS_FILTER_PARAMS)
- }}
- exportConfig={canExport ? {
- isDisabled: rawData.meta.totalItems === 0,
- ouiaId: 'export',
- ...exportConfig({ downloadReport })
- } : null}
- />;
-
-};
-
-SystemsTableToolbar.propTypes = {
- rawData: dataShape,
- canExport: propTypes.bool,
- canSetExcludedIncluded: propTypes.bool,
- canReadExcluded: propTypes.bool,
- parameters: propTypes.object,
- selectedRows: propTypes.array,
- selectedRowsCount: propTypes.number,
- methods: propTypes.shape({
- doOptOut: propTypes.func,
- apply: propTypes.func,
- handleSelect: propTypes.func,
- setColumnManagementModalOpen: propTypes.func
- }),
- intl: propTypes.any
-};
-
-export default injectIntl(SystemsTableToolbar);
diff --git a/src/Components/SmartComponents/SystemsPage/__snapshots__/SystemTableToolbar.test.js.snap b/src/Components/SmartComponents/SystemsPage/__snapshots__/SystemTableToolbar.test.js.snap
deleted file mode 100644
index 494e47aa3..000000000
--- a/src/Components/SmartComponents/SystemsPage/__snapshots__/SystemTableToolbar.test.js.snap
+++ /dev/null
@@ -1,1869 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`SystemsTableToolbar component Should match snapshot 1`] = `
-
-
-
-
-
-
-
-
-
-
-
- Export to CSV
- ,
-
- Export to JSON
- ,
- ]
- }
- isOpen={false}
- isPlain={true}
- onSelect={[Function]}
- ouiaId="Export"
- toggle={
-
-
-
- }
- >
-
- Export to CSV
- ,
-
- Export to JSON
- ,
- ]
- }
- isFlipEnabled={true}
- isGrouped={false}
- isOpen={false}
- isPlain={true}
- isText={false}
- menuAppendTo="inline"
- onSelect={[Function]}
- position="left"
- removeFindDomNode={false}
- toggle={
-
-
-
- }
- zIndex={9999}
- >
- ,
- }
- }
- toggleIndicator={null}
- >
-
-
-
-
-
-
- ,
- }
- }
- toggleVariant="default"
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Disable Vulnerability analysis on systems
- ,
-
- Enable Vulnerability analysis on systems
- ,
-
- Manage columns
- ,
- ]
- }
- isOpen={false}
- isPlain={true}
- onSelect={[Function]}
- ouiaId="Actions"
- toggle={
-
- }
- >
-
- Disable Vulnerability analysis on systems
- ,
-
- Enable Vulnerability analysis on systems
- ,
-
- Manage columns
- ,
- ]
- }
- isFlipEnabled={true}
- isGrouped={false}
- isOpen={false}
- isPlain={true}
- isText={false}
- menuAppendTo="inline"
- onSelect={[Function]}
- position="left"
- removeFindDomNode={false}
- toggle={
-
- }
- zIndex={9999}
- >
- ,
- }
- }
- >
-
-
-
-
- ,
- }
- }
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ,
- }
- }
- id="ins-primary-data-toolbar-expandable-content-6"
- isExpanded={false}
- showClearFiltersButton={false}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Systems with Vulnerability analysis enabled
-
-
- -
-
-
-
-
- Enabled systems
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ,
- }
- }
- id="ins-primary-data-toolbar-expandable-content-7"
- isExpanded={false}
- showClearFiltersButton={false}
- >
-
-
-
-
-
-
- ,
- }
- }
- clearFiltersButtonText="Clear all filters"
- collapseListedFiltersBreakpoint="lg"
- numberOfFilters={0}
- numberOfFiltersText={[Function]}
- showClearFiltersButton={false}
- >
-
-
-
-
-`;