diff --git a/src/containers/Cluster/Cluster.tsx b/src/containers/Cluster/Cluster.tsx
index a72d9fcfdf..1628166bec 100644
--- a/src/containers/Cluster/Cluster.tsx
+++ b/src/containers/Cluster/Cluster.tsx
@@ -28,6 +28,7 @@ import type {
AdditionalTenantsProps,
} from '../../types/additionalProps';
import {EFlag} from '../../types/api/enums';
+import {uiFactory} from '../../uiFactory/uiFactory';
import {cn} from '../../utils/cn';
import {useAutoRefreshInterval, useTypedDispatch, useTypedSelector} from '../../utils/hooks';
import {useAppTitle} from '../App/AppTitleContext';
@@ -39,7 +40,13 @@ import {VersionsContainer} from '../Versions/Versions';
import {ClusterOverview} from './ClusterOverview/ClusterOverview';
import type {ClusterTab} from './utils';
-import {clusterTabs, clusterTabsIds, getClusterPath, isClusterTab} from './utils';
+import {
+ clusterTabs,
+ clusterTabsIds,
+ getClusterPath,
+ isClusterTab,
+ useShouldShowEventsTab,
+} from './utils';
import './Cluster.scss';
@@ -60,6 +67,7 @@ export function Cluster({
const isClusterDashboardAvailable = useClusterDashboardAvailable();
const shouldShowNetworkTable = useShouldShowClusterNetworkTable();
+ const shouldShowEventsTab = useShouldShowEventsTab();
const [autoRefreshInterval] = useAutoRefreshInterval();
@@ -99,12 +107,17 @@ export function Cluster({
}, [dispatch]);
const actualClusterTabs = React.useMemo(() => {
- if (shouldShowNetworkTable) {
- return clusterTabs;
- } else {
- return clusterTabs.filter((tab) => tab.id !== clusterTabsIds.network);
+ let tabs = clusterTabs;
+
+ if (!shouldShowNetworkTable) {
+ tabs = tabs.filter((tab) => tab.id !== clusterTabsIds.network);
}
- }, [shouldShowNetworkTable]);
+ if (!shouldShowEventsTab) {
+ tabs = tabs.filter((tab) => tab.id !== clusterTabsIds.events);
+ }
+
+ return tabs;
+ }, [shouldShowEventsTab, shouldShowNetworkTable]);
const getClusterTitle = () => {
if (infoLoading) {
@@ -240,6 +253,17 @@ export function Cluster({
>
+ {shouldShowEventsTab && (
+
+ {uiFactory.renderEvents?.()}
+
+ )}
+
(
@@ -257,13 +281,19 @@ function useClusterTab() {
const defaultTab = useTypedSelector((state) => state.cluster.defaultClusterTab);
const shouldShowNetworkTable = useShouldShowClusterNetworkTable();
+ const shouldShowEventsTab = useShouldShowEventsTab();
const match = useRouteMatch<{activeTab: string}>(routes.cluster);
const {activeTab: activeTabFromParams} = match?.params || {};
let activeTab: ClusterTab;
- if (!shouldShowNetworkTable && activeTabFromParams === clusterTabsIds.network) {
+ const shouldSwitchFromNetworkToDefault =
+ !shouldShowNetworkTable && activeTabFromParams === clusterTabsIds.network;
+ const shouldSwitchFromEventsToDefault =
+ !shouldShowEventsTab && activeTabFromParams === clusterTabsIds.events;
+
+ if (shouldSwitchFromNetworkToDefault || shouldSwitchFromEventsToDefault) {
activeTab = INITIAL_DEFAULT_CLUSTER_TAB;
} else if (isClusterTab(activeTabFromParams)) {
activeTab = activeTabFromParams;
diff --git a/src/containers/Cluster/i18n/en.json b/src/containers/Cluster/i18n/en.json
index 57ad7248fa..a78205562b 100644
--- a/src/containers/Cluster/i18n/en.json
+++ b/src/containers/Cluster/i18n/en.json
@@ -40,5 +40,13 @@
"context_cpu-description": "CPU load is calculated as the cumulative usage across all actor system pools on all nodes in the cluster",
"context_memory-description": "Memory usage is the total memory consumed by all nodes in the cluster",
"context_storage-description": "Storage usage is a cumulative usage of raw disk space of all media types",
- "context_network-description": "Network usage is the average outgoing bandwidth usage across all nodes in the cluster"
+ "context_network-description": "Network usage is the average outgoing bandwidth usage across all nodes in the cluster",
+
+ "tab_databases": "Databases",
+ "tab_nodes": "Nodes",
+ "tab_storage": "Storage",
+ "tab_network": "Network",
+ "tab_versions": "Versions",
+ "tab_tablets": "Tablets",
+ "tab_events": "Events"
}
diff --git a/src/containers/Cluster/utils.tsx b/src/containers/Cluster/utils.tsx
index 47c8fa77d9..5560aeacc5 100644
--- a/src/containers/Cluster/utils.tsx
+++ b/src/containers/Cluster/utils.tsx
@@ -1,7 +1,11 @@
import type {CreateHrefOptions} from '../../routes';
import routes, {createHref} from '../../routes';
+import {useClusterEventsAvailable} from '../../store/reducers/capabilities/hooks';
import type {ClusterGroupsStats} from '../../store/reducers/cluster/types';
import type {ValueOf} from '../../types/common';
+import {uiFactory} from '../../uiFactory/uiFactory';
+
+import i18n from './i18n';
export const clusterTabsIds = {
tenants: 'tenants',
@@ -10,36 +14,55 @@ export const clusterTabsIds = {
network: 'network',
versions: 'versions',
tablets: 'tablets',
+ events: 'events',
} as const;
export type ClusterTab = ValueOf;
const tenants = {
id: clusterTabsIds.tenants,
- title: 'Databases',
+ get title() {
+ return i18n('tab_databases');
+ },
};
const nodes = {
id: clusterTabsIds.nodes,
- title: 'Nodes',
+ get title() {
+ return i18n('tab_nodes');
+ },
};
const storage = {
id: clusterTabsIds.storage,
- title: 'Storage',
+ get title() {
+ return i18n('tab_storage');
+ },
};
const network = {
id: clusterTabsIds.network,
- title: 'Network',
+ get title() {
+ return i18n('tab_network');
+ },
};
const versions = {
id: clusterTabsIds.versions,
- title: 'Versions',
+ get title() {
+ return i18n('tab_versions');
+ },
};
const tablets = {
id: clusterTabsIds.tablets,
- title: 'Tablets',
+ get title() {
+ return i18n('tab_tablets');
+ },
+};
+const events = {
+ id: clusterTabsIds.events,
+ get title() {
+ return i18n('tab_events');
+ },
};
-export const clusterTabs = [tenants, nodes, storage, network, tablets, versions];
+export const clusterTabs = [tenants, nodes, storage, network, tablets, versions, events];
export function isClusterTab(tab: any): tab is ClusterTab {
return Object.values(clusterTabsIds).includes(tab);
@@ -58,3 +81,7 @@ export const getTotalStorageGroupsUsed = (groupStats: ClusterGroupsStats) => {
return acc;
}, 0);
};
+
+export function useShouldShowEventsTab() {
+ return useClusterEventsAvailable() && uiFactory.renderEvents;
+}
diff --git a/src/store/reducers/capabilities/hooks.ts b/src/store/reducers/capabilities/hooks.ts
index 2e58e5a9a8..d19f5820a9 100644
--- a/src/store/reducers/capabilities/hooks.ts
+++ b/src/store/reducers/capabilities/hooks.ts
@@ -157,3 +157,7 @@ export const useEditClusterFeatureAvailable = () => {
export const useDeleteClusterFeatureAvailable = () => {
return useGetMetaFeatureVersion('/meta/delete_cluster') >= 1;
};
+
+export const useClusterEventsAvailable = () => {
+ return useGetMetaFeatureVersion('/meta/events') >= 1;
+};
diff --git a/src/types/api/capabilities.ts b/src/types/api/capabilities.ts
index 739bce59b6..bba1c81228 100644
--- a/src/types/api/capabilities.ts
+++ b/src/types/api/capabilities.ts
@@ -49,4 +49,5 @@ export type MetaCapability =
| '/meta/stop_database'
| '/meta/create_cluster'
| '/meta/update_cluster'
- | '/meta/delete_cluster';
+ | '/meta/delete_cluster'
+ | '/meta/events';
diff --git a/src/uiFactory/types.ts b/src/uiFactory/types.ts
index 6818a39e84..7785948275 100644
--- a/src/uiFactory/types.ts
+++ b/src/uiFactory/types.ts
@@ -37,6 +37,8 @@ export interface UIFactory {
scrollContainerRef: React.RefObject;
}) => React.ReactNode;
+ renderEvents?: () => React.ReactNode;
+
healthcheck: {
getHealthckechViewTitles: GetHealthcheckViewTitles;
getHealthcheckViewsOrder: GetHealthcheckViewsOrder;