diff --git a/CHANGELOG.md b/CHANGELOG.md index e235a66453..2f5e5155d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Changed the query to search for an agent in `management/configuration`. [#5485](https://github.com/wazuh/wazuh-kibana-app/pull/5485) - Changed the search bar in management/log to the one used in the rest of the app. [#5476](https://github.com/wazuh/wazuh-kibana-app/pull/5476) - Changed the design of the wizard to add agents. [#5457](https://github.com/wazuh/wazuh-kibana-app/pull/5457) -- Changed the search bar in Management (Rules, Decoders, CDB List, Groups) and Modules (Vulnerabilities > Inventory, Security Configuration Assessment > Inventory > {Policy ID} > Checks, MITRE ATT&CK > Intelligence > {Resource}, Integrity monitoring > Inventory > Files, Integrity monitoring > Inventory > Registry), Agent Inventory data, Explore agent modal [#5363](https://github.com/wazuh/wazuh-kibana-app/pull/5363) [#5442](https://github.com/wazuh/wazuh-kibana-app/pull/5442) [#5443](https://github.com/wazuh/wazuh-kibana-app/pull/5443) [#5444](https://github.com/wazuh/wazuh-kibana-app/pull/5444) [#5445](https://github.com/wazuh/wazuh-kibana-app/pull/5445) [#5447](https://github.com/wazuh/wazuh-kibana-app/pull/5447) +- Changed the search bar in Management (Rules, Decoders, CDB List, Groups) and Modules (Vulnerabilities > Inventory, Security Configuration Assessment > Inventory > {Policy ID} > Checks, MITRE ATT&CK > Intelligence > {Resource}, Integrity monitoring > Inventory > Files, Integrity monitoring > Inventory > Registry), Agent Inventory data, Explore agent modal, Agents [#5363](https://github.com/wazuh/wazuh-kibana-app/pull/5363) [#5442](https://github.com/wazuh/wazuh-kibana-app/pull/5442) [#5443](https://github.com/wazuh/wazuh-kibana-app/pull/5443) [#5444](https://github.com/wazuh/wazuh-kibana-app/pull/5444) [#5445](https://github.com/wazuh/wazuh-kibana-app/pull/5445) [#5447](https://github.com/wazuh/wazuh-kibana-app/pull/5447) [#5452](https://github.com/wazuh/wazuh-kibana-app/pull/5452) ### Fixed diff --git a/plugins/main/public/controllers/agent/components/agents-preview.js b/plugins/main/public/controllers/agent/components/agents-preview.js index 31ed799603..ef052c0745 100644 --- a/plugins/main/public/controllers/agent/components/agents-preview.js +++ b/plugins/main/public/controllers/agent/components/agents-preview.js @@ -197,7 +197,7 @@ export const AgentsPreview = compose( } removeFilters() { - this._isMount && this.setState({ agentTableFilters: [] }); + this._isMount && this.setState({ agentTableFilters: {} }); } showAgent(agent) { @@ -207,7 +207,7 @@ export const AgentsPreview = compose( filterAgentByStatus(status) { this._isMount && this.setState({ - agentTableFilters: [{ field: 'q', value: `status=${status}` }], + agentTableFilters: { q: `id!=000;status=${status}` }, }); } onRenderComplete() { diff --git a/plugins/main/public/controllers/agent/components/agents-table.js b/plugins/main/public/controllers/agent/components/agents-table.js index 09141ac9d6..853af6a40c 100644 --- a/plugins/main/public/controllers/agent/components/agents-table.js +++ b/plugins/main/public/controllers/agent/components/agents-table.js @@ -14,41 +14,37 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { - EuiBasicTable, EuiButton, - EuiButtonEmpty, EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiToolTip, - EuiTitle, - EuiSpacer, - EuiCallOut, - EuiCheckboxGroup, - EuiIcon, + EuiIconTip, } from '@elastic/eui'; -import { getToasts } from '../../../kibana-services'; import { AppNavigate } from '../../../react-services/app-navigate'; import { GroupTruncate } from '../../../components/common/util'; -import { - WzSearchBar, - filtersToObject, -} from '../../../components/wz-search-bar'; -import { getAgentFilterValues } from '../../../controllers/management/components/management/groups/get-agents-filters-values'; import { WzButtonPermissions } from '../../../components/common/permissions/button'; import { formatUIDate } from '../../../react-services/time-service'; import { withErrorBoundary } from '../../../components/common/hocs'; import { API_NAME_AGENT_STATUS, - UI_LOGGER_LEVELS, UI_ORDER_AGENT_STATUS, AGENT_SYNCED_STATUS, + SEARCH_BAR_WQL_VALUE_SUGGESTIONS_COUNT, } from '../../../../common/constants'; -import { UI_ERROR_SEVERITIES } from '../../../react-services/error-orchestrator/types'; -import { getErrorOrchestrator } from '../../../react-services/common-services'; import { AgentStatus } from '../../../components/agents/agent_status'; import { AgentSynced } from '../../../components/agents/agent-synced'; +import { TableWzAPI } from '../../../components/common/tables'; +import { WzRequest } from '../../../react-services/wz-request'; +import { get as getLodash } from 'lodash'; + +const searchBarWQLOptions = { + implicitQuery: { + query: 'id!=000', + conjunction: ';', + }, +}; export const AgentsTable = withErrorBoundary( class AgentsTable extends Component { @@ -56,285 +52,43 @@ export const AgentsTable = withErrorBoundary( constructor(props) { super(props); this.state = { - agents: [], - isLoading: false, - pageIndex: 0, - pageSize: 15, - sortDirection: 'asc', - sortField: 'id', - totalItems: 0, - selectedItems: [], - allSelected: false, - purgeModal: false, - isFilterColumnOpen: false, - filters: sessionStorage.getItem('agents_preview_selected_options') - ? JSON.parse( - sessionStorage.getItem('agents_preview_selected_options'), - ) - : [], - }; - this.suggestions = [ - { - type: 'q', - label: 'status', - description: 'Filter by agent connection status', - operators: ['=', '!='], - values: UI_ORDER_AGENT_STATUS, - }, - { - type: 'q', - label: 'group_config_status', - description: 'Filter by agent synced configuration status', - operators: ['=', '!='], - values: [AGENT_SYNCED_STATUS.SYNCED, AGENT_SYNCED_STATUS.NOT_SYNCED], - }, - { - type: 'q', - label: 'os.platform', - description: 'Filter by operating system platform', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('os.platform', value, { q: 'id!=000' }), - }, - { - type: 'q', - label: 'ip', - description: 'Filter by agent IP address', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('ip', value, { q: 'id!=000' }), - }, - { - type: 'q', - label: 'name', - description: 'Filter by agent name', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('name', value, { q: 'id!=000' }), - }, - { - type: 'q', - label: 'id', - description: 'Filter by agent id', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('id', value, { q: 'id!=000' }), + filters: { + default: { q: 'id!=000' }, + ...(sessionStorage.getItem('wz-agents-overview-table-filter') + ? JSON.parse( + sessionStorage.getItem('wz-agents-overview-table-filter'), + ) + : {}), }, - { - type: 'q', - label: 'group', - description: 'Filter by agent group', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('group', value, { q: 'id!=000' }), - }, - { - type: 'q', - label: 'node_name', - description: 'Filter by node name', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('node_name', value, { q: 'id!=000' }), - }, - { - type: 'q', - label: 'manager', - description: 'Filter by manager', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('manager', value, { q: 'id!=000' }), - }, - { - type: 'q', - label: 'version', - description: 'Filter by agent version', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('version', value, { q: 'id!=000' }), - }, - { - type: 'q', - label: 'configSum', - description: 'Filter by agent config sum', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('configSum', value, { q: 'id!=000' }), - }, - { - type: 'q', - label: 'mergedSum', - description: 'Filter by agent merged sum', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('mergedSum', value, { q: 'id!=000' }), - }, - { - type: 'q', - label: 'dateAdd', - description: 'Filter by add date', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('dateAdd', value, { q: 'id!=000' }), - }, - { - type: 'q', - label: 'lastKeepAlive', - description: 'Filter by last keep alive', - operators: ['=', '!='], - values: async value => - getAgentFilterValues('lastKeepAlive', value, { q: 'id!=000' }), - }, - ]; - this.downloadCsv.bind(this); + reloadTable: 0, + }; } - onTableChange = ({ page = {}, sort = {} }) => { - const { index: pageIndex, size: pageSize } = page; - const { field: sortField, direction: sortDirection } = sort; - this._isMount && - this.setState({ - pageIndex, - pageSize, - sortField, - sortDirection, - }); - }; - async componentDidMount() { this._isMount = true; - await this.getItems(); } componentWillUnmount() { this._isMount = false; - if (sessionStorage.getItem('agents_preview_selected_options')) { - sessionStorage.removeItem('agents_preview_selected_options'); + if (sessionStorage.getItem('wz-agents-overview-table-filter')) { + sessionStorage.removeItem('wz-agents-overview-table-filter'); } } async reloadAgents() { - await this.getItems(); + this.setState({ reloadTable: Date.now() }); await this.props.reload(); } - async componentDidUpdate(prevProps, prevState) { + async componentDidUpdate(prevProps) { if ( - !_.isEqual(prevState.filters, this.state.filters) || - prevState.pageIndex !== this.state.pageIndex || - prevState.pageSize !== this.state.pageSize || - prevState.sortField !== this.state.sortField || - prevState.sortDirection !== this.state.sortDirection - ) { - await this.getItems(); - } else if ( - !_.isEqual(prevProps.filters, this.props.filters) && - this.props.filters && - this.props.filters.length + // TODO: external filters + !_.isEqual(prevProps.filters, this.props.filters) ) { - this.setState({ filters: this.props.filters, pageIndex: 0 }); - this.props.removeFilters(); + this.setState({ filters: this.props.filters }); } } - async getItems() { - try { - this._isMount && this.setState({ isLoading: true }); - const selectFieldsList = this.defaultColumns - .filter(field => field.field != 'actions') - .map(field => field.field.replace('os_', 'os.')); // "os_name" subfield should be specified as 'os.name' - const selectFields = [ - ...selectFieldsList, - 'os.platform', - 'os.uname', - 'os.version', - ].join(','); // Add version and uname fields to render the OS icon and version in the table - - const rawAgents = await this.props.wzReq('GET', '/agents', { - params: { ...this.buildFilter(), select: selectFields }, - }); - const formatedAgents = ( - ((rawAgents || {}).data || {}).data || {} - ).affected_items.map(this.formatAgent.bind(this)); - - this._isMount && - this.setState({ - agents: formatedAgents, - totalItems: (((rawAgents || {}).data || {}).data || {}) - .total_affected_items, - isLoading: false, - }); - } catch (error) { - const options = { - context: `${AgentsTable.name}.getItems`, - level: UI_LOGGER_LEVELS.ERROR, - severity: UI_ERROR_SEVERITIES.BUSINESS, - store: true, - error: { - error: error, - message: error.message || error, - title: `Could not get the agents list`, - }, - }; - getErrorOrchestrator().handleError(options); - this.setState({ isLoading: false }); - } - } - - buildFilter() { - const { pageIndex, pageSize, filters } = this.state; - - const filter = { - ...filtersToObject(filters), - offset: pageIndex * pageSize || 0, - limit: pageSize, - sort: this.buildSortFilter(), - }; - filter.q = !filter.q ? `id!=000` : `id!=000;${filter.q}`; - - return filter; - } - - buildSortFilter() { - const { sortField, sortDirection } = this.state; - - const field = sortField === 'os_name' ? 'os.name,os.version' : sortField; - const direction = sortDirection === 'asc' ? '+' : '-'; - - return direction + field; - } - - buildQFilter() { - const { q } = this.state; - return q === '' ? `id!=000` : `id!=000;${q}`; - } - - formatAgent(agent) { - const agentVersion = - agent.version !== undefined ? agent.version.split(' ')[1] : '-'; - const node_name = - agent.node_name && agent.node_name !== 'unknown' - ? agent.node_name - : '-'; - - return { - id: agent.id, - name: agent.name, - ip: agent.ip, - status: agent.status, - group_config_status: agent.group_config_status, - group: agent?.group || '-', - os_name: agent, - version: agentVersion, - node_name: node_name, - dateAdd: agent.dateAdd ? formatUIDate(agent.dateAdd) : '-', - lastKeepAlive: agent.lastKeepAlive - ? formatUIDate(agent.lastKeepAlive) - : '-', - actions: agent, - upgrading: false, - }; - } - actionButtonsRender(agent) { return (
@@ -405,110 +159,6 @@ export const AgentsTable = withErrorBoundary( ); } - reloadAgent = () => { - this._isMount && - this.setState({ - isLoading: true, - }); - this.props.reload(); - }; - - downloadCsv = () => { - const filters = this.buildFilter(); - const formatedFilters = Object.keys(filters) - .filter(field => !['limit', 'offset', 'sort'].includes(field)) - .map(field => ({ name: field, value: filters[field] })); - this.props.downloadCsv(formatedFilters); - }; - - openColumnsFilter = () => { - this.setState({ - isFilterColumnOpen: !this.state.isFilterColumnOpen, - }); - }; - - formattedButton() { - return ( - <> - - - Export formatted - - - - - - - - - - - ); - } - - showToast = (color, title, text, time) => { - getToasts().add({ - color: color, - title: title, - text: text, - toastLifeTimeMs: time, - }); - }; - - callOutRender() { - const { selectedItems, pageSize, allSelected, totalItems } = this.state; - - if (selectedItems.length === 0) { - return; - } else if (selectedItems.length === pageSize) { - return ( -
- - - - - { - this._isMount && - this.setState(prevState => ({ - allSelected: !prevState.allSelected, - })); - }} - > - {allSelected - ? `Clear all agents selection (${totalItems})` - : `Select all agents (${totalItems})`} - - - - - -
- ); - } - } - - getTableColumnsSelected() { - return ( - JSON.parse(window.localStorage.getItem('columnsSelectedTableAgent')) || - [] - ); - } - - setTableColumnsSelected(data) { - window.localStorage.setItem( - 'columnsSelectedTableAgent', - JSON.stringify(data), - ); - } - // Columns with the property truncateText: true won't wrap the text // This is added to prevent the wrap because of the table-layout: auto defaultColumns = [ @@ -517,18 +167,21 @@ export const AgentsTable = withErrorBoundary( name: 'ID', sortable: true, show: true, + searchable: true, }, { field: 'name', name: 'Name', sortable: true, show: true, + searchable: true, }, { field: 'ip', name: 'IP address', sortable: true, show: true, + searchable: true, }, { field: 'group', @@ -536,37 +189,64 @@ export const AgentsTable = withErrorBoundary( sortable: true, show: true, render: groups => (groups !== '-' ? this.renderGroups(groups) : '-'), + searchable: true, }, { - field: 'os_name', + field: 'os.name,os.version', + composeField: ['os.name', 'os.version'], name: 'Operating system', sortable: true, show: true, - render: this.addIconPlatformRender, + render: (field, agentData) => this.addIconPlatformRender(agentData), + searchable: true, }, { field: 'node_name', name: 'Cluster node', sortable: true, show: true, + searchable: true, }, { field: 'version', name: 'Version', sortable: true, show: true, + searchable: true, }, { field: 'dateAdd', - name: 'Registration date', + name: ( + + Registration date{' '} + + + ), sortable: true, show: false, + searchable: false, }, { field: 'lastKeepAlive', - name: 'Last keep alive', + name: ( + + Last keep alive{' '} + + + ), sortable: true, show: false, + searchable: false, }, { field: 'status', @@ -580,6 +260,7 @@ export const AgentsTable = withErrorBoundary( labelProps={{ className: 'hide-agent-status' }} /> ), + searchable: true, }, { field: 'group_config_status', @@ -587,6 +268,7 @@ export const AgentsTable = withErrorBoundary( sortable: true, show: false, render: synced => , + searchable: true, }, { align: 'right', @@ -594,133 +276,11 @@ export const AgentsTable = withErrorBoundary( field: 'actions', name: 'Actions', show: true, - render: agent => this.actionButtonsRender(agent), + render: (field, agentData) => this.actionButtonsRender(agentData), + searchable: false, }, ]; - columns() { - const selectedColumns = this.getTableColumnsSelected(); - - if (selectedColumns.length != 0) { - const newSelectedColumns = []; - selectedColumns.forEach(item => { - if (item.show) { - const column = this.defaultColumns.find( - column => column.field === item.field, - ); - newSelectedColumns.push(column); - } - }); - return newSelectedColumns; - } else { - const fieldColumns = this.defaultColumns.map(item => { - return { - field: item.field, - name: item.name, - show: item.show, - }; - }); - this.setTableColumnsSelected(fieldColumns); - return fieldColumns; - } - } - - headRender() { - const formattedButton = this.formattedButton(); - return ( -
- - - - - {!!this.state.totalItems && ( - -

Agents ({this.state.totalItems})

-
- )} -
-
-
- - this.props.addingNewAgent()} - > - Deploy new agent - - - {formattedButton} -
- -
- ); - } - - filterBarRender() { - return ( - - - - this.setState({ filters, pageIndex: 0 }) - } - placeholder='Filter or search agent' - /> - - - this.reloadAgents()} - > - Refresh - - - - ); - } - - selectColumnsRender() { - const columnsSelected = this.getTableColumnsSelected(); - - const onChange = optionId => { - let item = columnsSelected.find(item => item.field === optionId); - item.show = !item.show; - this.setTableColumnsSelected(columnsSelected); - this.forceUpdate(); - }; - - const options = () => { - return columnsSelected.map(item => { - return { - id: item.field, - label: item.name, - checked: item.show, - }; - }); - }; - - return this.state.isFilterColumnOpen ? ( - - - - - - ) : ( - '' - ); - } - tableRender() { const getRowProps = item => { const { id } = item; @@ -746,50 +306,201 @@ export const AgentsTable = withErrorBoundary( }; }; - const { - pageIndex, - pageSize, - totalItems, - agents, - sortField, - sortDirection, - isLoading, - } = this.state; - const columns = this.columns(); - const pagination = - totalItems > 15 - ? { - pageIndex: pageIndex, - pageSize: pageSize, - totalItemCount: totalItems, - pageSizeOptions: [15, 25, 50, 100], - } - : false; - const sorting = { - sort: { - field: sortField, - direction: sortDirection, - }, - }; - // The EuiBasicTable tableLayout is set to "auto" to improve the use of empty space in the component. // Previously the tableLayout is set to "fixed" with percentage width for each column, but the use of space was not optimal. // Important: If all the columns have the truncateText property set to true, the table cannot adjust properly when the viewport size is small. return ( - this.props.addingNewAgent()} + > + Deploy new agent + , + ]} + endpoint='/agents' + tableColumns={this.defaultColumns} + tableInitialSortingField='id' + tablePageSizeOptions={[10, 25, 50, 100]} + reload={this.state.reloadTable} + mapResponseItem={item => { + return { + ...item, + ...(item.ip ? { ip: item.ip } : { ip: '-' }), + ...(typeof item.dateAdd === 'string' + ? { dateAdd: formatUIDate(item.dateAdd) } + : { dateAdd: '-' }), + ...(typeof item.lastKeepAlive === 'string' + ? { lastKeepAlive: formatUIDate(item.lastKeepAlive) } + : { lastKeepAlive: '-' }), + ...(item.node_name !== 'unknown' + ? { node_name: item.node_name } + : { node_name: '-' }), + /* + The agent version contains the Wazuh word, this gets the string starting with + v + */ + ...(typeof item.version === 'string' + ? { version: item.version.match(/(v\d.+)/)?.[1] } + : { version: '-' }), + }; + }} rowProps={getRowProps} - cellProps={getCellProps} - noItemsMessage='No agents found' - {...(pagination && { pagination })} + filters={this.state.filters} + downloadCsv + showReload + showFieldSelector + searchTable + searchBarWQL={{ + options: searchBarWQLOptions, + suggestions: { + field(currentValue) { + return [ + { + label: 'dateAdd', + description: 'filter by registration date', + }, + { label: 'id', description: 'filter by id' }, + { label: 'ip', description: 'filter by IP address' }, + { label: 'group', description: 'filter by group' }, + { + label: 'group_config_status', + description: 'filter by group configuration status', + }, + { + label: 'lastKeepAlive', + description: 'filter by last keep alive', + }, + { label: 'manager', description: 'filter by manager' }, + { label: 'name', description: 'filter by name' }, + { + label: 'node_name', + description: 'filter by cluster name', + }, + { + label: 'os.name', + description: 'filter by operating system name', + }, + { + label: 'os.platform', + description: 'filter by operating platform', + }, + { + label: 'os.version', + description: 'filter by operating system version', + }, + { label: 'status', description: 'filter by status' }, + { label: 'version', description: 'filter by version' }, + ]; + }, + value: async (currentValue, { field }) => { + try { + switch (field) { + case 'status': + return UI_ORDER_AGENT_STATUS.map(status => ({ + label: status, + })); + case 'group_config_status': + return [ + AGENT_SYNCED_STATUS.SYNCED, + AGENT_SYNCED_STATUS.NOT_SYNCED, + ].map(label => ({ + label, + })); + default: { + const response = await WzRequest.apiReq( + 'GET', + '/agents', + { + params: { + distinct: true, + limit: SEARCH_BAR_WQL_VALUE_SUGGESTIONS_COUNT, + select: field, + sort: `+${field}`, + ...(currentValue + ? { + q: `${searchBarWQLOptions.implicitQuery.query}${searchBarWQLOptions.implicitQuery.conjunction}${field}~${currentValue}`, + } + : { + q: `${searchBarWQLOptions.implicitQuery.query}`, + }), + }, + }, + ); + if (field === 'group') { + /* the group field is returned as an string[], + example: ['group1', 'group2'] + + Due the API request done to get the distinct values for the groups is + not returning the exepected values, as workaround, the values are + extracted in the frontend using the returned results. + + This API request to get the distint values of groups doesn't + return the unique values for the groups, else the unique combination + of groups. + */ + return response?.data?.data.affected_items + .map(item => getLodash(item, field)) + .flat() + .filter( + (item, index, array) => + array.indexOf(item) === index, + ) + .sort() + .map(group => ({ label: group })); + } + return response?.data?.data.affected_items.map( + item => ({ + label: getLodash(item, field), + }), + ); + } + } + } catch (error) { + return []; + } + }, + }, + validate: { + value: ({ formattedValue, value: rawValue }, { field }) => { + const value = formattedValue ?? rawValue; + if (value) { + if (['dateAdd', 'lastKeepAlive'].includes(field)) { + return /^\d{4}-\d{2}-\d{2}([ T]\d{2}:\d{2}:\d{2}(.\d{1,6})?Z?)?$/.test( + value, + ) + ? undefined + : `"${value}" is not a expected format. Valid formats: YYYY-MM-DD, YYYY-MM-DD HH:mm:ss, YYYY-MM-DDTHH:mm:ss, YYYY-MM-DDTHH:mm:ssZ.`; + } + } + }, + }, + }} + searchBarProps={{ + buttonsRender: () => ( + this.reloadAgents()} + > + Refresh + + ), + }} + saveStateStorage={{ + system: 'localStorage', + key: 'wz-agents-overview-table', + }} + tableProps={{ + tableLayout: 'auto', + getCellProps, + }} /> @@ -797,25 +508,16 @@ export const AgentsTable = withErrorBoundary( } filterGroupBadge = group => { - const { filters } = this.state; - let auxFilters = filters.map( - filter => filter.value.match(/group=(.*S?)/)[1], - ); - if (filters.length > 0) { - !auxFilters.includes(group) - ? this.setState({ - filters: [...filters, { field: 'q', value: `group=${group}` }], - }) - : false; - } else { - this.setState({ - filters: [...filters, { field: 'q', value: `group=${group}` }], - }); - } + this.setState({ + filters: { + default: { q: 'id!=000' }, + q: `id!=000;group=${group}`, + }, + }); }; renderGroups(groups) { - return ( + return Array.isArray(groups) ? ( - ); + ) : undefined; } render() { - const title = this.headRender(); - const filter = this.filterBarRender(); - const selectColumnsRender = this.selectColumnsRender(); const table = this.tableRender(); - const callOut = this.callOutRender(); - let renderPurgeModal, loadItems; return (
- {filter} - - - {title} - {loadItems} - {callOut} - {selectColumnsRender} - {table} - {renderPurgeModal} - + {table}
); } diff --git a/plugins/main/public/controllers/overview/components/stats.js b/plugins/main/public/controllers/overview/components/stats.js index a9b1786250..0f8a7bfab4 100644 --- a/plugins/main/public/controllers/overview/components/stats.js +++ b/plugins/main/public/controllers/overview/components/stats.js @@ -33,11 +33,11 @@ export const Stats = withErrorBoundary (class Stats extends Component { goToAgents(status) { if(status){ sessionStorage.setItem( - 'agents_preview_selected_options', - JSON.stringify([{field: 'q', value: `status=${status}`}]) + 'wz-agents-overview-table-filter', + JSON.stringify({q: `id!=000;status=${status}`}) ); - }else if(sessionStorage.getItem('agents_preview_selected_options')){ - sessionStorage.removeItem('agents_preview_selected_options'); + }else if(sessionStorage.getItem('wz-agents-overview-table-filter')){ + sessionStorage.removeItem('wz-agents-overview-table-filter'); } window.location.href = '#/agents-preview'; }