diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45522be494..c14c8b6c41 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,7 +11,7 @@ All notable changes to the Wazuh app project will be documented in this file.
### Changed
-- Removed embedded discover [#6120](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6120) [#6235](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6235)
+- Removed embedded discover [#6120](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6120) [#6235](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6235) [#6254](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6254)
- Develop logic of a new index for the fim module [#6227](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6227)
- Allow editing groups for an agent from Endpoints Summary [#6250](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6250)
diff --git a/plugins/main/public/components/common/data-grid/use-data-grid.test.tsx b/plugins/main/public/components/common/data-grid/use-data-grid.test.tsx
new file mode 100644
index 0000000000..3d6cdc7d5f
--- /dev/null
+++ b/plugins/main/public/components/common/data-grid/use-data-grid.test.tsx
@@ -0,0 +1,23 @@
+import { useDataGrid, tDataGridProps } from './use-data-grid';
+import { renderHook } from '@testing-library/react-hooks';
+import React from 'react';
+
+describe('useDataGrid hook', () => {
+ it('should return override the numbers of rows per page', () => {
+
+ const dataGridProps: tDataGridProps = {
+ indexPattern: 'mocked-index-pattern',
+ results: {},
+ defaultColumns: [],
+ DocViewInspectButton: () =>
,
+ ariaLabelledBy: '',
+ pagination: {
+ pageSize: 10,
+ pageSizeOptions: [10, 20, 30],
+ }
+ }
+ const { result } = renderHook(() => useDataGrid(dataGridProps));
+ expect(result.current.pagination.pageSize).toEqual(10);
+ expect(result.current.pagination.pageSizeOptions).toEqual([10, 20, 30]);
+ })
+});
\ No newline at end of file
diff --git a/plugins/main/public/components/common/data-grid/use-data-grid.ts b/plugins/main/public/components/common/data-grid/use-data-grid.ts
index a27cb3d2d0..a4ca8f9312 100644
--- a/plugins/main/public/components/common/data-grid/use-data-grid.ts
+++ b/plugins/main/public/components/common/data-grid/use-data-grid.ts
@@ -6,6 +6,7 @@ import { parseData, getFieldFormatted, parseColumns } from './data-grid-service'
import { IndexPattern } from '../../../../../../src/plugins/data/common';
const MAX_ENTRIES_PER_QUERY = 10000;
+const DEFAULT_PAGE_SIZE_OPTIONS = [20, 50, 100];
export type tDataGridColumn = {
render?: (value: any) => string | React.ReactNode;
@@ -17,11 +18,12 @@ type tDataGridProps = {
defaultColumns: tDataGridColumn[];
DocViewInspectButton: ({ rowIndex }: EuiDataGridCellValueElementProps) => React.JSX.Element
ariaLabelledBy: string;
+ pagination?: Partial;
};
export const useDataGrid = (props: tDataGridProps): EuiDataGridProps => {
- const { indexPattern, DocViewInspectButton, results, defaultColumns } = props;
+ const { indexPattern, DocViewInspectButton, results, defaultColumns, pagination: defaultPagination } = props;
/** Columns **/
const [columns, setColumns] = useState(defaultColumns);
const [columnVisibility, setVisibility] = useState(() =>
@@ -40,7 +42,7 @@ export const useDataGrid = (props: tDataGridProps): EuiDataGridProps => {
const [sortingColumns, setSortingColumns] = useState(defaultSorting);
const onSort = (sortingColumns) => { setSortingColumns(sortingColumns) };
/** Pagination **/
- const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 20 });
+ const [pagination, setPagination] = useState(defaultPagination || { pageIndex: 0, pageSize: 20, pageSizeOptions: DEFAULT_PAGE_SIZE_OPTIONS });
const onChangeItemsPerPage = useMemo(() => (pageSize) =>
setPagination((pagination) => ({
...pagination,
@@ -95,7 +97,6 @@ export const useDataGrid = (props: tDataGridProps): EuiDataGridProps => {
sorting: { columns: sortingColumns, onSort },
pagination: {
...pagination,
- pageSizeOptions: [20, 50, 100],
onChangeItemsPerPage: onChangeItemsPerPage,
onChangePage: onChangePage,
}
diff --git a/plugins/main/public/components/common/hooks/index.ts b/plugins/main/public/components/common/hooks/index.ts
index e3ce7584c7..a4cd7dbf91 100644
--- a/plugins/main/public/components/common/hooks/index.ts
+++ b/plugins/main/public/components/common/hooks/index.ts
@@ -28,3 +28,4 @@ export * from './use_async_action_run_on_start';
export { useEsSearch } from './use-es-search';
export { useValueSuggestion, IValueSuggestion } from './use-value-suggestion';
export * from './use-state-storage';
+export * from './useDockedSideNav';
diff --git a/plugins/main/public/components/common/hooks/useDockedSideNav.tsx b/plugins/main/public/components/common/hooks/useDockedSideNav.tsx
new file mode 100644
index 0000000000..489536b4d6
--- /dev/null
+++ b/plugins/main/public/components/common/hooks/useDockedSideNav.tsx
@@ -0,0 +1,20 @@
+import { useEffect, useState } from 'react';
+import { getChrome } from '../../../kibana-services';
+
+export const useDockedSideNav = () => {
+ const [sideNavDocked, setSideNavDocked] = useState(false);
+
+ useEffect(() => {
+ const isNavDrawerSubscription = getChrome()
+ .getIsNavDrawerLocked$()
+ .subscribe((value: boolean) => {
+ setSideNavDocked(value);
+ });
+
+ return () => {
+ isNavDrawerSubscription.unsubscribe();
+ };
+ }, []);
+
+ return sideNavDocked;
+};
diff --git a/plugins/main/public/components/common/modules/modules-defaults.js b/plugins/main/public/components/common/modules/modules-defaults.js
index 8c3fd06459..3d18631800 100644
--- a/plugins/main/public/components/common/modules/modules-defaults.js
+++ b/plugins/main/public/components/common/modules/modules-defaults.js
@@ -19,10 +19,13 @@ import ButtonModuleExploreAgent from '../../../controllers/overview/components/o
import { ButtonModuleGenerateReport } from '../modules/buttons';
import { OfficePanel } from '../../overview/office-panel';
import { GitHubPanel } from '../../overview/github-panel';
-import { DashboardVuls, InventoryVuls } from '../../overview/vulnerabilities';
+import { DashboardVuls, InventoryVuls } from '../../overview/vulnerabilities'
import { withModuleNotForAgent } from '../hocs';
+import WazuhDiscover from '../wazuh-discover/wz-discover';
+import { threatHuntingColumns } from '../wazuh-discover/config/data-grid-columns';
import { DashboardFim } from '../../overview/fim/dashboard/dashboard';
import { InventoryFim } from '../../overview/fim/inventory/inventory';
+import React from 'react';
const DashboardTab = {
id: 'dashboard',
@@ -30,12 +33,26 @@ const DashboardTab = {
buttons: [ButtonModuleExploreAgent, ButtonModuleGenerateReport],
component: Dashboard,
};
+const ALERTS_INDEX_PATTERN = 'wazuh-alerts-*';
+const DEFAULT_INDEX_PATTERN = ALERTS_INDEX_PATTERN;
+
+const renderDiscoverTab = (indexName = DEFAULT_INDEX_PATTERN, columns) => {
+ return {
+ id: 'events',
+ name: 'Events',
+ buttons: [ButtonModuleExploreAgent],
+ component: () =>
+ ,
+ }
+};
+
const EventsTab = {
- id: 'events',
- name: 'Events',
- buttons: [ButtonModuleExploreAgent],
- component: Events,
+ id: 'events',
+ name: 'Events',
+ buttons: [ButtonModuleExploreAgent],
+ component: Events,
};
+
const RegulatoryComplianceTabs = [
DashboardTab,
{
@@ -49,8 +66,8 @@ const RegulatoryComplianceTabs = [
export const ModulesDefaults = {
general: {
- init: 'dashboard',
- tabs: [DashboardTab, EventsTab],
+ init: 'events',
+ tabs: [DashboardTab,renderDiscoverTab(DEFAULT_INDEX_PATTERN, threatHuntingColumns)],
availableFor: ['manager', 'agent'],
},
fim: {
diff --git a/plugins/main/public/components/common/search-bar/search-bar-service.ts b/plugins/main/public/components/common/search-bar/search-bar-service.ts
index d7c546ccff..f7457faf4f 100644
--- a/plugins/main/public/components/common/search-bar/search-bar-service.ts
+++ b/plugins/main/public/components/common/search-bar/search-bar-service.ts
@@ -17,10 +17,14 @@ export interface SearchParams {
direction: 'asc' | 'desc';
}[];
};
+ dateRange?: {
+ from: string;
+ to: string;
+ };
}
export const search = async (params: SearchParams): Promise => {
- const { indexPattern, filters = [], query, pagination, sorting, fields } = params;
+ const { indexPattern, filters: defaultFilters = [], query, pagination, sorting, fields } = params;
if(!indexPattern){
return;
}
@@ -31,6 +35,24 @@ export const search = async (params: SearchParams): Promise {
return {
@@ -128,3 +129,24 @@ export const getDiscoverPanels = (
}
};
}
+
+export const histogramChartInput = (indexPatternName: string, filters, query, dateRangeFrom, dateRangeTo) => ({
+ viewMode: ViewMode.VIEW,
+ panels: getDiscoverPanels(indexPatternName),
+ isFullScreenMode: false,
+ filters: filters ?? [],
+ useMargins: false,
+ id: 'wz-discover-events-histogram',
+ timeRange: {
+ from: dateRangeFrom,
+ to: dateRangeTo,
+ },
+ title: 'Discover Events Histogram',
+ description: 'Histogram of events by date',
+ query: query,
+ refreshConfig: {
+ pause: false,
+ value: 15,
+ },
+ hidePanelTitles: true,
+ })
\ No newline at end of file
diff --git a/plugins/main/public/components/common/wazuh-discover/discover.scss b/plugins/main/public/components/common/wazuh-discover/discover.scss
new file mode 100644
index 0000000000..db82845c2a
--- /dev/null
+++ b/plugins/main/public/components/common/wazuh-discover/discover.scss
@@ -0,0 +1,28 @@
+.discoverContainer {
+ height: calc(100vh - 104px);
+
+ .euiDataGrid--fullScreen {
+ height: calc(100vh - 49px);
+ bottom: 0;
+ top: auto;
+ }
+
+ .discoverDataGrid {
+ height: calc(100vh - 496px);
+ }
+
+ .discoverChartContainer {
+ min-height: 234px;
+ .dshLayout-isMaximizedPanel {
+ top: 0;
+ left: 0;
+ min-height: calc(100vh - 49px);
+ position: fixed;
+ z-index: 9999;
+ }
+ }
+}
+
+.headerIsExpanded .discoverContainer {
+ height: calc(100vh - 153px);
+}
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 b33ac2ed87..b22c58d0d4 100644
--- a/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx
+++ b/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx
@@ -16,7 +16,7 @@ import {
EuiPanel,
} from '@elastic/eui';
import { IntlProvider } from 'react-intl';
-import { IndexPattern } from '../../../../../../src/plugins/data/common';
+import { Filter, IndexPattern } from '../../../../../../src/plugins/data/common';
import { SearchResponse } from '../../../../../../src/core/server';
import { useDocViewer } from '../doc-viewer';
import DocViewer from '../doc-viewer/doc-viewer';
@@ -24,21 +24,15 @@ import { DiscoverNoResults } from '../../overview/vulnerabilities/common/compone
import { LoadingSpinner } from '../../overview/vulnerabilities/common/components/loading_spinner';
import { useDataGrid, tDataGridColumn, exportSearchToCSV } from '../data-grid';
import { ErrorHandler, ErrorFactory, HttpError } from '../../../react-services/error-management';
-import { withErrorBoundary } from '../hocs';
import { HitsCounter } from '../../../kibana-integrations/discover/application/components/hits_counter';
import { formatNumWithCommas } from '../../../kibana-integrations/discover/application/helpers';
import useSearchBar from '../search-bar/use-search-bar';
import { search } from '../search-bar';
import { getPlugins } from '../../../kibana-services';
-import { ViewMode } from '../../../../../../src/plugins/embeddable/public';
-import { getDiscoverPanels } from './config/chart';
+import { histogramChartInput } from './config/histogram-chart';
+import { useDockedSideNav } from '../hooks/useDockedSideNav'
const DashboardByRenderer = getPlugins().dashboard.DashboardContainerByValueRenderer;
-
-/**
- * ToDo:
- * - add possibility to customize column render
- * - add save query feature
- */
+import './discover.scss';
export const MAX_ENTRIES_PER_QUERY = 10000;
@@ -49,16 +43,14 @@ type WazuhDiscoverProps = {
const WazuhDiscover = (props: WazuhDiscoverProps) => {
const { indexPatternName, tableColumns: defaultTableColumns } = props
- const { searchBarProps } = useSearchBar({
- defaultIndexPatternID: indexPatternName,
- })
- const { isLoading, filters, query, indexPatterns } = searchBarProps;
+ const [sidebarDocked, setSidebarDocked] = useState(false);
const SearchBar = getPlugins().data.ui.SearchBar;
const [results, setResults] = useState({} as SearchResponse);
const [inspectedHit, setInspectedHit] = useState(undefined);
const [indexPattern, setIndexPattern] = useState(undefined);
const [isSearching, setIsSearching] = useState(false);
const [isExporting, setIsExporting] = useState(false);
+ const sideNavDocked = useDockedSideNav();
const onClickInspectDoc = useMemo(() => (index: number) => {
const rowClicked = results.hits.hits[index];
@@ -78,12 +70,22 @@ const WazuhDiscover = (props: WazuhDiscoverProps) => {
);
};
+ const { searchBarProps } = useSearchBar({
+ defaultIndexPatternID: indexPatternName,
+ })
+ const { isLoading, filters, query, indexPatterns, dateRangeFrom, dateRangeTo } = searchBarProps;
+
const dataGridProps = useDataGrid({
ariaLabelledBy: 'Discover events table',
defaultColumns: defaultTableColumns,
results,
indexPattern: indexPattern as IndexPattern,
- DocViewInspectButton
+ DocViewInspectButton,
+ pagination: {
+ pageIndex: 0,
+ pageSize: 15,
+ pageSizeOptions: [15, 25, 50, 100],
+ }
})
const { pagination, sorting, columnVisibility } = dataGridProps;
@@ -95,23 +97,31 @@ const WazuhDiscover = (props: WazuhDiscoverProps) => {
useEffect(() => {
if (!isLoading) {
+ setIsSearching(true);
setIndexPattern(indexPatterns?.[0] as IndexPattern);
search({
indexPattern: indexPatterns?.[0] as IndexPattern,
filters,
query,
pagination,
- sorting
+ sorting,
+ dateRange: {
+ from: dateRangeFrom,
+ to: dateRangeTo,
+ }
}).then((results) => {
setResults(results);
setIsSearching(false);
}).catch((error) => {
- const searchError = ErrorFactory.create(HttpError, { error, message: 'Error fetching vulnerabilities' })
+ const searchError = ErrorFactory.create(HttpError, { error, message: 'Error fetching data' })
ErrorHandler.handleError(searchError);
setIsSearching(false);
})
}
- }, [JSON.stringify(searchBarProps), JSON.stringify(pagination), JSON.stringify(sorting)]);
+ },
+ [JSON.stringify(searchBarProps),
+ JSON.stringify(pagination),
+ JSON.stringify(sorting)]);
const timeField = indexPattern?.timeFieldName ? indexPattern.timeFieldName : undefined;
@@ -141,7 +151,7 @@ const WazuhDiscover = (props: WazuhDiscoverProps) => {
return (
{
}
- {isSearching ?
- : null}
- {!isLoading && !isSearching && results?.hits?.total === 0 ?
+ {!isLoading && results?.hits?.total === 0 ?
: null}
- {!isLoading && !isSearching && results?.hits?.total > 0 ? (
+ {!isLoading && results?.hits?.total > 0 ? (
<>
-
+
-
- { }}
- tooltip={results?.hits?.total && results?.hits?.total > MAX_ENTRIES_PER_QUERY ? {
- ariaLabel: 'Warning',
- content: `The query results has exceeded the limit of 10,000 hits. To provide a better experience the table only shows the first ${formatNumWithCommas(MAX_ENTRIES_PER_QUERY)} hits.`,
- iconType: 'alert',
- position: 'top'
- } : undefined}
- />
-
- Export Formated
-
- >
- )
- }}
- />
- >) : null}
+
+
+ { }}
+ tooltip={results?.hits?.total && results?.hits?.total > MAX_ENTRIES_PER_QUERY ? {
+ ariaLabel: 'Warning',
+ content: `The query results has exceeded the limit of 10,000 hits. To provide a better experience the table only shows the first ${formatNumWithCommas(MAX_ENTRIES_PER_QUERY)} hits.`,
+ iconType: 'alert',
+ position: 'top'
+ } : undefined}
+ />
+
+ Export Formated
+
+ >
+ )
+ }}
+ />
+
+ >
+ ) : null}
{inspectedHit && (
setInspectedHit(undefined)} size="m">
@@ -240,6 +234,6 @@ const WazuhDiscover = (props: WazuhDiscoverProps) => {
);
-}
+};
export default WazuhDiscover;
\ No newline at end of file
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 b92b1d9ae9..f4abe37010 100644
--- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx
+++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx
@@ -31,7 +31,7 @@ import { LoadingSpinner } from '../../common/components/loading_spinner';
// common components/hooks
import DocViewer from '../../../../common/doc-viewer/doc-viewer';
import useSearchBar from '../../../../common/search-bar/use-search-bar';
-import { useAppConfig } from '../../../../common/hooks';
+import { useAppConfig, useDockedSideNav } from '../../../../common/hooks';
import { useDataGrid } from '../../../../common/data-grid/use-data-grid';
import { useDocViewer } from '../../../../common/doc-viewer/use-doc-viewer';
import { withErrorBoundary } from '../../../../common/hocs';
@@ -56,6 +56,7 @@ const InventoryVulsComponent = () => {
);
const [isSearching, setIsSearching] = useState(false);
const [isExporting, setIsExporting] = useState(false);
+ const sideNavDocked = useDockedSideNav();
const onClickInspectDoc = useMemo(
() => (index: number) => {
@@ -198,6 +199,7 @@ const InventoryVulsComponent = () => {
isSuccess &&
results?.hits?.total > 0 ? (