ydb-embedded-ui 20.3.0 → 20.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Drawer/Drawer.d.ts +2 -1
- package/dist/components/Drawer/Drawer.js +4 -4
- package/dist/components/Drawer/Drawer.js.map +1 -1
- package/dist/components/EntityStatus/EntityStatus.d.ts +4 -1
- package/dist/components/EntityStatus/EntityStatus.js +2 -2
- package/dist/components/EntityStatus/EntityStatus.js.map +1 -1
- package/dist/components/PDiskInfo/i18n/index.d.ts +1 -1
- package/dist/components/TenantNameWrapper/utils.d.ts +1 -1
- package/dist/components/TenantNameWrapper/utils.js.map +1 -1
- package/dist/containers/Cluster/Cluster.js +1 -1
- package/dist/containers/Cluster/Cluster.js.map +1 -1
- package/dist/containers/Cluster/ClusterDrawerHealthcheck.d.ts +8 -0
- package/dist/containers/Cluster/ClusterDrawerHealthcheck.js +71 -9
- package/dist/containers/Cluster/ClusterDrawerHealthcheck.js.map +1 -1
- package/dist/containers/Cluster/ClusterInfo/ClusterInfo.d.ts +2 -1
- package/dist/containers/Cluster/ClusterInfo/ClusterInfo.js +2 -2
- package/dist/containers/Cluster/ClusterInfo/ClusterInfo.js.map +1 -1
- package/dist/containers/Cluster/ClusterOverview/ClusterOverview.d.ts +1 -0
- package/dist/containers/Cluster/ClusterOverview/ClusterOverview.js.map +1 -1
- package/dist/containers/Cluster/ClusterOverview/components/BridgeInfoTable.d.ts +2 -0
- package/dist/containers/Cluster/ClusterOverview/components/BridgeInfoTable.js +54 -103
- package/dist/containers/Cluster/ClusterOverview/components/BridgeInfoTable.js.map +1 -1
- package/dist/containers/Cluster/ClusterOverview/components/BridgeInfoTable.scss +24 -89
- package/dist/containers/Cluster/ClusterOverview/components/bridgePileHealthcheck.d.ts +13 -0
- package/dist/containers/Cluster/ClusterOverview/components/bridgePileHealthcheck.js +94 -0
- package/dist/containers/Cluster/ClusterOverview/components/bridgePileHealthcheck.js.map +1 -0
- package/dist/containers/Cluster/i18n/en.json +8 -5
- package/dist/containers/Cluster/i18n/index.d.ts +1 -1
- package/dist/containers/Clusters/constants.d.ts +1 -1
- package/dist/containers/Header/DatabaseRightControls.d.ts +4 -2
- package/dist/containers/Header/DatabaseRightControls.js +2 -2
- package/dist/containers/Header/DatabaseRightControls.js.map +1 -1
- package/dist/containers/Header/Header.js +2 -2
- package/dist/containers/Header/Header.js.map +1 -1
- package/dist/containers/Header/HeaderActionsMenu.d.ts +4 -2
- package/dist/containers/Header/HeaderActionsMenu.js +19 -2
- package/dist/containers/Header/HeaderActionsMenu.js.map +1 -1
- package/dist/containers/Header/HeaderRightControls.d.ts +4 -2
- package/dist/containers/Header/HeaderRightControls.js +2 -2
- package/dist/containers/Header/HeaderRightControls.js.map +1 -1
- package/dist/containers/Header/hooks/useClusterData.d.ts +1 -0
- package/dist/containers/Header/hooks/useHeaderData.d.ts +1 -0
- package/dist/containers/Header/hooks/useHeaderData.js +1 -0
- package/dist/containers/Header/hooks/useHeaderData.js.map +1 -1
- package/dist/containers/Header/hooks/useSharedDatabasePath.d.ts +10 -0
- package/dist/containers/Header/hooks/useSharedDatabasePath.js +102 -0
- package/dist/containers/Header/hooks/useSharedDatabasePath.js.map +1 -0
- package/dist/containers/Header/i18n/en.json +1 -0
- package/dist/containers/Header/i18n/index.d.ts +1 -1
- package/dist/containers/Operations/constants.d.ts +1 -1
- package/dist/containers/Storage/PaginatedStorageGroupsTable/columns/i18n/index.d.ts +1 -1
- package/dist/containers/Tablets/Tablets.js +3 -3
- package/dist/containers/Tablets/Tablets.js.map +1 -1
- package/dist/containers/Tablets/TabletsTable.js +17 -27
- package/dist/containers/Tablets/TabletsTable.js.map +1 -1
- package/dist/containers/Tablets/i18n/en.json +3 -0
- package/dist/containers/Tablets/i18n/index.d.ts +1 -1
- package/dist/containers/Tablets/useTabletQueryParams.d.ts +8 -0
- package/dist/containers/Tablets/useTabletQueryParams.js +40 -0
- package/dist/containers/Tablets/useTabletQueryParams.js.map +1 -0
- package/dist/containers/Tablets/utils.d.ts +11 -0
- package/dist/containers/Tablets/utils.js +23 -0
- package/dist/containers/Tablets/utils.js.map +1 -0
- package/dist/containers/Tenant/Healthcheck/Healthcheck.d.ts +6 -1
- package/dist/containers/Tenant/Healthcheck/Healthcheck.js +5 -5
- package/dist/containers/Tenant/Healthcheck/Healthcheck.js.map +1 -1
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckDrawer.d.ts +2 -1
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckDrawer.js +2 -2
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckDrawer.js.map +1 -1
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssue.d.ts +5 -1
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssue.js +5 -3
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssue.js.map +1 -1
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssueDetails/ComputeLocation.d.ts +1 -1
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssueDetails/ComputeLocation.js +11 -2
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssueDetails/ComputeLocation.js.map +1 -1
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssueDetails/NodeInfo.js +2 -0
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssueDetails/NodeInfo.js.map +1 -1
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssueDetails/StorageLocation.js +4 -1
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssueDetails/StorageLocation.js.map +1 -1
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssues.d.ts +5 -1
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssues.js +2 -2
- package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssues.js.map +1 -1
- package/dist/containers/Tenant/Healthcheck/i18n/en.json +2 -1
- package/dist/containers/Tenant/Healthcheck/i18n/index.d.ts +1 -1
- package/dist/containers/Tenant/Query/SavedQueries/SavedQueries.scss +2 -0
- package/dist/containers/VDiskPage/i18n/index.d.ts +1 -1
- package/dist/routes.d.ts +4 -1
- package/dist/routes.js +3 -2
- package/dist/routes.js.map +1 -1
- package/dist/services/api/viewer.d.ts +3 -1
- package/dist/services/api/viewer.js +3 -2
- package/dist/services/api/viewer.js.map +1 -1
- package/dist/store/reducers/cluster/cluster.d.ts +2 -1
- package/dist/store/reducers/cluster/cluster.js +1 -0
- package/dist/store/reducers/cluster/cluster.js.map +1 -1
- package/dist/store/reducers/healthcheckInfo/types.d.ts +1 -0
- package/dist/store/reducers/healthcheckInfo/utils.d.ts +1 -0
- package/dist/store/reducers/healthcheckInfo/utils.js +43 -9
- package/dist/store/reducers/healthcheckInfo/utils.js.map +1 -1
- package/dist/store/reducers/tenants/tenants.d.ts +10 -1
- package/dist/store/reducers/tenants/tenants.js +33 -0
- package/dist/store/reducers/tenants/tenants.js.map +1 -1
- package/dist/store/reducers/tenants/types.d.ts +4 -0
- package/dist/types/api/healthcheck.d.ts +7 -0
- package/package.json +1 -1
- package/src/components/Drawer/Drawer.tsx +6 -0
- package/src/components/EntityStatus/EntityStatus.tsx +9 -1
- package/src/components/TenantNameWrapper/utils.ts +1 -1
- package/src/containers/Cluster/Cluster.tsx +1 -0
- package/src/containers/Cluster/ClusterDrawerHealthcheck.tsx +112 -13
- package/src/containers/Cluster/ClusterInfo/ClusterInfo.tsx +7 -1
- package/src/containers/Cluster/ClusterOverview/ClusterOverview.tsx +1 -0
- package/src/containers/Cluster/ClusterOverview/components/BridgeInfoTable.scss +24 -89
- package/src/containers/Cluster/ClusterOverview/components/BridgeInfoTable.tsx +156 -200
- package/src/containers/Cluster/ClusterOverview/components/bridgePileHealthcheck.ts +142 -0
- package/src/containers/Cluster/i18n/en.json +8 -5
- package/src/containers/Header/DatabaseRightControls.tsx +7 -1
- package/src/containers/Header/Header.tsx +3 -0
- package/src/containers/Header/HeaderActionsMenu.tsx +24 -2
- package/src/containers/Header/HeaderRightControls.tsx +7 -1
- package/src/containers/Header/hooks/useHeaderData.ts +1 -0
- package/src/containers/Header/hooks/useSharedDatabasePath.ts +195 -0
- package/src/containers/Header/i18n/en.json +1 -0
- package/src/containers/Tablets/Tablets.tsx +3 -3
- package/src/containers/Tablets/TabletsTable.tsx +37 -31
- package/src/containers/Tablets/i18n/en.json +3 -0
- package/src/containers/Tablets/useTabletQueryParams.ts +54 -0
- package/src/containers/Tablets/utils.ts +43 -0
- package/src/containers/Tenant/Healthcheck/Healthcheck.tsx +37 -4
- package/src/containers/Tenant/Healthcheck/components/HealthcheckDrawer.tsx +3 -0
- package/src/containers/Tenant/Healthcheck/components/HealthcheckIssue.tsx +17 -4
- package/src/containers/Tenant/Healthcheck/components/HealthcheckIssueDetails/ComputeLocation.tsx +13 -3
- package/src/containers/Tenant/Healthcheck/components/HealthcheckIssueDetails/NodeInfo.tsx +1 -0
- package/src/containers/Tenant/Healthcheck/components/HealthcheckIssueDetails/StorageLocation.tsx +3 -1
- package/src/containers/Tenant/Healthcheck/components/HealthcheckIssues.tsx +15 -2
- package/src/containers/Tenant/Healthcheck/i18n/en.json +2 -1
- package/src/containers/Tenant/Query/SavedQueries/SavedQueries.scss +2 -0
- package/src/routes.ts +8 -2
- package/src/services/api/viewer.ts +10 -2
- package/src/store/reducers/cluster/cluster.ts +2 -1
- package/src/store/reducers/healthcheckInfo/types.ts +1 -0
- package/src/store/reducers/healthcheckInfo/utils.ts +50 -10
- package/src/store/reducers/tenants/tenants.ts +55 -1
- package/src/store/reducers/tenants/types.ts +5 -0
- package/src/types/api/healthcheck.ts +8 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
import {Pencil, PlugConnection, TrashBin} from '@gravity-ui/icons';
|
|
3
|
+
import {DatabaseArrowRight, Pencil, PlugConnection, TrashBin} from '@gravity-ui/icons';
|
|
4
4
|
import {useHistory} from 'react-router-dom';
|
|
5
5
|
|
|
6
6
|
import {getConnectToDBDialog} from '../../components/ConnectToDB/ConnectToDBDialog';
|
|
@@ -9,11 +9,12 @@ import {DropdownMenu} from '../../components/DropdownMenu';
|
|
|
9
9
|
import {getClusterPath} from '../../routes';
|
|
10
10
|
import {useEmMetaAvailable} from '../../store/reducers/capabilities/hooks';
|
|
11
11
|
import type {PreparedTenant} from '../../store/reducers/tenants/types';
|
|
12
|
-
import type {ClusterLinkWithTitle} from '../../types/additionalProps';
|
|
12
|
+
import type {AdditionalTenantsProps, ClusterLinkWithTitle} from '../../types/additionalProps';
|
|
13
13
|
import {uiFactory} from '../../uiFactory/uiFactory';
|
|
14
14
|
import {clusterTabsIds} from '../Cluster/utils';
|
|
15
15
|
|
|
16
16
|
import {b} from './constants';
|
|
17
|
+
import {useSharedDatabasePath} from './hooks/useSharedDatabasePath';
|
|
17
18
|
import {headerKeyset} from './i18n';
|
|
18
19
|
|
|
19
20
|
export interface HeaderActionsMenuProps {
|
|
@@ -22,6 +23,8 @@ export interface HeaderActionsMenuProps {
|
|
|
22
23
|
databaseData?: PreparedTenant;
|
|
23
24
|
isDatabaseDataLoading: boolean;
|
|
24
25
|
isV2NavigationEnabled: boolean;
|
|
26
|
+
isViewerUser?: boolean;
|
|
27
|
+
prepareTenantBackend?: AdditionalTenantsProps['prepareTenantBackend'];
|
|
25
28
|
databaseLinks: ClusterLinkWithTitle[];
|
|
26
29
|
}
|
|
27
30
|
|
|
@@ -31,11 +34,19 @@ export function DBHeaderActionsMenu({
|
|
|
31
34
|
databaseData,
|
|
32
35
|
isDatabaseDataLoading,
|
|
33
36
|
isV2NavigationEnabled,
|
|
37
|
+
isViewerUser,
|
|
38
|
+
prepareTenantBackend,
|
|
34
39
|
databaseLinks,
|
|
35
40
|
}: HeaderActionsMenuProps) {
|
|
36
41
|
const history = useHistory();
|
|
37
42
|
|
|
38
43
|
const emMetaAvailable = useEmMetaAvailable();
|
|
44
|
+
const sharedDatabasePath = useSharedDatabasePath({
|
|
45
|
+
clusterName,
|
|
46
|
+
databaseData,
|
|
47
|
+
isViewerUser,
|
|
48
|
+
prepareTenantBackend,
|
|
49
|
+
});
|
|
39
50
|
|
|
40
51
|
const isEditDBAvailable = emMetaAvailable && uiFactory.onEditDB !== undefined;
|
|
41
52
|
const isDeleteDBAvailable = emMetaAvailable && uiFactory.onDeleteDB !== undefined;
|
|
@@ -70,6 +81,16 @@ export function DBHeaderActionsMenu({
|
|
|
70
81
|
]);
|
|
71
82
|
}
|
|
72
83
|
|
|
84
|
+
if (sharedDatabasePath) {
|
|
85
|
+
menuItems.push([
|
|
86
|
+
{
|
|
87
|
+
title: headerKeyset('action_go-to-shared-db'),
|
|
88
|
+
iconStart: DatabaseArrowRight,
|
|
89
|
+
href: sharedDatabasePath,
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
}
|
|
93
|
+
|
|
73
94
|
const {onEditDB, onDeleteDB} = uiFactory;
|
|
74
95
|
|
|
75
96
|
const isEnoughData = clusterName && databaseData;
|
|
@@ -115,6 +136,7 @@ export function DBHeaderActionsMenu({
|
|
|
115
136
|
history,
|
|
116
137
|
isEditDBAvailable,
|
|
117
138
|
isDeleteDBAvailable,
|
|
139
|
+
sharedDatabasePath,
|
|
118
140
|
]);
|
|
119
141
|
|
|
120
142
|
if (!menuItems.length) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {Flex} from '@gravity-ui/uikit';
|
|
2
2
|
|
|
3
3
|
import type {PreparedTenant} from '../../store/reducers/tenants/types';
|
|
4
|
-
import type {ClusterLinkWithTitle} from '../../types/additionalProps';
|
|
4
|
+
import type {AdditionalTenantsProps, ClusterLinkWithTitle} from '../../types/additionalProps';
|
|
5
5
|
import {useHasDeveloperUi} from '../../utils/developerUI/developerUI';
|
|
6
6
|
|
|
7
7
|
import {ClusterRightControls} from './ClusterRightControls';
|
|
@@ -20,6 +20,8 @@ interface HeaderRightControlsProps {
|
|
|
20
20
|
isDatabaseDataLoading: boolean;
|
|
21
21
|
isAddClusterAvailable: boolean;
|
|
22
22
|
isV2NavigationEnabled: boolean;
|
|
23
|
+
isViewerUser?: boolean;
|
|
24
|
+
prepareTenantBackend?: AdditionalTenantsProps['prepareTenantBackend'];
|
|
23
25
|
clusterLinks: ClusterLinkWithTitle[];
|
|
24
26
|
databaseLinks: ClusterLinkWithTitle[];
|
|
25
27
|
handleEditCluster?: () => Promise<boolean>;
|
|
@@ -37,6 +39,8 @@ export function HeaderRightControls({
|
|
|
37
39
|
isDatabaseDataLoading,
|
|
38
40
|
isAddClusterAvailable,
|
|
39
41
|
isV2NavigationEnabled,
|
|
42
|
+
isViewerUser,
|
|
43
|
+
prepareTenantBackend,
|
|
40
44
|
clusterLinks,
|
|
41
45
|
databaseLinks,
|
|
42
46
|
handleEditCluster,
|
|
@@ -58,6 +62,8 @@ export function HeaderRightControls({
|
|
|
58
62
|
databaseData={databaseData}
|
|
59
63
|
isDatabaseDataLoading={isDatabaseDataLoading}
|
|
60
64
|
isV2NavigationEnabled={isV2NavigationEnabled}
|
|
65
|
+
isViewerUser={isViewerUser}
|
|
66
|
+
prepareTenantBackend={prepareTenantBackend}
|
|
61
67
|
showDeveloperUI={showDeveloperUI}
|
|
62
68
|
databaseLinks={databaseLinks}
|
|
63
69
|
/>
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {skipToken} from '@reduxjs/toolkit/query';
|
|
4
|
+
|
|
5
|
+
import {getTenantBackend} from '../../../components/TenantNameWrapper/utils';
|
|
6
|
+
import {getTenantPath} from '../../../routes';
|
|
7
|
+
import {backend as activeBackend, environment} from '../../../store';
|
|
8
|
+
import {useClusterBaseInfo} from '../../../store/reducers/cluster/cluster';
|
|
9
|
+
import {tenantsApi} from '../../../store/reducers/tenants/tenants';
|
|
10
|
+
import type {PreparedTenant, SharedDatabaseTarget} from '../../../store/reducers/tenants/types';
|
|
11
|
+
import type {AdditionalTenantsProps} from '../../../types/additionalProps';
|
|
12
|
+
import {uiFactory} from '../../../uiFactory/uiFactory';
|
|
13
|
+
import {useDatabasesV2} from '../../../utils/hooks/useDatabasesV2';
|
|
14
|
+
import {useIsUserAllowedToMakeChanges} from '../../../utils/hooks/useIsUserAllowedToMakeChanges';
|
|
15
|
+
|
|
16
|
+
interface UseSharedDatabasePathParams {
|
|
17
|
+
clusterName?: string;
|
|
18
|
+
databaseData?: PreparedTenant;
|
|
19
|
+
isViewerUser?: boolean;
|
|
20
|
+
prepareTenantBackend?: AdditionalTenantsProps['prepareTenantBackend'];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface SharedDatabaseNameResolutionParams {
|
|
24
|
+
databaseData?: PreparedTenant;
|
|
25
|
+
isClusterBaseInfoResolved: boolean;
|
|
26
|
+
isViewerUser?: boolean;
|
|
27
|
+
useDatabaseId?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function canResolveSharedDatabaseName({
|
|
31
|
+
databaseData,
|
|
32
|
+
isClusterBaseInfoResolved,
|
|
33
|
+
isViewerUser,
|
|
34
|
+
useDatabaseId,
|
|
35
|
+
}: SharedDatabaseNameResolutionParams) {
|
|
36
|
+
return Boolean(
|
|
37
|
+
isViewerUser &&
|
|
38
|
+
isClusterBaseInfoResolved &&
|
|
39
|
+
databaseData?.Type === 'Serverless' &&
|
|
40
|
+
databaseData.ResourceId &&
|
|
41
|
+
!useDatabaseId &&
|
|
42
|
+
!databaseData.sharedTenantName,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getSharedDatabaseBackend(
|
|
47
|
+
sharedDatabase: SharedDatabaseTarget | undefined,
|
|
48
|
+
prepareTenantBackend: AdditionalTenantsProps['prepareTenantBackend'],
|
|
49
|
+
) {
|
|
50
|
+
if (!sharedDatabase) {
|
|
51
|
+
return activeBackend;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
getTenantBackend({NodeIds: sharedDatabase.nodeIds}, {prepareTenantBackend}) ?? activeBackend
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function shouldResolveSharedDatabaseNodeIds(
|
|
60
|
+
balancer: string | undefined,
|
|
61
|
+
settings: {use_meta_proxy?: boolean} | undefined,
|
|
62
|
+
prepareTenantBackend: AdditionalTenantsProps['prepareTenantBackend'],
|
|
63
|
+
) {
|
|
64
|
+
const useMetaProxy = Boolean(
|
|
65
|
+
window.api.meta && uiFactory.useMetaProxy && settings?.use_meta_proxy !== false,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return Boolean(
|
|
69
|
+
balancer && !useMetaProxy && prepareTenantBackend && !prepareTenantBackend(undefined),
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getResolvedSharedDatabasePath({
|
|
74
|
+
backend,
|
|
75
|
+
clusterName,
|
|
76
|
+
databaseData,
|
|
77
|
+
isClusterBaseInfoResolved,
|
|
78
|
+
isViewerUser,
|
|
79
|
+
sharedDatabase,
|
|
80
|
+
}: {
|
|
81
|
+
backend?: string;
|
|
82
|
+
clusterName?: string;
|
|
83
|
+
databaseData?: PreparedTenant;
|
|
84
|
+
isClusterBaseInfoResolved: boolean;
|
|
85
|
+
isViewerUser?: boolean;
|
|
86
|
+
sharedDatabase?: string;
|
|
87
|
+
}) {
|
|
88
|
+
if (
|
|
89
|
+
!isViewerUser ||
|
|
90
|
+
!isClusterBaseInfoResolved ||
|
|
91
|
+
databaseData?.Type !== 'Serverless' ||
|
|
92
|
+
!sharedDatabase
|
|
93
|
+
) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return getTenantPath(
|
|
98
|
+
{clusterName, database: sharedDatabase, backend},
|
|
99
|
+
{withBasename: true, environment},
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function useSharedDatabasePath({
|
|
104
|
+
clusterName,
|
|
105
|
+
databaseData,
|
|
106
|
+
isViewerUser,
|
|
107
|
+
prepareTenantBackend,
|
|
108
|
+
}: UseSharedDatabasePathParams) {
|
|
109
|
+
const isMetaDatabasesAvailable = useDatabasesV2();
|
|
110
|
+
const {
|
|
111
|
+
balancer,
|
|
112
|
+
settings,
|
|
113
|
+
isError: isClusterBaseInfoError,
|
|
114
|
+
isResolved: isClusterBaseInfoResolved,
|
|
115
|
+
} = useClusterBaseInfo();
|
|
116
|
+
const isMonitoringAllowed = useIsUserAllowedToMakeChanges();
|
|
117
|
+
const isClusterBaseInfoAvailable = isClusterBaseInfoResolved && !isClusterBaseInfoError;
|
|
118
|
+
|
|
119
|
+
const useDatabaseId = uiFactory.useDatabaseId && settings?.use_meta_proxy !== false;
|
|
120
|
+
const needSharedDatabaseNodeIds = shouldResolveSharedDatabaseNodeIds(
|
|
121
|
+
balancer,
|
|
122
|
+
settings,
|
|
123
|
+
prepareTenantBackend,
|
|
124
|
+
);
|
|
125
|
+
const shouldResolveSharedDatabaseName = canResolveSharedDatabaseName({
|
|
126
|
+
databaseData,
|
|
127
|
+
isClusterBaseInfoResolved: isClusterBaseInfoAvailable,
|
|
128
|
+
isViewerUser,
|
|
129
|
+
useDatabaseId,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const shouldResolveSharedDatabaseFromViewer =
|
|
133
|
+
shouldResolveSharedDatabaseName && !window.api.meta;
|
|
134
|
+
|
|
135
|
+
const {currentData: sharedDatabaseFromViewer} = tenantsApi.useGetSharedDatabaseQuery(
|
|
136
|
+
shouldResolveSharedDatabaseFromViewer && databaseData?.ResourceId
|
|
137
|
+
? {
|
|
138
|
+
clusterName,
|
|
139
|
+
database: databaseData.Name,
|
|
140
|
+
backend: activeBackend,
|
|
141
|
+
environmentName: environment,
|
|
142
|
+
isMonitoringAllowed: isMonitoringAllowed === true,
|
|
143
|
+
needNodeIds: needSharedDatabaseNodeIds,
|
|
144
|
+
resourceId: databaseData.ResourceId,
|
|
145
|
+
}
|
|
146
|
+
: skipToken,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const shouldResolveSharedDatabaseFromMetaList =
|
|
150
|
+
shouldResolveSharedDatabaseName && Boolean(window.api.meta);
|
|
151
|
+
|
|
152
|
+
const {currentData: databases} = tenantsApi.useGetTenantsInfoQuery(
|
|
153
|
+
shouldResolveSharedDatabaseFromMetaList
|
|
154
|
+
? {clusterName, environmentName: environment, isMetaDatabasesAvailable}
|
|
155
|
+
: skipToken,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const sharedDatabaseTarget = React.useMemo<SharedDatabaseTarget | undefined>(() => {
|
|
159
|
+
if (databaseData?.sharedTenantName) {
|
|
160
|
+
return {
|
|
161
|
+
name: databaseData.sharedTenantName,
|
|
162
|
+
nodeIds: databaseData.sharedNodeIds,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (sharedDatabaseFromViewer) {
|
|
167
|
+
return sharedDatabaseFromViewer;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const sharedDatabase = databases?.find(({Id}) => Id === databaseData?.ResourceId);
|
|
171
|
+
const sharedDatabaseName = sharedDatabase?.Name?.trim();
|
|
172
|
+
|
|
173
|
+
return sharedDatabaseName
|
|
174
|
+
? {name: sharedDatabaseName, nodeIds: sharedDatabase?.NodeIds}
|
|
175
|
+
: undefined;
|
|
176
|
+
}, [
|
|
177
|
+
databaseData?.ResourceId,
|
|
178
|
+
databaseData?.sharedNodeIds,
|
|
179
|
+
databaseData?.sharedTenantName,
|
|
180
|
+
databases,
|
|
181
|
+
sharedDatabaseFromViewer,
|
|
182
|
+
]);
|
|
183
|
+
|
|
184
|
+
const sharedDatabase = useDatabaseId ? databaseData?.ResourceId : sharedDatabaseTarget?.name;
|
|
185
|
+
const backend = getSharedDatabaseBackend(sharedDatabaseTarget, prepareTenantBackend);
|
|
186
|
+
|
|
187
|
+
return getResolvedSharedDatabasePath({
|
|
188
|
+
backend,
|
|
189
|
+
clusterName,
|
|
190
|
+
databaseData,
|
|
191
|
+
isClusterBaseInfoResolved: isClusterBaseInfoAvailable,
|
|
192
|
+
isViewerUser,
|
|
193
|
+
sharedDatabase,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"action_edit-cluster": "Edit Cluster",
|
|
16
16
|
"action_delete-cluster": "Delete Cluster",
|
|
17
17
|
"action_connect-to-db": "Connect to database",
|
|
18
|
+
"action_go-to-shared-db": "Go to shared database",
|
|
18
19
|
"action_manage": "Manage",
|
|
19
20
|
|
|
20
21
|
"title_developer-ui": "Developer UI",
|
|
@@ -2,7 +2,6 @@ import React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import {skipToken} from '@reduxjs/toolkit/query';
|
|
4
4
|
import {isNil} from 'lodash';
|
|
5
|
-
import {StringParam, useQueryParams} from 'use-query-params';
|
|
6
5
|
|
|
7
6
|
import {useClusterWithProxy} from '../../store/reducers/cluster/cluster';
|
|
8
7
|
import {selectTabletsWithFqdn, tabletsApi} from '../../store/reducers/tablets';
|
|
@@ -12,6 +11,7 @@ import {valueIsDefined} from '../../utils';
|
|
|
12
11
|
import {useAutoRefreshInterval, useTypedSelector} from '../../utils/hooks';
|
|
13
12
|
|
|
14
13
|
import {TabletsTable} from './TabletsTable';
|
|
14
|
+
import {useTabletQueryParams} from './useTabletQueryParams';
|
|
15
15
|
|
|
16
16
|
const TABLET_ID_SEARCH_DEBOUNCE = 1000;
|
|
17
17
|
const TABLET_ID_PATTERN = /^\d+$/;
|
|
@@ -60,8 +60,8 @@ export function Tablets({
|
|
|
60
60
|
// search remains client-side over the already loaded tablets list.
|
|
61
61
|
const isDatabasePage = !isNil(path) && !isNil(databaseFullPath) && path === databaseFullPath;
|
|
62
62
|
|
|
63
|
-
const
|
|
64
|
-
const currentSearch = tabletsSearch
|
|
63
|
+
const {tabletsSearch} = useTabletQueryParams();
|
|
64
|
+
const currentSearch = tabletsSearch;
|
|
65
65
|
|
|
66
66
|
// Debounce the search value before sending it to the backend, since the
|
|
67
67
|
// request can be heavy.
|
|
@@ -2,9 +2,8 @@ import React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import {ArrowRotateLeft} from '@gravity-ui/icons';
|
|
4
4
|
import type {Column as DataTableColumn, SortOrder} from '@gravity-ui/react-data-table';
|
|
5
|
-
import {Icon, Text} from '@gravity-ui/uikit';
|
|
5
|
+
import {Icon, Select, Text} from '@gravity-ui/uikit';
|
|
6
6
|
import {isNil} from 'lodash';
|
|
7
|
-
import {StringParam, useQueryParams} from 'use-query-params';
|
|
8
7
|
|
|
9
8
|
import {ButtonWithConfirmDialog} from '../../components/ButtonWithConfirmDialog/ButtonWithConfirmDialog';
|
|
10
9
|
import {EntitiesCount} from '../../components/EntitiesCount';
|
|
@@ -24,6 +23,8 @@ import {DEFAULT_TABLE_SETTINGS, EMPTY_DATA_PLACEHOLDER} from '../../utils/consta
|
|
|
24
23
|
import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
|
|
25
24
|
|
|
26
25
|
import i18n from './i18n';
|
|
26
|
+
import {useTabletQueryParams} from './useTabletQueryParams';
|
|
27
|
+
import {filterTablets, getAvailableTabletTypes} from './utils';
|
|
27
28
|
|
|
28
29
|
function isFollowerTablet(state: TTabletStateInfo) {
|
|
29
30
|
return state.Leader === false;
|
|
@@ -228,55 +229,58 @@ export function TabletsTable({
|
|
|
228
229
|
nodeId,
|
|
229
230
|
backendSearchActive,
|
|
230
231
|
}: TabletsTableProps) {
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
});
|
|
232
|
+
const {tabletsSearch, tabletTypes, handleTabletsSearchChange, handleTabletTypesChange} =
|
|
233
|
+
useTabletQueryParams();
|
|
234
234
|
|
|
235
235
|
// Track sort state for scroll dependencies
|
|
236
236
|
const [sortParams, setSortParams] = React.useState<SortOrder | SortOrder[] | undefined>();
|
|
237
237
|
|
|
238
238
|
const {filteredTablets, showEndOfRange} = React.useMemo(() => {
|
|
239
|
-
let showEnd = false;
|
|
240
|
-
if (backendSearchActive) {
|
|
241
|
-
// Backend already filtered the list; just scan once for showEndOfRange.
|
|
242
|
-
for (const tablet of tablets) {
|
|
243
|
-
if (tablet.EndOfRangeKeyPrefix) {
|
|
244
|
-
showEnd = true;
|
|
245
|
-
break;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return {filteredTablets: tablets, showEndOfRange: showEnd};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const preparedTabletsSearch = tabletsSearch?.trim() ?? '';
|
|
252
|
-
|
|
253
239
|
return {
|
|
254
|
-
filteredTablets: tablets
|
|
255
|
-
|
|
256
|
-
|
|
240
|
+
filteredTablets: filterTablets(tablets, {
|
|
241
|
+
tabletIdSearch: backendSearchActive ? undefined : tabletsSearch,
|
|
242
|
+
tabletTypes,
|
|
257
243
|
}),
|
|
258
|
-
showEndOfRange:
|
|
244
|
+
showEndOfRange: tablets.some((tablet) => Boolean(tablet.EndOfRangeKeyPrefix)),
|
|
259
245
|
};
|
|
260
|
-
}, [tablets, tabletsSearch, backendSearchActive]);
|
|
246
|
+
}, [tablets, tabletsSearch, tabletTypes, backendSearchActive]);
|
|
247
|
+
|
|
248
|
+
const tabletTypeOptions = React.useMemo(() => {
|
|
249
|
+
return getAvailableTabletTypes(tablets, tabletTypes).map((tabletType) => ({
|
|
250
|
+
value: tabletType,
|
|
251
|
+
content: tabletType,
|
|
252
|
+
}));
|
|
253
|
+
}, [tablets, tabletTypes]);
|
|
261
254
|
|
|
262
255
|
const columns = React.useMemo(
|
|
263
256
|
() => getColumns({nodeId, showEndOfRange}),
|
|
264
257
|
[nodeId, showEndOfRange],
|
|
265
258
|
);
|
|
266
259
|
|
|
267
|
-
const
|
|
268
|
-
setQueryParams({tabletsSearch: value || undefined}, 'replaceIn');
|
|
269
|
-
};
|
|
260
|
+
const filtersActive = Boolean(tabletsSearch.trim()) || tabletTypes.length > 0;
|
|
270
261
|
|
|
271
262
|
return (
|
|
272
263
|
<TableWithControlsLayout fullHeight>
|
|
273
264
|
<TableWithControlsLayout.Controls>
|
|
274
265
|
<Search
|
|
275
266
|
placeholder={i18n('controls.search-placeholder')}
|
|
276
|
-
onChange={
|
|
277
|
-
value={tabletsSearch
|
|
267
|
+
onChange={handleTabletsSearchChange}
|
|
268
|
+
value={tabletsSearch}
|
|
278
269
|
width={238}
|
|
279
270
|
/>
|
|
271
|
+
<Select
|
|
272
|
+
multiple
|
|
273
|
+
filterable
|
|
274
|
+
hasClear
|
|
275
|
+
qa="tablets-type-filter"
|
|
276
|
+
aria-label={i18n('controls.type-filter-label')}
|
|
277
|
+
label={i18n('controls.type-filter-label')}
|
|
278
|
+
placeholder={i18n('controls.type-filter-placeholder')}
|
|
279
|
+
value={tabletTypes}
|
|
280
|
+
options={tabletTypeOptions}
|
|
281
|
+
onUpdate={handleTabletTypesChange}
|
|
282
|
+
width={220}
|
|
283
|
+
/>
|
|
280
284
|
<EntitiesCount
|
|
281
285
|
label={i18n('controls.entities-count-label')}
|
|
282
286
|
loading={loading}
|
|
@@ -287,14 +291,16 @@ export function TabletsTable({
|
|
|
287
291
|
{error ? <ResponseError error={error} /> : null}
|
|
288
292
|
<TableWithControlsLayout.Table
|
|
289
293
|
scrollContainerRef={scrollContainerRef}
|
|
290
|
-
scrollDependencies={[tabletsSearch, sortParams]}
|
|
294
|
+
scrollDependencies={[tabletsSearch, tabletTypes, sortParams]}
|
|
291
295
|
loading={loading}
|
|
292
296
|
>
|
|
293
297
|
<ResizeableDataTable
|
|
294
298
|
columns={columns}
|
|
295
299
|
data={filteredTablets}
|
|
296
300
|
settings={DEFAULT_TABLE_SETTINGS}
|
|
297
|
-
emptyDataMessage={
|
|
301
|
+
emptyDataMessage={
|
|
302
|
+
filtersActive ? i18n('noTabletsMatchFilters') : i18n('noTabletsData')
|
|
303
|
+
}
|
|
298
304
|
onSortChange={setSortParams}
|
|
299
305
|
/>
|
|
300
306
|
</TableWithControlsLayout.Table>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"noTabletsData": "No tablets data",
|
|
3
|
+
"noTabletsMatchFilters": "No tablets match the selected filters",
|
|
3
4
|
"Type": "Type",
|
|
4
5
|
"Tablet": "Tablet",
|
|
5
6
|
"State": "State",
|
|
@@ -13,5 +14,7 @@
|
|
|
13
14
|
"controls.kill-not-allowed": "You don't have enough rights to restart tablet",
|
|
14
15
|
"controls.kill-impossible-follower": "It's impossible to restart a follower",
|
|
15
16
|
"controls.search-placeholder": "Tablet ID",
|
|
17
|
+
"controls.type-filter-label": "Type",
|
|
18
|
+
"controls.type-filter-placeholder": "All types",
|
|
16
19
|
"controls.entities-count-label": "Tablets"
|
|
17
20
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type {QueryParamConfig} from 'use-query-params';
|
|
4
|
+
import {StringParam, useQueryParams} from 'use-query-params';
|
|
5
|
+
import {z} from 'zod';
|
|
6
|
+
|
|
7
|
+
const tabletTypesSchema = z
|
|
8
|
+
.array(
|
|
9
|
+
z
|
|
10
|
+
.string()
|
|
11
|
+
.trim()
|
|
12
|
+
.min(1)
|
|
13
|
+
.max(128)
|
|
14
|
+
.regex(/^[A-Za-z_][A-Za-z0-9_]*$/),
|
|
15
|
+
)
|
|
16
|
+
.max(100)
|
|
17
|
+
.catch([]);
|
|
18
|
+
|
|
19
|
+
export const TabletTypesParam: QueryParamConfig<string[] | undefined, string[]> = {
|
|
20
|
+
encode: (value) => (value?.length ? value.join(',') : undefined),
|
|
21
|
+
decode: (value) => {
|
|
22
|
+
if (typeof value !== 'string') {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return Array.from(new Set(tabletTypesSchema.parse(value.split(','))));
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function useTabletQueryParams() {
|
|
31
|
+
const [{tabletsSearch, tabletTypes}, setQueryParams] = useQueryParams({
|
|
32
|
+
tabletsSearch: StringParam,
|
|
33
|
+
tabletTypes: TabletTypesParam,
|
|
34
|
+
});
|
|
35
|
+
const handleTabletsSearchChange = React.useCallback(
|
|
36
|
+
(value: string) => {
|
|
37
|
+
setQueryParams({tabletsSearch: value || undefined}, 'replaceIn');
|
|
38
|
+
},
|
|
39
|
+
[setQueryParams],
|
|
40
|
+
);
|
|
41
|
+
const handleTabletTypesChange = React.useCallback(
|
|
42
|
+
(value: string[]) => {
|
|
43
|
+
setQueryParams({tabletTypes: value.length ? value : undefined}, 'replaceIn');
|
|
44
|
+
},
|
|
45
|
+
[setQueryParams],
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
tabletsSearch: tabletsSearch ?? '',
|
|
50
|
+
tabletTypes: tabletTypes ?? [],
|
|
51
|
+
handleTabletsSearchChange,
|
|
52
|
+
handleTabletTypesChange,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
interface FilterTabletsOptions {
|
|
2
|
+
tabletIdSearch?: string | null;
|
|
3
|
+
tabletTypes: string[];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface FilterableTablet {
|
|
7
|
+
TabletId?: string;
|
|
8
|
+
Type?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function filterTablets<T extends FilterableTablet>(
|
|
12
|
+
tablets: T[],
|
|
13
|
+
options: FilterTabletsOptions,
|
|
14
|
+
) {
|
|
15
|
+
const tabletIdSearch = options.tabletIdSearch?.trim() ?? '';
|
|
16
|
+
const tabletTypes = new Set(options.tabletTypes);
|
|
17
|
+
|
|
18
|
+
if (!tabletIdSearch && tabletTypes.size === 0) {
|
|
19
|
+
return tablets;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return tablets.filter((tablet) => {
|
|
23
|
+
const matchesTabletId = String(tablet.TabletId).includes(tabletIdSearch);
|
|
24
|
+
const matchesTabletType =
|
|
25
|
+
tabletTypes.size === 0 || (tablet.Type !== undefined && tabletTypes.has(tablet.Type));
|
|
26
|
+
|
|
27
|
+
return matchesTabletId && matchesTabletType;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getAvailableTabletTypes<T extends FilterableTablet>(
|
|
32
|
+
tablets: T[],
|
|
33
|
+
selectedTabletTypes: string[],
|
|
34
|
+
) {
|
|
35
|
+
const availableTabletTypes = new Set(selectedTabletTypes);
|
|
36
|
+
tablets.forEach((tablet) => {
|
|
37
|
+
if (tablet.Type) {
|
|
38
|
+
availableTabletTypes.add(tablet.Type);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return Array.from(availableTabletTypes).sort();
|
|
43
|
+
}
|
|
@@ -34,7 +34,7 @@ import cryCatIcon from '../../../assets/icons/cry-cat.svg';
|
|
|
34
34
|
|
|
35
35
|
import './Healthcheck.scss';
|
|
36
36
|
|
|
37
|
-
type HealthcheckDetailsProps =
|
|
37
|
+
type HealthcheckDetailsProps = (
|
|
38
38
|
| {
|
|
39
39
|
database: string;
|
|
40
40
|
clusterName?: string;
|
|
@@ -46,7 +46,12 @@ type HealthcheckDetailsProps =
|
|
|
46
46
|
database?: undefined;
|
|
47
47
|
databaseType?: undefined;
|
|
48
48
|
scope?: 'cluster';
|
|
49
|
-
}
|
|
49
|
+
}
|
|
50
|
+
) & {
|
|
51
|
+
targetIssueId?: string;
|
|
52
|
+
targetLeafIssueId?: string;
|
|
53
|
+
targetLeafIssueRef?: React.Ref<HTMLDivElement>;
|
|
54
|
+
};
|
|
50
55
|
|
|
51
56
|
export function Healthcheck(props: HealthcheckDetailsProps) {
|
|
52
57
|
if (props.database !== undefined) {
|
|
@@ -59,6 +64,9 @@ export function Healthcheck(props: HealthcheckDetailsProps) {
|
|
|
59
64
|
clusterName={props.clusterName}
|
|
60
65
|
databaseType={props.databaseType}
|
|
61
66
|
scope={props.scope}
|
|
67
|
+
targetIssueId={props.targetIssueId}
|
|
68
|
+
targetLeafIssueId={props.targetLeafIssueId}
|
|
69
|
+
targetLeafIssueRef={props.targetLeafIssueRef}
|
|
62
70
|
/>
|
|
63
71
|
</HealthcheckContext.Provider>
|
|
64
72
|
);
|
|
@@ -76,16 +84,30 @@ function DatabaseHealthcheckInner({
|
|
|
76
84
|
clusterName,
|
|
77
85
|
databaseType,
|
|
78
86
|
scope = 'database',
|
|
87
|
+
targetIssueId,
|
|
88
|
+
targetLeafIssueId,
|
|
89
|
+
targetLeafIssueRef,
|
|
79
90
|
}: {
|
|
80
91
|
database: string;
|
|
81
92
|
clusterName?: string;
|
|
82
93
|
databaseType?: ETenantType;
|
|
83
94
|
scope?: 'cluster' | 'database';
|
|
95
|
+
targetIssueId?: string;
|
|
96
|
+
targetLeafIssueId?: string;
|
|
97
|
+
targetLeafIssueRef?: React.Ref<HTMLDivElement>;
|
|
84
98
|
}) {
|
|
85
99
|
const healthcheck = useHealthcheck(database, {clusterName, databaseType});
|
|
86
100
|
const target = getDatabaseHealthcheckAssistantTarget({database, clusterName, scope});
|
|
87
101
|
|
|
88
|
-
return
|
|
102
|
+
return (
|
|
103
|
+
<HealthcheckContent
|
|
104
|
+
healthcheck={healthcheck}
|
|
105
|
+
target={target}
|
|
106
|
+
targetIssueId={targetIssueId}
|
|
107
|
+
targetLeafIssueId={targetLeafIssueId}
|
|
108
|
+
targetLeafIssueRef={targetLeafIssueRef}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
89
111
|
}
|
|
90
112
|
|
|
91
113
|
function ClusterHealthcheckInner({clusterName}: {clusterName: string}) {
|
|
@@ -112,9 +134,15 @@ interface HealthcheckResult {
|
|
|
112
134
|
function HealthcheckContent({
|
|
113
135
|
healthcheck,
|
|
114
136
|
target,
|
|
137
|
+
targetIssueId,
|
|
138
|
+
targetLeafIssueId,
|
|
139
|
+
targetLeafIssueRef,
|
|
115
140
|
}: {
|
|
116
141
|
healthcheck: HealthcheckResult;
|
|
117
142
|
target: HealthcheckAssistantTarget;
|
|
143
|
+
targetIssueId?: string;
|
|
144
|
+
targetLeafIssueId?: string;
|
|
145
|
+
targetLeafIssueRef?: React.Ref<HTMLDivElement>;
|
|
118
146
|
}) {
|
|
119
147
|
const SuccessImage = getIllustration('SuccessOperation');
|
|
120
148
|
const renderAssistantAction = uiFactory.healthcheck.renderAssistantAction;
|
|
@@ -214,7 +242,12 @@ function HealthcheckContent({
|
|
|
214
242
|
<Flex direction="column" grow={1}>
|
|
215
243
|
{renderControls()}
|
|
216
244
|
<Flex direction="column" gap={4} grow={1} className={b('issues')}>
|
|
217
|
-
<Issues
|
|
245
|
+
<Issues
|
|
246
|
+
issues={leavesIssues}
|
|
247
|
+
targetIssueId={targetIssueId}
|
|
248
|
+
targetLeafIssueId={targetLeafIssueId}
|
|
249
|
+
targetLeafIssueRef={targetLeafIssueRef}
|
|
250
|
+
/>
|
|
218
251
|
</Flex>
|
|
219
252
|
</Flex>
|
|
220
253
|
);
|