diff --git a/CHANGELOG.md b/CHANGELOG.md index e03809bb52..4450a84152 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Added AngularJS dependencies [#6145](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6145) - Added a migration task to setup the configuration using a configuration file [#6337](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6337) - Added the ability to manage the API hosts from the Server APIs [#6337](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6337) [#6519](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6519) -- Added edit agent groups and upgrade agents actions to Endpoints Summary [#6250](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6250) [#6476](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6476) [#6274](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6274) [#6501](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6501) +- Improve fleet management by adding 'Edit Agent Groups' and 'Upgrade Agents' actions, as well as a filter to show only outdated agents [#6250](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6250) [#6476](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6476) [#6274](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6274) [#6501](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6501) [#6529](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6529) - Added propagation of updates from the table to dashboard visualizations in Endpoints summary [#6460](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6460) - Handle index pattern selector on new discover [#6499](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6499) diff --git a/plugins/main/public/components/agents/syscollector/__snapshots__/inventory.test.tsx.snap b/plugins/main/public/components/agents/syscollector/__snapshots__/inventory.test.tsx.snap index 665925c7d8..df26ef4255 100644 --- a/plugins/main/public/components/agents/syscollector/__snapshots__/inventory.test.tsx.snap +++ b/plugins/main/public/components/agents/syscollector/__snapshots__/inventory.test.tsx.snap @@ -141,7 +141,7 @@ exports[`Inventory component A Apple agent should be well rendered. 1`] = ` class="euiFlexItem" >
{ +const WzButtons: { [key in WzButtonType]: React.FunctionComponent } = { + default: EuiButton, + empty: EuiButtonEmpty, + icon: EuiButtonIcon, + link: EuiLink, + switch: EuiSwitch, +}; + +export const WzButton = ({ + buttonType = WzButtonType.default, + tooltip, + ...rest +}: WzButtonProps) => { const Button = WzButtons[buttonType]; - - const button = + + Show only outdated + +
+ +
+
+
@@ -368,7 +447,7 @@ exports[`AgentsTable component Renders correctly to match the snapshot 1`] = `
+
+
+
+ +
+ + + Show only outdated + +
+
+
+
+
@@ -1030,7 +1189,7 @@ exports[`AgentsTable component Renders correctly to match the snapshot with cust
+
+
+
+ +
+ + + Show only outdated + +
+
+
+
+
@@ -1644,7 +1883,7 @@ exports[`AgentsTable component Renders correctly to match the snapshot with no p
({ appStateReducers: state => state, })); +jest.mock( + '../../../../../../node_modules/@elastic/eui/lib/services/accessibility/html_id_generator', + () => ({ + htmlIdGenerator: () => () => 'htmlId', + }), +); + const permissionsStore = { appStateReducers: { userAccount: { @@ -336,32 +343,13 @@ describe('AgentsTable component', () => { jest.fn()} - wzReq={WzRequest.apiReq} - addingNewAgent={() => jest.fn()} - downloadCsv={() => jest.fn()} - clickAction={() => jest.fn()} - formatUIDate={date => jest.fn()} - reload={() => jest.fn()} + showOnlyOutdated={false} + setShowOnlyOutdated={() => jest.fn()} + totalOutdated={0} /> , ); - // Set table id to avoid snapshot changes - const tableId = '__table_d203a723-1198-11ee-ab9b-75fc624fc672'; - wrapper.find('table')[0]['attribs']['id'] = tableId; - - // Set select all checkbox id to avoid snapshot changes - const checkBoxSelectId = - '_selection_column-checkbox_i6bf14741-d0fa-11ee-81c4-29d002524ab5'; - - //Mobile - wrapper.find('.euiCheckbox__input')[0]['attribs']['id'] = checkBoxSelectId; - wrapper.find('.euiCheckbox__label')[0]['attribs']['for'] = checkBoxSelectId; - - //Desktop - wrapper.find('.euiCheckbox__input')[1]['attribs']['id'] = checkBoxSelectId; - expect(wrapper).toMatchSnapshot(); expect( window.localStorage.getItem('wz-agents-overview-table-visible-fields'), @@ -373,32 +361,13 @@ describe('AgentsTable component', () => { jest.fn()} - wzReq={WzRequest.apiReq} - addingNewAgent={() => jest.fn()} - downloadCsv={() => jest.fn()} - clickAction={() => jest.fn()} - formatUIDate={date => jest.fn()} - reload={() => jest.fn()} + showOnlyOutdated={false} + setShowOnlyOutdated={() => jest.fn()} + totalOutdated={0} /> , ); - // Set table id to avoid snapshot changes - const tableId = '__table_d203a723-1198-11ee-ab9b-75fc624fc672'; - wrapper.find('table')[0]['attribs']['id'] = tableId; - - // Set select all checkbox id to avoid snapshot changes - const checkBoxSelectId = - '_selection_column-checkbox_i6bf14741-d0fa-11ee-81c4-29d002524ab5'; - - //Mobile - wrapper.find('.euiCheckbox__input')[0]['attribs']['id'] = checkBoxSelectId; - wrapper.find('.euiCheckbox__label')[0]['attribs']['for'] = checkBoxSelectId; - - //Desktop - wrapper.find('.euiCheckbox__input')[1]['attribs']['id'] = checkBoxSelectId; - expect(wrapper).toMatchSnapshot(); expect( window.localStorage.getItem('wz-agents-overview-table-visible-fields'), @@ -414,32 +383,13 @@ describe('AgentsTable component', () => { jest.fn()} - wzReq={WzRequest.apiReq} - addingNewAgent={() => jest.fn()} - downloadCsv={() => jest.fn()} - clickAction={() => jest.fn()} - formatUIDate={date => jest.fn()} - reload={() => jest.fn()} + showOnlyOutdated={false} + setShowOnlyOutdated={() => jest.fn()} + totalOutdated={0} /> , ); - // Set table id to avoid snapshot changes - const tableId = '__table_d203a723-1198-11ee-ab9b-75fc624fc672'; - wrapper.find('table')[0]['attribs']['id'] = tableId; - - // Set select all checkbox id to avoid snapshot changes - const checkBoxSelectId = - '_selection_column-checkbox_i6bf14741-d0fa-11ee-81c4-29d002524ab5'; - - //Mobile - wrapper.find('.euiCheckbox__input')[0]['attribs']['id'] = checkBoxSelectId; - wrapper.find('.euiCheckbox__label')[0]['attribs']['for'] = checkBoxSelectId; - - //Desktop - wrapper.find('.euiCheckbox__input')[1]['attribs']['id'] = checkBoxSelectId; - expect(wrapper).toMatchSnapshot(); expect( window.localStorage.getItem('wz-agents-overview-table-visible-fields'), diff --git a/plugins/main/public/components/endpoints-summary/table/agents-table.tsx b/plugins/main/public/components/endpoints-summary/table/agents-table.tsx index e84419283a..fe97484304 100644 --- a/plugins/main/public/components/endpoints-summary/table/agents-table.tsx +++ b/plugins/main/public/components/endpoints-summary/table/agents-table.tsx @@ -18,6 +18,7 @@ import { EuiPanel, EuiCallOut, EuiButton, + EuiToolTip, } from '@elastic/eui'; import { WzButtonPermissions } from '../../common/permissions/button'; import { withErrorBoundary } from '../../common/hocs'; @@ -46,6 +47,7 @@ import { UI_ERROR_SEVERITIES } from '../../../react-services/error-orchestrator/ import { getErrorOrchestrator } from '../../../react-services/common-services'; import { AgentUpgradesInProgress } from './upgrades-in-progress/upgrades-in-progress'; import { AgentUpgradesTaskDetailsModal } from './upgrade-task-details-modal'; +import { WzButton } from '../../common/buttons'; const searchBarWQLOptions = { implicitQuery: { @@ -67,6 +69,9 @@ interface AgentsTableProps { filters: any; updateCurrentAgentData: (agent) => void; externalReload?: boolean; + showOnlyOutdated: boolean; + setShowOnlyOutdated: (newValue: boolean) => void; + totalOutdated?: number; setExternalReload?: (newValue: number) => void; } @@ -177,7 +182,9 @@ export const AgentsTable = compose( const agentIds = data?.items?.map(agent => agent.id); try { - const outdatedAgents = await getOutdatedAgents(agentIds); + const outdatedAgents = agentIds?.length + ? (await getOutdatedAgents({ agentIds })).affected_items + : []; setOutdatedAgents(outdatedAgents); } catch (error) { setOutdatedAgents([]); @@ -205,31 +212,49 @@ export const AgentsTable = compose( ? agentList.totalItems : selectedItems.length; - const selectedtemsRenderer = selectedItems.length ? ( - - - - - {showSelectAllItems ? ( + const selectedtemsRenderer = ( + + {selectedItems.length ? ( - - {!allAgentsSelected - ? `Select all ${agentList.totalItems} agents` - : `Clear ${agentList.totalItems} agents selected`} - + + + + + {showSelectAllItems ? ( + + + {!allAgentsSelected + ? `Select all ${agentList.totalItems} agents` + : `Clear ${agentList.totalItems} agents selected`} + + + ) : null} + ) : null} + + props.setShowOnlyOutdated(!props.showOnlyOutdated)} + /> + - ) : null; + ); const tableRender = () => { // The EuiBasicTable tableLayout is set to "auto" to improve the use of empty space in the component. @@ -250,7 +275,7 @@ export const AgentsTable = compose( allowGetTasks={!denyGetTasks} /> } - actionButtons={({ filters }) => ( + actionButtons={ - )} + } postActionButtons={({ filters }) => ( )} - endpoint='/agents' + endpoint={props.showOnlyOutdated ? '/agents/outdated' : '/agents'} tableColumns={agentsTableColumns( !denyEditGroups, !denyUpgrade, @@ -326,7 +351,7 @@ export const AgentsTable = compose( label: 'dateAdd', description: 'filter by registration date', }, - { label: 'id', description: 'filter by id' }, + { label: 'id', description: 'filter by ID' }, { label: 'ip', description: 'filter by IP address' }, { label: 'group', description: 'filter by group' }, { diff --git a/plugins/main/public/components/endpoints-summary/table/global-actions/edit-groups/result.tsx b/plugins/main/public/components/endpoints-summary/table/global-actions/edit-groups/result.tsx index 4058e70462..8d050e1595 100644 --- a/plugins/main/public/components/endpoints-summary/table/global-actions/edit-groups/result.tsx +++ b/plugins/main/public/components/endpoints-summary/table/global-actions/edit-groups/result.tsx @@ -52,7 +52,7 @@ export const EditAgentsGroupsModalResult = ({ sortable: true, }, ]} - pagination={agents.length > 10} + pagination={true} sorting={{ sort: { field: 'id', @@ -88,12 +88,12 @@ export const EditAgentsGroupsModalResult = ({ }, { field: 'id', - name: 'Agent Ids', + name: 'Agent IDs', align: 'left', render: ids => ids.join(', '), }, ]} - pagination={errors.length > 10} + pagination={true} /> ); diff --git a/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.tsx b/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.tsx index 78f4078758..4a0e1e89c5 100644 --- a/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.tsx +++ b/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.tsx @@ -5,6 +5,7 @@ import { EuiContextMenuPanel, EuiContextMenuItem, EuiHorizontalRule, + EuiToolTip, } from '@elastic/eui'; import { WzElementPermissions } from '../../../common/permissions/element'; import { Agent } from '../../types'; @@ -65,6 +66,18 @@ export const AgentsTableGlobalActions = ({ ? allAgentsCount : selectedAgents.length; + const selectAgentsTooltip = (content: React.ReactNode) => ( + + {content} + + ); + + const actions = { + addGroups: 'Add groups to agents', + removeGroups: 'Remove groups from agents', + upgrade: 'Upgrade agents', + }; + return ( <> - - - Add groups to agents - {totalAgents ? ` (${totalAgents})` : ''} - - + {allowEditGroups && !totalAgents ? ( + selectAgentsTooltip(actions.addGroups) + ) : ( + + + {actions.addGroups} + {totalAgents ? ` (${totalAgents})` : ''} + + + )} { setAddOrRemoveGroups('remove'); closePopover(); setIsEditGroupsVisible(true); }} > - - - Remove groups from agents - {totalAgents ? ` (${totalAgents})` : ''} - - + {allowEditGroups && !totalAgents ? ( + selectAgentsTooltip(actions.removeGroups) + ) : ( + + + {actions.removeGroups} + {totalAgents ? ` (${totalAgents})` : ''} + + + )} { closePopover(); setIsUpgradeAgentsVisible(true); }} > - - - Upgrade agents - {totalAgents ? ` (${totalAgents})` : ''} - - + {allowUpgrade && !totalAgents ? ( + selectAgentsTooltip(actions.upgrade) + ) : ( + + + {actions.upgrade} + {totalAgents ? ` (${totalAgents})` : ''} + + + )} 10} + pagination={true} sorting={{ sort: { field: 'id', @@ -72,7 +72,7 @@ export const UpgradeAgentsModalResult = ({ columns={[ { field: 'agent', - name: 'Agent id', + name: 'Agent ID', align: 'left', sortable: true, }, @@ -90,12 +90,12 @@ export const UpgradeAgentsModalResult = ({ }, { field: 'task_id', - name: 'task id', + name: 'Task ID', align: 'left', sortable: true, }, ]} - pagination={tasks.length > 10} + pagination={true} sorting={{ sort: { field: 'agent', @@ -131,12 +131,12 @@ export const UpgradeAgentsModalResult = ({ }, { field: 'id', - name: 'Agent Ids', + name: 'Agent IDs', align: 'left', render: ids => ids.join(', '), }, ]} - pagination={errors.length > 10} + pagination={true} /> ); diff --git a/plugins/main/public/components/endpoints-summary/table/upgrade-task-details-modal.tsx b/plugins/main/public/components/endpoints-summary/table/upgrade-task-details-modal.tsx index 2197da597f..63f16fd205 100644 --- a/plugins/main/public/components/endpoints-summary/table/upgrade-task-details-modal.tsx +++ b/plugins/main/public/components/endpoints-summary/table/upgrade-task-details-modal.tsx @@ -48,7 +48,7 @@ export const AgentUpgradesTaskDetailsModal = ({ tableColumns={[ { field: 'task_id', - name: 'Task id', + name: 'Task ID', sortable: true, searchable: true, show: true, @@ -56,7 +56,7 @@ export const AgentUpgradesTaskDetailsModal = ({ }, { field: 'agent_id', - name: 'Agent id', + name: 'Agent ID', sortable: true, searchable: true, show: true, @@ -151,7 +151,7 @@ export const AgentUpgradesTaskDetailsModal = ({ return [ { label: 'agent_id', - description: 'filter by agent id', + description: 'filter by agent ID', }, { label: 'status', description: 'filter by status' }, { @@ -162,7 +162,7 @@ export const AgentUpgradesTaskDetailsModal = ({ label: 'last_update_time', description: 'filter by last update date', }, - { label: 'task_id', description: 'filter by task id' }, + { label: 'task_id', description: 'filter by task ID' }, ]; }, value: async (currentValue, { field }) => { diff --git a/plugins/main/public/components/overview/mitre_attack_intelligence/__snapshots__/intelligence.test.tsx.snap b/plugins/main/public/components/overview/mitre_attack_intelligence/__snapshots__/intelligence.test.tsx.snap index 718fee7b6a..84909f720c 100644 --- a/plugins/main/public/components/overview/mitre_attack_intelligence/__snapshots__/intelligence.test.tsx.snap +++ b/plugins/main/public/components/overview/mitre_attack_intelligence/__snapshots__/intelligence.test.tsx.snap @@ -161,7 +161,7 @@ exports[`Module Mitre Att&ck intelligence container should render the component class="euiFlexItem" >
{/* agent name (agent id) Unpin button right aligned, require justifyContent="flexEnd" in the EuiFlexGroup */} - +