(
)}
/>
diff --git a/src/containers/Cluster/utils.tsx b/src/containers/Cluster/utils.tsx
index 4846786a61..8530250e61 100644
--- a/src/containers/Cluster/utils.tsx
+++ b/src/containers/Cluster/utils.tsx
@@ -1,5 +1,3 @@
-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';
@@ -75,10 +73,6 @@ export function isClusterTab(tab: any): tab is ClusterTab {
return Object.values(clusterTabsIds).includes(tab);
}
-export const getClusterPath = (activeTab?: ClusterTab, query = {}, options?: CreateHrefOptions) => {
- return createHref(routes.cluster, activeTab ? {activeTab} : undefined, query, options);
-};
-
export const getTotalStorageGroupsUsed = (groupStats: ClusterGroupsStats) => {
return Object.values(groupStats).reduce((acc, data) => {
Object.values(data).forEach((erasureStats) => {
diff --git a/src/containers/Clusters/columns.tsx b/src/containers/Clusters/columns.tsx
index 82e0806bb4..301c2ad926 100644
--- a/src/containers/Clusters/columns.tsx
+++ b/src/containers/Clusters/columns.tsx
@@ -14,6 +14,7 @@ import {
import {EntityStatus} from '../../components/EntityStatusNew/EntityStatus';
import {VersionsBar} from '../../components/VersionsBar/VersionsBar';
+import {getClusterPath} from '../../routes';
import type {PreparedCluster} from '../../store/reducers/clusters/types';
import {EFlag} from '../../types/api/enums';
import {uiFactory} from '../../uiFactory/uiFactory';
@@ -21,7 +22,7 @@ import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
import {formatNumber, formatStorageValuesToTb} from '../../utils/dataFormatters/dataFormatters';
import {createDeveloperUIMonitoringPageHref} from '../../utils/developerUI/developerUI';
import {getCleanBalancerValue} from '../../utils/parseBalancer';
-import {clusterTabsIds, getClusterPath} from '../Cluster/utils';
+import {clusterTabsIds} from '../Cluster/utils';
import {COLUMNS_NAMES, COLUMNS_TITLES} from './constants';
import i18n from './i18n';
@@ -43,17 +44,6 @@ function getTitleColumn({isEditClusterAvailable, isDeleteClusterAvailable}: Clus
defaultOrder: DataTable.ASCENDING,
sortAccessor: (row) => row.title || row.name,
render: ({row}) => {
- const {
- name: clusterName,
- use_embedded_ui: useEmbeddedUi,
- preparedBackend: backend,
- } = row;
-
- const clusterPath =
- useEmbeddedUi && backend
- ? createDeveloperUIMonitoringPageHref(backend)
- : getClusterPath(undefined, {backend, clusterName}, {withBasename: true});
-
const clusterStatus = row.cluster?.Overall;
const cleanedBalancer = row.balancer ? getCleanBalancerValue(row.balancer) : null;
@@ -98,11 +88,7 @@ function getTitleColumn({isEditClusterAvailable, isDeleteClusterAvailable}: Clus
};
const renderName = () => {
- return (
-
- {row.title || row.name}
-
- );
+ return ;
};
const renderStatus = () => {
@@ -153,6 +139,32 @@ function getTitleColumn({isEditClusterAvailable, isDeleteClusterAvailable}: Clus
} satisfies Column;
}
+interface ClusterNameProps {
+ row: PreparedCluster;
+}
+
+function ClusterName({row}: ClusterNameProps) {
+ const {
+ name: clusterName,
+ use_embedded_ui: useEmbeddedUi,
+ preparedBackend: backend,
+ settings,
+ } = row;
+ const clusterPath =
+ useEmbeddedUi && backend
+ ? createDeveloperUIMonitoringPageHref(backend)
+ : getClusterPath(
+ {environment: settings?.auth_service},
+ {backend, clusterName},
+ {withBasename: true},
+ );
+ return (
+
+ {row.title || row.name}
+
+ );
+}
+
const CLUSTERS_COLUMNS: Column[] = [
{
name: COLUMNS_NAMES.VERSIONS,
@@ -167,12 +179,7 @@ const CLUSTERS_COLUMNS: Column[] = [
return versions[0] || undefined;
},
render: ({row}) => {
- const {
- preparedVersions,
- versions = [],
- name: clusterName,
- preparedBackend: backend,
- } = row;
+ const {versions = []} = row;
const hasErrors = !versions.length || versions.some((item) => !item.version);
@@ -180,20 +187,7 @@ const CLUSTERS_COLUMNS: Column[] = [
return EMPTY_CELL;
}
- return (
- preparedVersions.length > 0 && (
-
-
-
- )
- );
+ return ;
},
},
{
@@ -359,6 +353,36 @@ const CLUSTERS_COLUMNS: Column[] = [
},
];
+interface VersionsProps {
+ row: PreparedCluster;
+}
+
+function Versions({row}: VersionsProps) {
+ const {
+ preparedVersions,
+ name: clusterName,
+ preparedBackend: backend,
+ settings,
+ use_embedded_ui: useEmbeddedUi,
+ } = row;
+ if (!preparedVersions.length) {
+ return null;
+ }
+ const clusterPath =
+ useEmbeddedUi && backend
+ ? createDeveloperUIMonitoringPageHref(backend)
+ : getClusterPath(
+ {activeTab: clusterTabsIds.versions, environment: settings?.auth_service},
+ {backend, clusterName},
+ {withBasename: true},
+ );
+ return (
+
+
+
+ );
+}
+
export function getClustersColumns(params: ClustersColumnsParams) {
return [getTitleColumn(params), ...CLUSTERS_COLUMNS];
}
diff --git a/src/containers/Header/Header.tsx b/src/containers/Header/Header.tsx
index 0cb76136f8..172f94c7ef 100644
--- a/src/containers/Header/Header.tsx
+++ b/src/containers/Header/Header.tsx
@@ -15,7 +15,8 @@ import {useHistory, useLocation} from 'react-router-dom';
import {getConnectToDBDialog} from '../../components/ConnectToDB/ConnectToDBDialog';
import {InternalLink} from '../../components/InternalLink';
-import {checkIsClustersPage, checkIsTenantPage} from '../../routes';
+import {checkIsClustersPage, checkIsTenantPage, getClusterPath} from '../../routes';
+import {environment} from '../../store';
import {
useAddClusterFeatureAvailable,
useDatabasesAvailable,
@@ -39,7 +40,6 @@ import {
useIsViewerUser,
} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
import {isAccessError} from '../../utils/response';
-import {getClusterPath} from '../Cluster/utils';
import {getBreadcrumbs} from './breadcrumbs';
import {headerKeyset} from './i18n';
@@ -98,6 +98,7 @@ function Header() {
...pageBreadcrumbsOptions,
singleClusterMode,
isViewerUser,
+ environment,
};
if (clusterTitle) {
@@ -156,7 +157,7 @@ function Header() {
action: () => {
onDeleteDB({clusterName, databaseData}).then((isDeleted) => {
if (isDeleted) {
- const path = getClusterPath('tenants');
+ const path = getClusterPath({activeTab: 'tenants'});
history.push(path);
}
});
diff --git a/src/containers/Header/breadcrumbs.tsx b/src/containers/Header/breadcrumbs.tsx
index d347dbab04..8e3ef8bc0a 100644
--- a/src/containers/Header/breadcrumbs.tsx
+++ b/src/containers/Header/breadcrumbs.tsx
@@ -7,7 +7,13 @@ import {
import {isNil} from 'lodash';
import {TabletIcon} from '../../components/TabletIcon/TabletIcon';
-import routes, {getPDiskPagePath, getStorageGroupPath} from '../../routes';
+import routes, {
+ getClusterPath,
+ getDefaultNodePath,
+ getPDiskPagePath,
+ getStorageGroupPath,
+ getTenantPath,
+} from '../../routes';
import type {
BreadcrumbsOptions,
ClusterBreadcrumbsOptions,
@@ -26,9 +32,7 @@ import {
TENANT_PAGES_IDS,
} from '../../store/reducers/tenant/constants';
import {CLUSTER_DEFAULT_TITLE, getTabletLabel} from '../../utils/constants';
-import {getClusterPath} from '../Cluster/utils';
-import {getDefaultNodePath} from '../Node/NodePages';
-import {TenantTabsGroups, getTenantPath} from '../Tenant/TenantPages';
+import {TenantTabsGroups} from '../Tenant/TenantPages';
import {headerKeyset} from './i18n';
@@ -63,7 +67,7 @@ const getClustersBreadcrumbs: GetBreadcrumbs = (opti
};
const getClusterBreadcrumbs: GetBreadcrumbs = (options, query = {}) => {
- const {clusterName, clusterTab, singleClusterMode, isViewerUser} = options;
+ const {clusterName, clusterTab, singleClusterMode, isViewerUser, environment} = options;
if (!isViewerUser) {
return [];
@@ -77,7 +81,7 @@ const getClusterBreadcrumbs: GetBreadcrumbs = (option
breadcrumbs.push({
text: clusterName || CLUSTER_DEFAULT_TITLE,
- link: getClusterPath(clusterTab, query),
+ link: getClusterPath({activeTab: clusterTab, environment}, query),
icon: ,
});
@@ -114,7 +118,9 @@ const getNodeBreadcrumbs: GetBreadcrumbs = (options, que
const lastItem = {
text,
- link: nodeId ? getDefaultNodePath(nodeId, {database, ...query}, nodeActiveTab) : undefined,
+ link: nodeId
+ ? getDefaultNodePath({id: nodeId, activeTab: nodeActiveTab}, {database, ...query})
+ : undefined,
icon: getNodeIcon(nodeRole),
};
diff --git a/src/containers/Heatmap/HeatmapCanvas/HeatmapCanvas.js b/src/containers/Heatmap/HeatmapCanvas/HeatmapCanvas.js
index c955b97f49..1409e4878f 100644
--- a/src/containers/Heatmap/HeatmapCanvas/HeatmapCanvas.js
+++ b/src/containers/Heatmap/HeatmapCanvas/HeatmapCanvas.js
@@ -3,10 +3,9 @@ import React from 'react';
import throttle from 'lodash/throttle';
import PropTypes from 'prop-types';
-import {getTabletPagePath} from '../../../routes';
+import {useTabletPagePath} from '../../../routes';
import {basename as appBasename} from '../../../store/index';
import {cn} from '../../../utils/cn';
-import {useDatabaseFromQuery} from '../../../utils/hooks/useDatabaseFromQuery';
const b = cn('heatmap');
const defaultDimensions = {width: 0, height: 0};
@@ -19,7 +18,8 @@ export const HeatmapCanvas = (props) => {
const {tablets} = props;
const canvasRef = React.useRef(null);
const containerRef = React.useRef(null);
- const database = useDatabaseFromQuery();
+
+ const getTabletPagePath = useTabletPagePath();
function drawTablet(ctx) {
return (tablet, index) => {
@@ -92,7 +92,7 @@ export const HeatmapCanvas = (props) => {
const generateTabletExternalLink = (tablet) => {
const {TabletId: id} = tablet;
const hostname = window.location.hostname;
- const path = getTabletPagePath(id, {database});
+ const path = getTabletPagePath(id);
const protocol = 'https://';
const href = [hostname, appBasename, path]
.map((item) => (item.startsWith('/') ? item.slice(1) : item))
diff --git a/src/containers/Node/Node.tsx b/src/containers/Node/Node.tsx
index 09421b36fd..eec30bd049 100644
--- a/src/containers/Node/Node.tsx
+++ b/src/containers/Node/Node.tsx
@@ -12,7 +12,7 @@ import {FullNodeViewer} from '../../components/FullNodeViewer/FullNodeViewer';
import {InfoViewerSkeleton} from '../../components/InfoViewerSkeleton/InfoViewerSkeleton';
import {InternalLink} from '../../components/InternalLink';
import {PageMetaWithAutorefresh} from '../../components/PageMeta/PageMeta';
-import routes from '../../routes';
+import routes, {getDefaultNodePath} from '../../routes';
import {
useCapabilitiesLoaded,
useConfigAvailable,
@@ -30,7 +30,7 @@ import {PaginatedStorage} from '../Storage/PaginatedStorage';
import {Tablets} from '../Tablets/Tablets';
import type {NodeTab} from './NodePages';
-import {NODE_TABS, getDefaultNodePath, nodePageQueryParams, nodePageTabSchema} from './NodePages';
+import {NODE_TABS, nodePageQueryParams, nodePageTabSchema} from './NodePages';
import NodeStructure from './NodeStructure/NodeStructure';
import {Threads} from './Threads/Threads';
import i18n from './i18n';
@@ -221,7 +221,10 @@ function NodePageContent({
{tabs.map(({id, title}) => {
- const path = getDefaultNodePath(nodeId, {database}, id as NodeTab);
+ const path = getDefaultNodePath(
+ {id: nodeId, activeTab: id as NodeTab},
+ {database},
+ );
return (
diff --git a/src/containers/Node/NodePages.ts b/src/containers/Node/NodePages.ts
index 2b8a5a5638..b3741a9f68 100644
--- a/src/containers/Node/NodePages.ts
+++ b/src/containers/Node/NodePages.ts
@@ -2,7 +2,6 @@ import {StringParam} from 'use-query-params';
import {z} from 'zod';
import type {QueryParamsTypeFromQueryObject} from '../../routes';
-import routes, {createHref} from '../../routes';
import type {ValueOf} from '../../types/common';
import i18n from './i18n';
@@ -58,19 +57,4 @@ export const nodePageQueryParams = {
vdiskId: StringParam,
};
-type NodePageQuery = QueryParamsTypeFromQueryObject;
-
-export function getDefaultNodePath(
- nodeId: string | number,
- query: NodePageQuery = {},
- activeTab?: NodeTab,
-) {
- return createHref(
- routes.node,
- {
- id: nodeId,
- activeTab,
- },
- query,
- );
-}
+export type NodePageQuery = QueryParamsTypeFromQueryObject;
diff --git a/src/containers/PDiskPage/PDiskSpaceDistribution/PDiskSpaceDistribution.tsx b/src/containers/PDiskPage/PDiskSpaceDistribution/PDiskSpaceDistribution.tsx
index 5c39803d94..293595bbae 100644
--- a/src/containers/PDiskPage/PDiskSpaceDistribution/PDiskSpaceDistribution.tsx
+++ b/src/containers/PDiskPage/PDiskSpaceDistribution/PDiskSpaceDistribution.tsx
@@ -5,7 +5,7 @@ import {InfoViewer} from '../../../components/InfoViewer';
import {InternalLink} from '../../../components/InternalLink';
import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
import {VDiskInfo} from '../../../components/VDiskInfo/VDiskInfo';
-import {getVDiskPagePath} from '../../../routes';
+import {useVDiskPagePath} from '../../../routes';
import type {
EmptySlotData,
LogSlotData,
@@ -32,6 +32,7 @@ interface PDiskSpaceDistributionProps {
}
export function PDiskSpaceDistribution({data}: PDiskSpaceDistributionProps) {
+ const getVDiskPagePath = useVDiskPagePath();
const {SlotItems} = data;
const {PDiskId, NodeId} = data;
@@ -40,7 +41,15 @@ export function PDiskSpaceDistribution({data}: PDiskSpaceDistributionProps) {
const renderSlots = () => {
return SlotItems?.map((item, index) => {
- return ;
+ return (
+
+ );
});
};
@@ -72,17 +81,19 @@ interface SlotProps {
pDiskId?: string | number;
nodeId?: string | number;
+ getVDiskPagePath?: (
+ params: {nodeId: string | number | undefined; vDiskId: string | undefined},
+ query?: {activeTab?: string},
+ ) => string | undefined;
}
-function Slot({item, nodeId}: SlotProps) {
+function Slot({item, nodeId, getVDiskPagePath}: SlotProps) {
const renderContent = () => {
if (isVDiskSlot(item)) {
- const vDiskPagePath = valueIsDefined(item.SlotData?.StringifiedId)
- ? getVDiskPagePath({
- nodeId,
- vDiskId: item.SlotData.StringifiedId,
- })
- : undefined;
+ const vDiskPagePath = getVDiskPagePath?.({
+ nodeId,
+ vDiskId: item.SlotData.StringifiedId,
+ });
return (
{
};
function GroupId({id}: {id: string | number}) {
- const database = useDatabaseFromQuery();
+ const getStorageGroupPath = useStorageGroupPath();
return (
diff --git a/src/containers/Tablet/Tablet.tsx b/src/containers/Tablet/Tablet.tsx
index da5964550c..3a140ec0bc 100644
--- a/src/containers/Tablet/Tablet.tsx
+++ b/src/containers/Tablet/Tablet.tsx
@@ -13,7 +13,7 @@ import {ResponseError} from '../../components/Errors/ResponseError';
import {InternalLink} from '../../components/InternalLink';
import {LoaderWrapper} from '../../components/LoaderWrapper/LoaderWrapper';
import {PageMetaWithAutorefresh} from '../../components/PageMeta/PageMeta';
-import {getTabletPagePath, tabletPageQueryParams} from '../../routes';
+import {tabletPageQueryParams, useTabletPagePath} from '../../routes';
import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
import {tabletApi} from '../../store/reducers/tablet';
import {EFlag} from '../../types/api/enums';
@@ -69,7 +69,7 @@ export function Tablet() {
const database = queryDatabase?.toString();
const [autoRefreshInterval] = useAutoRefreshInterval();
const {currentData, isFetching, error} = tabletApi.useGetTabletQuery(
- {id, database: queryDatabase ?? undefined, followerId: queryFollowerId ?? undefined},
+ {id, database, followerId: queryFollowerId ?? undefined},
{pollingInterval: autoRefreshInterval},
);
@@ -114,9 +114,7 @@ export function Tablet() {
{error ? : null}
- {currentData ? (
-
- ) : null}
+ {currentData ? : null}