diff --git a/CHANGELOG.md b/CHANGELOG.md index b82c42fdf5..0566813cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,11 +30,17 @@ All notable changes to the Wazuh app project will be documented in this file. - Refined the layout of the agent details view [#7193](https://github.com/wazuh/wazuh-dashboard-plugins/issues/7193) - Changed the width of the command column, relocate argvs column and change the width of the rest of the columns in the table processes. [#7195](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7195) -## Wazuh v4.10.1 - OpenSearch Dashboards 2.16.0 - Revision 00 +## Wazuh v4.10.1 - OpenSearch Dashboards 2.16.0 - Revision 01 ### Added - Support for Wazuh 4.10.1 +- Added comma separators to numbers [#7233](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7233) + +### Changed + +- Move the ability to manage the visibility of fields in `Events` and `Vulnerability Detection` > `Inventory` tables from `Columns` button to a new `Available fields` button enhancing the performance of the view [#7226](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7226) +- Change the color of `Export formatted` button of data grid tables to match the color of the rest of table buttons [#7226](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7226) ## Wazuh v4.10.0 - OpenSearch Dashboards 2.16.0 - Revision 08 @@ -87,6 +93,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Fixed error message to prevent pass no strings to the wazuh logger [#7167](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7167) - Fixed the rendering of the `data.vunerability.reference` in the table and flyout [#7177](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7177) - Fixed incorrect or empty Wazuh API version displayed after upgrade [#440](https://github.com/wazuh/wazuh-dashboard/issues/440) +- Fixed typo in flyout title for available updates [#7235](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7235) ### Removed diff --git a/plugins/main/public/components/common/charts/visualizations/legend.tsx b/plugins/main/public/components/common/charts/visualizations/legend.tsx index f3a2c6a6e8..b707a16c49 100644 --- a/plugins/main/public/components/common/charts/visualizations/legend.tsx +++ b/plugins/main/public/components/common/charts/visualizations/legend.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { EuiIcon } from '@elastic/eui'; import { EuiListGroup } from '@elastic/eui'; import './legend.scss'; +import { formatUINumber } from '../../../../react-services/format-number'; type ChartLegendProps = { data: { @@ -24,7 +25,7 @@ export function ChartLegend({ data }: ChartLegendProps) { textOverflow: 'ellipsis', overflow: 'hidden', }} - >{`${label} (${value})`} + >{`${label} (${formatUINumber(value)})`} ), icon: , ...rest, diff --git a/plugins/main/public/components/common/data-grid/use-data-grid.tsx b/plugins/main/public/components/common/data-grid/use-data-grid.tsx index 68ff88c09e..551b6107bd 100644 --- a/plugins/main/public/components/common/data-grid/use-data-grid.tsx +++ b/plugins/main/public/components/common/data-grid/use-data-grid.tsx @@ -247,7 +247,10 @@ export const useDataGrid = (props: tDataGridProps): EuiDataGridProps => { return { 'aria-labelledby': props.ariaLabelledBy, - columns: orderFirstMatchedColumns(getColumns, visibleColumns), + columnsAvailable: orderFirstMatchedColumns(getColumns, visibleColumns), // This is a custom property used by the Available fields and is not part of EuiDataGrid component specification + columns: visibleColumns.map(columnId => + getColumns.find(({ id }) => id === columnId), + ), columnVisibility: { visibleColumns, setVisibleColumns, diff --git a/plugins/main/public/components/common/tables/table-wz-api.tsx b/plugins/main/public/components/common/tables/table-wz-api.tsx index f40e81b955..93b8641251 100644 --- a/plugins/main/public/components/common/tables/table-wz-api.tsx +++ b/plugins/main/public/components/common/tables/table-wz-api.tsx @@ -27,6 +27,8 @@ import { TableDefault } from './table-default'; import { WzRequest } from '../../../react-services/wz-request'; import { ExportTableCsv } from './components/export-table-csv'; import { useStateStorage, useAppConfig } from '../hooks'; +import { formatUINumber } from '../../../react-services/format-number'; + /** * Search input custom filter button */ @@ -217,7 +219,7 @@ export function TableWzAPI({ {isLoading ? ( ) : ( - ({totalItems}) + ({formatUINumber(totalItems)}) )} diff --git a/plugins/main/public/components/common/wazuh-data-grid/wz-data-grid.tsx b/plugins/main/public/components/common/wazuh-data-grid/wz-data-grid.tsx index ff1413aa35..fe643ca4fe 100644 --- a/plugins/main/public/components/common/wazuh-data-grid/wz-data-grid.tsx +++ b/plugins/main/public/components/common/wazuh-data-grid/wz-data-grid.tsx @@ -160,6 +160,10 @@ const WazuhDataGrid = (props: tWazuhDataGridProps) => { {!isLoading && results?.hits?.total > 0 ? (
void; maxEntriesPerQuery?: number; dateRange: TimeRange; + columnsAvailable: EuiDataGridColumn[]; + columnVisibility: EuiDataGridColumnVisibility; }; const DiscoverDataGridAdditionalControls = ( @@ -27,8 +32,9 @@ const DiscoverDataGridAdditionalControls = ( maxEntriesPerQuery = MAX_ENTRIES_PER_QUERY, onClickExportResults, dateRange, + columnsAvailable, + columnVisibility, } = props; - const onHandleExportResults = () => { onClickExportResults && onClickExportResults(); }; @@ -69,13 +75,18 @@ const DiscoverDataGridAdditionalControls = ( disabled={totalHits === 0 || isExporting} size='xs' iconType='exportAction' - color='primary' + color='text' isLoading={isExporting} className='euiDataGrid__controlBtn' onClick={onHandleExportResults} > Export Formatted + + ); }; diff --git a/plugins/main/public/components/common/wazuh-discover/components/visible-columns-selector.tsx b/plugins/main/public/components/common/wazuh-discover/components/visible-columns-selector.tsx new file mode 100644 index 0000000000..71beaa14f8 --- /dev/null +++ b/plugins/main/public/components/common/wazuh-discover/components/visible-columns-selector.tsx @@ -0,0 +1,154 @@ +import React, { useState, ChangeEvent } from 'react'; +import { FormattedMessage } from '@osd/i18n/react'; +import classNames from 'classnames'; +import { + EuiPopover, + EuiPopoverTitle, + EuiButtonEmpty, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiSwitch, + EuiDataGridColumn, + EuiDataGridColumnVisibility, + EuiToolTip, + EuiIcon, +} from '@elastic/eui'; + +// Based on https://github.com/opensearch-project/oui/blob/1.8.1/src/components/datagrid/column_selector.tsx +// Only select visibility funcitonality +// Replace OuiI18n by FormattedMessage +export const DataGridVisibleColumnsSelector = ({ + availableColumns, + columnVisibility, +}: { + availableColumns: EuiDataGridColumn[]; + columnVisibility: EuiDataGridColumnVisibility; +}) => { + const [searchValue, setSearchValue] = useState(''); + const [isOpen, setIsOpen] = useState(false); + + const maxAvailableColumns = 500; + + const { visibleColumns, setVisibleColumns } = columnVisibility; + + const searchValueLowerCase = searchValue.toLowerCase(); + + const filteredColumns = ( + searchValue + ? availableColumns.filter(({ name }) => + name.toLowerCase().includes(searchValueLowerCase), + ) + : availableColumns + ).slice(0, maxAvailableColumns); + + const controlBtnClasses = classNames('euiDataGrid__controlBtn', { + // TODO: research if this is required + // 'euiDataGrid__controlBtn--active': + // availableColumns.length - visibleColumns.length > 0, + }); + + return ( + setIsOpen(false)} + anchorPosition='downLeft' + panelPaddingSize='s' + panelClassName='euiDataGridColumnSelectorPopover' + button={ + setIsOpen(!isOpen)} + > + + {availableColumns.length > maxAvailableColumns && ( + + } + > + + + )} + + } + > + + ) => + setSearchValue(e.currentTarget.value) + } + /> + +
+ {filteredColumns.map(({ name, id }) => { + return ( +
+ + + { + const { + target: { checked }, + } = event; + + let nextVisibleColumns; + + if (checked) { + if (!visibleColumns.includes(id)) { + nextVisibleColumns = [...visibleColumns, id]; + } + } else { + if (visibleColumns.includes(id)) { + nextVisibleColumns = visibleColumns.filter( + (visibleColumnId: string) => visibleColumnId !== id, + ); + } + } + if (nextVisibleColumns) { + setVisibleColumns(nextVisibleColumns); + } + }} + /> + + +
+ ); + })} +
+
+ ); +}; diff --git a/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx b/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx index 8cffce3fed..75aa6c36d0 100644 --- a/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx +++ b/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx @@ -275,11 +275,14 @@ const WazuhDiscoverComponent = (props: WazuhDiscoverProps) => { {...dataGridProps} className={sideNavDocked ? 'dataGridDockedNav' : ''} toolbarVisibility={{ + showColumnSelector: { allowHide: false }, additionalControls: ( <> { const { @@ -97,7 +98,7 @@ const VulsPanelContent = ({ agent }) => { const value = severityStats?.find(v => v.key.toUpperCase() === severity.toUpperCase()) ?.doc_count || '0'; - return value ? `${value} ${severity}` : '0'; + return value ? `${formatUINumber(value)} ${severity}` : '0'; }; const renderSeverityStats = (severity, index) => { diff --git a/plugins/main/public/components/management/cluster/components/overview_cards.tsx b/plugins/main/public/components/management/cluster/components/overview_cards.tsx index 5b6db7ac3f..7379d7d8c5 100644 --- a/plugins/main/public/components/management/cluster/components/overview_cards.tsx +++ b/plugins/main/public/components/management/cluster/components/overview_cards.tsx @@ -15,6 +15,7 @@ import '../dashboard/cluster_dashboard.scss'; import { getPlugins } from '../../../../kibana-services'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; import { tFilter, tParsedIndexPattern } from '../../../common/data-source'; +import { formatUINumber } from '../../../../react-services/format-number'; interface OverviewCardsProps { goAgents: () => void; @@ -155,7 +156,7 @@ export const OverviewCards = ({ onClick={goNodes} style={{ height: 'auto' }} > - {nodesCount} + {formatUINumber(nodesCount)} ), @@ -187,7 +188,7 @@ export const OverviewCards = ({ onClick={goAgents} style={{ height: 'auto' }} > - {agentsCount} + {formatUINumber(agentsCount)} ), diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx index 2497b0af16..9118a32785 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx @@ -55,6 +55,7 @@ import VulsEvaluationFilter, { excludeUnderEvaluationFilter, getUnderEvaluationFilterValue, } from '../../common/components/vuls-evaluation-filter'; +import { DataGridVisibleColumnsSelector } from '../../../../common/wazuh-discover/components/visible-columns-selector'; const InventoryVulsComponent = () => { const { @@ -255,6 +256,7 @@ const InventoryVulsComponent = () => { {...dataGridProps} className={sideNavDocked ? 'dataGridDockedNav' : ''} toolbarVisibility={{ + showColumnSelector: { allowHide: false }, additionalControls: ( <> { } size='xs' iconType='exportAction' - color='primary' + color='text' isLoading={isExporting} className='euiDataGrid__controlBtn' onClick={onClickExportResults} > Export Formatted + + ), }} diff --git a/plugins/main/public/components/settings/api/api-table.js b/plugins/main/public/components/settings/api/api-table.js index a162154669..4ab3dc6c24 100644 --- a/plugins/main/public/components/settings/api/api-table.js +++ b/plugins/main/public/components/settings/api/api-table.js @@ -583,7 +583,7 @@ export const ApiTable = compose(withErrorBoundary)( { return ; }} diff --git a/plugins/main/public/controllers/overview/components/last-alerts-stat/last-alerts-stat.tsx b/plugins/main/public/controllers/overview/components/last-alerts-stat/last-alerts-stat.tsx index cf9258ca81..0e0e4435f4 100644 --- a/plugins/main/public/controllers/overview/components/last-alerts-stat/last-alerts-stat.tsx +++ b/plugins/main/public/controllers/overview/components/last-alerts-stat/last-alerts-stat.tsx @@ -20,6 +20,7 @@ import { FILTER_OPERATOR, PatternDataSourceFilterManager, } from '../../../../components/common/data-source/pattern/pattern-data-source-filter-manager'; +import { formatUINumber } from '../../../../react-services/format-number'; type SeverityKey = 'low' | 'medium' | 'high' | 'critical'; @@ -155,7 +156,7 @@ export function LastAlertsStat({ }} href={discoverLocation} > - {statValue} + {formatUINumber(statValue)} } diff --git a/plugins/main/public/react-services/format-number.ts b/plugins/main/public/react-services/format-number.ts new file mode 100644 index 0000000000..2d065407c4 --- /dev/null +++ b/plugins/main/public/react-services/format-number.ts @@ -0,0 +1,18 @@ +/* + * Wazuh app - Time and date functions + * Copyright (C) 2015-2022 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +export function formatUINumber(value) { + if (Number.isNaN(Number(value))) { + return value; + } + return Number(value).toLocaleString('en-US'); +} diff --git a/plugins/main/public/styles/layout.scss b/plugins/main/public/styles/layout.scss index eec1878679..2cb2ae21e8 100644 --- a/plugins/main/public/styles/layout.scss +++ b/plugins/main/public/styles/layout.scss @@ -88,6 +88,10 @@ margin-left: 7px; } +.wz-margin-left-4 { + margin-left: 4px; +} + .wz-margin-left-16 { margin-left: 16px; }