ydb-embedded-ui 4.15.0 → 4.16.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/CHANGELOG.md +21 -0
- package/dist/components/DiagnosticCard/DiagnosticCard.scss +5 -0
- package/dist/components/DiagnosticCard/DiagnosticCard.tsx +17 -0
- package/dist/components/EntityStatus/EntityStatus.js +2 -2
- package/dist/components/EntityStatus/EntityStatus.scss +15 -0
- package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +3 -11
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/Details.tsx +20 -11
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +14 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +26 -37
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx +37 -25
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +154 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +7 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +7 -0
- package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.scss +4 -1
- package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +42 -11
- package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +1 -1
- package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx +11 -7
- package/dist/containers/Tenant/Query/i18n/en.json +3 -0
- package/dist/containers/Tenant/Query/i18n/ru.json +3 -0
- package/dist/containers/Tenant/utils/queryTemplates.ts +30 -6
- package/dist/containers/Tenants/Tenants.tsx +3 -1
- package/dist/containers/UserSettings/Setting.tsx +9 -2
- package/dist/containers/UserSettings/i18n/en.json +5 -1
- package/dist/containers/UserSettings/i18n/ru.json +5 -1
- package/dist/containers/UserSettings/settings.ts +25 -0
- package/dist/services/api.ts +16 -16
- package/dist/store/reducers/executeQuery.ts +33 -7
- package/dist/store/reducers/explainQuery.ts +12 -4
- package/dist/store/reducers/healthcheckInfo.ts +27 -11
- package/dist/store/reducers/settings/settings.ts +4 -10
- package/dist/store/reducers/tenant/tenant.ts +19 -1
- package/dist/store/reducers/tenant/types.ts +3 -1
- package/dist/store/reducers/tenants/selectors.ts +1 -1
- package/dist/store/reducers/tenants/utils.ts +2 -2
- package/dist/types/api/tenant.ts +19 -20
- package/dist/types/store/executeQuery.ts +11 -1
- package/dist/types/store/query.ts +2 -1
- package/dist/utils/autofetcher.ts +7 -7
- package/dist/utils/constants.ts +2 -1
- package/dist/utils/hooks/i18n/en.json +1 -1
- package/dist/utils/hooks/i18n/ru.json +1 -1
- package/dist/utils/hooks/useQueryModes.ts +4 -2
- package/dist/utils/i18n/i18n.ts +10 -4
- package/dist/utils/query.ts +14 -0
- package/dist/utils/settings.ts +10 -0
- package/package.json +1 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/PreviewItem.tsx +0 -33
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/index.ts +0 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +0 -213
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [4.16.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.15.1...v4.16.0) (2023-08-25)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* add language setting ([#520](https://github.com/ydb-platform/ydb-embedded-ui/issues/520)) ([425c9ae](https://github.com/ydb-platform/ydb-embedded-ui/commit/425c9ae1fed83d7695d2a9288c2ef24c2807d8da))
|
9
|
+
* **Diagnostics:** update Healthcheck design ([#509](https://github.com/ydb-platform/ydb-embedded-ui/issues/509)) ([e315ca4](https://github.com/ydb-platform/ydb-embedded-ui/commit/e315ca42ac6c9d1736aaa25e2dd90afc2bcb9a8e))
|
10
|
+
* **Query:** support PostgreSQL syntax ([#515](https://github.com/ydb-platform/ydb-embedded-ui/issues/515)) ([0c8346e](https://github.com/ydb-platform/ydb-embedded-ui/commit/0c8346efc3643a8d201137901880f985dc100458))
|
11
|
+
|
12
|
+
|
13
|
+
### Bug Fixes
|
14
|
+
|
15
|
+
* **UserSettings:** update query mode setting description ([#521](https://github.com/ydb-platform/ydb-embedded-ui/issues/521)) ([c526471](https://github.com/ydb-platform/ydb-embedded-ui/commit/c52647192ff95d8fb9961479a85cc4d5a639d4e6))
|
16
|
+
|
17
|
+
## [4.15.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.15.0...v4.15.1) (2023-08-21)
|
18
|
+
|
19
|
+
|
20
|
+
### Bug Fixes
|
21
|
+
|
22
|
+
* **SchemaTree:** update create table template ([#512](https://github.com/ydb-platform/ydb-embedded-ui/issues/512)) ([712b3f3](https://github.com/ydb-platform/ydb-embedded-ui/commit/712b3f3612b09fdc5c850ffc3a984cd86827e5b9))
|
23
|
+
|
3
24
|
## [4.15.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.14.0...v4.15.0) (2023-08-17)
|
4
25
|
|
5
26
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import {ReactNode} from 'react';
|
2
|
+
import cn from 'bem-cn-lite';
|
3
|
+
|
4
|
+
import {Card} from '@gravity-ui/uikit';
|
5
|
+
|
6
|
+
import './DiagnosticCard.scss';
|
7
|
+
|
8
|
+
const b = cn('diagnostic-card');
|
9
|
+
|
10
|
+
interface DiagnosticCardProps {
|
11
|
+
children?: ReactNode;
|
12
|
+
className?: string;
|
13
|
+
}
|
14
|
+
|
15
|
+
export function DiagnosticCard({children, className}: DiagnosticCardProps) {
|
16
|
+
return <Card className={b(null, className)}>{children}</Card>;
|
17
|
+
}
|
@@ -107,13 +107,13 @@ class EntityStatus extends React.Component {
|
|
107
107
|
);
|
108
108
|
}
|
109
109
|
render() {
|
110
|
-
const {name, label, iconPath, hasClipboardButton, className} = this.props;
|
110
|
+
const {name, label, iconPath, hasClipboardButton, className, size, status} = this.props;
|
111
111
|
|
112
112
|
return (
|
113
113
|
<div className={b(null, className)} title={name}>
|
114
114
|
{iconPath ? this.renderStatusLink() : this.renderIcon()}
|
115
115
|
{label && (
|
116
|
-
<span title={label} className={b('label')}>
|
116
|
+
<span title={label} className={b('label', {size, state: status.toLowerCase()})}>
|
117
117
|
{label}
|
118
118
|
</span>
|
119
119
|
)}
|
@@ -47,6 +47,15 @@
|
|
47
47
|
line-height: var(--yc-text-body-2-line-height);
|
48
48
|
|
49
49
|
color: var(--yc-color-text-complementary);
|
50
|
+
|
51
|
+
&_size_m {
|
52
|
+
font-size: var(--yc-text-body-2-font-size);
|
53
|
+
line-height: var(--yc-text-body-2-line-height);
|
54
|
+
}
|
55
|
+
|
56
|
+
&_size_l {
|
57
|
+
font-size: var(--yc-text-header-2-font-size);
|
58
|
+
}
|
50
59
|
}
|
51
60
|
|
52
61
|
&__link {
|
@@ -89,6 +98,11 @@
|
|
89
98
|
width: 18px;
|
90
99
|
height: 18px;
|
91
100
|
}
|
101
|
+
|
102
|
+
&_size_l {
|
103
|
+
width: 27px;
|
104
|
+
height: 27px;
|
105
|
+
}
|
92
106
|
}
|
93
107
|
|
94
108
|
&__status-color {
|
@@ -115,6 +129,7 @@
|
|
115
129
|
}
|
116
130
|
}
|
117
131
|
|
132
|
+
&__label,
|
118
133
|
&__status-icon {
|
119
134
|
&_state_blue {
|
120
135
|
color: var(--yc-color-infographics-info-heavy);
|
@@ -8,8 +8,7 @@ import type {EPathType} from '../../../../types/api/schema';
|
|
8
8
|
import {Icon} from '../../../../components/Icon';
|
9
9
|
import Overview from '../Overview/Overview';
|
10
10
|
import {Healthcheck} from '../Healthcheck';
|
11
|
-
|
12
|
-
import TenantOverview from '../TenantOverview/TenantOverview';
|
11
|
+
import {TenantOverview} from '../TenantOverview/TenantOverview';
|
13
12
|
|
14
13
|
import './DetailedOverview.scss';
|
15
14
|
|
@@ -25,12 +24,9 @@ const b = cn('kv-detailed-overview');
|
|
25
24
|
function DetailedOverview(props: DetailedOverviewProps) {
|
26
25
|
const [isModalVisible, setIsModalVisible] = useState(false);
|
27
26
|
|
28
|
-
const [expandedIssueId, setExpandedIssueId] = useState<string>();
|
29
|
-
|
30
27
|
const {currentSchemaPath} = useSelector((state: any) => state.schema);
|
31
28
|
|
32
|
-
const openModalHandler = (
|
33
|
-
setExpandedIssueId(id);
|
29
|
+
const openModalHandler = () => {
|
34
30
|
setIsModalVisible(true);
|
35
31
|
};
|
36
32
|
|
@@ -41,11 +37,7 @@ function DetailedOverview(props: DetailedOverviewProps) {
|
|
41
37
|
const renderModal = () => {
|
42
38
|
return (
|
43
39
|
<Modal open={isModalVisible} onClose={closeModalHandler} className={b('modal')}>
|
44
|
-
<Healthcheck
|
45
|
-
tenant={props.tenantName}
|
46
|
-
fetchData={false}
|
47
|
-
expandedIssueId={expandedIssueId}
|
48
|
-
/>
|
40
|
+
<Healthcheck tenant={props.tenantName} fetchData={false} />
|
49
41
|
<Button
|
50
42
|
className={b('close-modal-button')}
|
51
43
|
onClick={closeModalHandler}
|
@@ -4,7 +4,9 @@ import {Button, Icon} from '@gravity-ui/uikit';
|
|
4
4
|
|
5
5
|
import updateArrow from '../../../../../assets/icons/update-arrow.svg';
|
6
6
|
|
7
|
+
import type {IResponseError} from '../../../../../types/api/error';
|
7
8
|
import type {IIssuesTree} from '../../../../../types/store/healthcheck';
|
9
|
+
import {ResponseError} from '../../../../../components/Errors/ResponseError';
|
8
10
|
|
9
11
|
import IssueTree from '../IssuesViewer/IssueTree';
|
10
12
|
|
@@ -13,17 +15,14 @@ import i18n from '../i18n';
|
|
13
15
|
const b = cn('healthcheck');
|
14
16
|
|
15
17
|
interface DetailsProps {
|
16
|
-
|
18
|
+
issueTrees?: IIssuesTree[];
|
17
19
|
loading?: boolean;
|
18
20
|
onUpdate: VoidFunction;
|
21
|
+
error?: IResponseError;
|
19
22
|
}
|
20
23
|
|
21
24
|
export const Details = (props: DetailsProps) => {
|
22
|
-
const {loading, onUpdate,
|
23
|
-
|
24
|
-
if (!issueTree) {
|
25
|
-
return null;
|
26
|
-
}
|
25
|
+
const {loading, onUpdate, issueTrees, error} = props;
|
27
26
|
|
28
27
|
const renderHealthcheckHeader = () => {
|
29
28
|
return (
|
@@ -38,18 +37,28 @@ export const Details = (props: DetailsProps) => {
|
|
38
37
|
);
|
39
38
|
};
|
40
39
|
|
41
|
-
const
|
40
|
+
const renderContent = () => {
|
41
|
+
if (error) {
|
42
|
+
return <ResponseError error={error} defaultMessage={i18n('no-data')} />;
|
43
|
+
}
|
44
|
+
|
45
|
+
if (!issueTrees || !issueTrees.length) {
|
46
|
+
return i18n('status_message.ok');
|
47
|
+
}
|
48
|
+
|
42
49
|
return (
|
43
|
-
|
44
|
-
|
45
|
-
|
50
|
+
<>
|
51
|
+
{issueTrees.map((issueTree) => (
|
52
|
+
<IssueTree key={issueTree.id} issueTree={issueTree} />
|
53
|
+
))}
|
54
|
+
</>
|
46
55
|
);
|
47
56
|
};
|
48
57
|
|
49
58
|
return (
|
50
59
|
<div className={b('details')}>
|
51
60
|
{renderHealthcheckHeader()}
|
52
|
-
{
|
61
|
+
<div className={b('details-content-wrapper')}>{renderContent()}</div>
|
53
62
|
</div>
|
54
63
|
);
|
55
64
|
};
|
@@ -3,6 +3,8 @@
|
|
3
3
|
@import '@gravity-ui/uikit/styles/mixins.scss';
|
4
4
|
|
5
5
|
.healthcheck {
|
6
|
+
display: flex;
|
7
|
+
|
6
8
|
&_expanded {
|
7
9
|
// Since most of the inner containers have fixed width, we can set fixed width here as well
|
8
10
|
// Thus we will get rid of unneeded layout shift when scrollbar appear
|
@@ -17,7 +19,7 @@
|
|
17
19
|
margin-bottom: 15px;
|
18
20
|
}
|
19
21
|
|
20
|
-
&
|
22
|
+
&__details-content-wrapper {
|
21
23
|
overflow-x: hidden;
|
22
24
|
overflow-y: auto;
|
23
25
|
|
@@ -67,6 +69,17 @@
|
|
67
69
|
line-height: 24px;
|
68
70
|
}
|
69
71
|
|
72
|
+
&__issues-statistics {
|
73
|
+
display: flex;
|
74
|
+
flex-wrap: wrap;
|
75
|
+
align-items: center;
|
76
|
+
|
77
|
+
margin: 10px 0;
|
78
|
+
|
79
|
+
column-gap: 26px;
|
80
|
+
row-gap: 16px;
|
81
|
+
}
|
82
|
+
|
70
83
|
&__self-check-status-indicator {
|
71
84
|
padding: 0 8px;
|
72
85
|
|
@@ -8,40 +8,36 @@ import {SelfCheckResult} from '../../../../types/api/healthcheck';
|
|
8
8
|
import {useTypedSelector, useAutofetcher} from '../../../../utils/hooks';
|
9
9
|
import {
|
10
10
|
getHealthcheckInfo,
|
11
|
-
|
12
|
-
|
11
|
+
selectIssuesStatistics,
|
12
|
+
selectIssuesTrees,
|
13
13
|
setDataWasNotLoaded,
|
14
14
|
} from '../../../../store/reducers/healthcheckInfo';
|
15
|
+
import {DiagnosticCard} from '../../../../components/DiagnosticCard/DiagnosticCard';
|
15
16
|
|
16
17
|
import {Details} from './Details';
|
17
18
|
import {Preview} from './Preview';
|
18
19
|
|
19
|
-
import i18n from './i18n';
|
20
20
|
import './Healthcheck.scss';
|
21
21
|
|
22
22
|
interface HealthcheckProps {
|
23
23
|
tenant: string;
|
24
24
|
preview?: boolean;
|
25
25
|
fetchData?: boolean;
|
26
|
-
|
27
|
-
showMoreHandler?: (id: string) => void;
|
26
|
+
showMoreHandler?: VoidFunction;
|
28
27
|
}
|
29
28
|
|
30
29
|
const b = cn('healthcheck');
|
31
30
|
|
32
31
|
export const Healthcheck = (props: HealthcheckProps) => {
|
33
|
-
const {tenant, preview, fetchData = true, showMoreHandler
|
32
|
+
const {tenant, preview, fetchData = true, showMoreHandler} = props;
|
34
33
|
|
35
34
|
const dispatch = useDispatch();
|
36
35
|
|
37
36
|
const {data, loading, wasLoaded, error} = useTypedSelector((state) => state.healthcheckInfo);
|
38
37
|
const selfCheckResult = data?.self_check_result || SelfCheckResult.UNSPECIFIED;
|
39
38
|
|
40
|
-
const
|
41
|
-
const
|
42
|
-
selectIssuesTreeById(state, expandedIssueId),
|
43
|
-
);
|
44
|
-
|
39
|
+
const issuesStatistics = useTypedSelector(selectIssuesStatistics);
|
40
|
+
const issueTrees = useTypedSelector(selectIssuesTrees);
|
45
41
|
const {autorefresh} = useTypedSelector((state) => state.schema);
|
46
42
|
|
47
43
|
const fetchHealthcheck = useCallback(
|
@@ -66,37 +62,30 @@ export const Healthcheck = (props: HealthcheckProps) => {
|
|
66
62
|
);
|
67
63
|
|
68
64
|
const renderContent = () => {
|
69
|
-
if (error) {
|
70
|
-
return error.statusText;
|
71
|
-
}
|
72
|
-
|
73
65
|
if (loading && !wasLoaded) {
|
74
66
|
return (
|
75
|
-
<
|
67
|
+
<DiagnosticCard className={b('loader')}>
|
76
68
|
<Loader size="m" />
|
77
|
-
</
|
69
|
+
</DiagnosticCard>
|
78
70
|
);
|
79
71
|
}
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
}
|
98
|
-
|
99
|
-
return <div className="error">{i18n('no-data')}</div>;
|
72
|
+
return preview ? (
|
73
|
+
<Preview
|
74
|
+
issuesStatistics={issuesStatistics}
|
75
|
+
selfCheckResult={selfCheckResult}
|
76
|
+
loading={loading}
|
77
|
+
onShowMore={showMoreHandler}
|
78
|
+
onUpdate={fetchHealthcheck}
|
79
|
+
error={error}
|
80
|
+
/>
|
81
|
+
) : (
|
82
|
+
<Details
|
83
|
+
loading={loading}
|
84
|
+
onUpdate={fetchHealthcheck}
|
85
|
+
issueTrees={issueTrees}
|
86
|
+
error={error}
|
87
|
+
/>
|
88
|
+
);
|
100
89
|
};
|
101
90
|
|
102
91
|
return <div className={b({expanded: !preview})}>{renderContent()}</div>;
|
@@ -1,13 +1,14 @@
|
|
1
1
|
import cn from 'bem-cn-lite';
|
2
2
|
|
3
|
-
import {Button, Icon} from '@gravity-ui/uikit';
|
3
|
+
import {Button, Icon, Link} from '@gravity-ui/uikit';
|
4
4
|
|
5
5
|
import updateArrow from '../../../../../assets/icons/update-arrow.svg';
|
6
6
|
|
7
|
-
import {SelfCheckResult} from '../../../../../types/api/healthcheck';
|
8
|
-
import type {
|
9
|
-
|
10
|
-
import
|
7
|
+
import {SelfCheckResult, type StatusFlag} from '../../../../../types/api/healthcheck';
|
8
|
+
import type {IResponseError} from '../../../../../types/api/error';
|
9
|
+
import {DiagnosticCard} from '../../../../../components/DiagnosticCard/DiagnosticCard';
|
10
|
+
import EntityStatus from '../../../../../components/EntityStatus/EntityStatus';
|
11
|
+
import {ResponseError} from '../../../../../components/Errors/ResponseError';
|
11
12
|
|
12
13
|
import i18n from '../i18n';
|
13
14
|
|
@@ -15,21 +16,18 @@ const b = cn('healthcheck');
|
|
15
16
|
|
16
17
|
interface PreviewProps {
|
17
18
|
selfCheckResult: SelfCheckResult;
|
18
|
-
|
19
|
+
issuesStatistics?: [StatusFlag, number][];
|
19
20
|
loading?: boolean;
|
20
|
-
onShowMore?:
|
21
|
+
onShowMore?: VoidFunction;
|
21
22
|
onUpdate: VoidFunction;
|
23
|
+
error?: IResponseError;
|
22
24
|
}
|
23
25
|
|
24
26
|
export const Preview = (props: PreviewProps) => {
|
25
|
-
const {selfCheckResult,
|
27
|
+
const {selfCheckResult, issuesStatistics, loading, onShowMore, onUpdate, error} = props;
|
26
28
|
|
27
29
|
const isStatusOK = selfCheckResult === SelfCheckResult.GOOD;
|
28
30
|
|
29
|
-
if (!issuesTrees) {
|
30
|
-
return null;
|
31
|
-
}
|
32
|
-
|
33
31
|
const renderStatus = () => {
|
34
32
|
const modifier = selfCheckResult.toLowerCase();
|
35
33
|
|
@@ -46,26 +44,40 @@ export const Preview = (props: PreviewProps) => {
|
|
46
44
|
);
|
47
45
|
};
|
48
46
|
|
49
|
-
const
|
47
|
+
const renderContent = () => {
|
48
|
+
if (error) {
|
49
|
+
return <ResponseError error={error} defaultMessage={i18n('no-data')} />;
|
50
|
+
}
|
51
|
+
|
50
52
|
return (
|
51
53
|
<div className={b('preview-content')}>
|
52
|
-
{isStatusOK
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
{isStatusOK || !issuesStatistics || !issuesStatistics.length ? (
|
55
|
+
i18n('status_message.ok')
|
56
|
+
) : (
|
57
|
+
<>
|
58
|
+
<div>Issues:</div>
|
59
|
+
<div className={b('issues-statistics')}>
|
60
|
+
{issuesStatistics.map(([status, count]) => (
|
61
|
+
<EntityStatus
|
62
|
+
key={status}
|
63
|
+
mode="icons"
|
64
|
+
status={status}
|
65
|
+
label={count.toString()}
|
66
|
+
size="l"
|
67
|
+
/>
|
68
|
+
))}
|
69
|
+
</div>
|
70
|
+
<Link onClick={() => onShowMore?.()}>{i18n('label.show-details')}</Link>
|
71
|
+
</>
|
72
|
+
)}
|
61
73
|
</div>
|
62
74
|
);
|
63
75
|
};
|
64
76
|
|
65
77
|
return (
|
66
|
-
<
|
78
|
+
<DiagnosticCard className={b('preview')}>
|
67
79
|
{renderStatus()}
|
68
|
-
{
|
69
|
-
</
|
80
|
+
{renderContent()}
|
81
|
+
</DiagnosticCard>
|
70
82
|
);
|
71
83
|
};
|
@@ -0,0 +1,154 @@
|
|
1
|
+
import cn from 'bem-cn-lite';
|
2
|
+
import {useCallback} from 'react';
|
3
|
+
import {useDispatch} from 'react-redux';
|
4
|
+
|
5
|
+
import {Loader} from '@gravity-ui/uikit';
|
6
|
+
|
7
|
+
import {InfoViewer} from '../../../../components/InfoViewer';
|
8
|
+
import {PoolUsage} from '../../../../components/PoolUsage/PoolUsage';
|
9
|
+
import {Tablet} from '../../../../components/Tablet';
|
10
|
+
import EntityStatus from '../../../../components/EntityStatus/EntityStatus';
|
11
|
+
import {formatCPU} from '../../../../utils';
|
12
|
+
import {TABLET_STATES, TENANT_DEFAULT_TITLE} from '../../../../utils/constants';
|
13
|
+
import {bytesToGB} from '../../../../utils/utils';
|
14
|
+
import {mapDatabaseTypeToDBName} from '../../utils/schema';
|
15
|
+
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
16
|
+
import {ETabletVolatileState} from '../../../../types/api/tenant';
|
17
|
+
import {getTenantInfo, setDataWasNotLoaded} from '../../../../store/reducers/tenant/tenant';
|
18
|
+
|
19
|
+
import i18n from './i18n';
|
20
|
+
import './TenantOverview.scss';
|
21
|
+
|
22
|
+
const b = cn('tenant-overview');
|
23
|
+
|
24
|
+
interface TenantOverviewProps {
|
25
|
+
tenantName: string;
|
26
|
+
additionalTenantInfo?: any;
|
27
|
+
}
|
28
|
+
|
29
|
+
export function TenantOverview({tenantName, additionalTenantInfo}: TenantOverviewProps) {
|
30
|
+
const {tenant, loading, wasLoaded} = useTypedSelector((state) => state.tenant);
|
31
|
+
const {autorefresh} = useTypedSelector((state) => state.schema);
|
32
|
+
const dispatch = useDispatch();
|
33
|
+
const fetchTenant = useCallback(
|
34
|
+
(isBackground = true) => {
|
35
|
+
if (!isBackground) {
|
36
|
+
dispatch(setDataWasNotLoaded());
|
37
|
+
}
|
38
|
+
dispatch(getTenantInfo({path: tenantName}));
|
39
|
+
},
|
40
|
+
[dispatch, tenantName],
|
41
|
+
);
|
42
|
+
|
43
|
+
useAutofetcher(fetchTenant, [fetchTenant], autorefresh);
|
44
|
+
|
45
|
+
const {
|
46
|
+
Metrics = {},
|
47
|
+
PoolStats,
|
48
|
+
StateStats = [],
|
49
|
+
MemoryUsed,
|
50
|
+
Name,
|
51
|
+
State,
|
52
|
+
CoresUsed,
|
53
|
+
StorageGroups,
|
54
|
+
StorageAllocatedSize,
|
55
|
+
Type,
|
56
|
+
SystemTablets,
|
57
|
+
} = tenant || {};
|
58
|
+
|
59
|
+
const tenantType = mapDatabaseTypeToDBName(Type);
|
60
|
+
const memoryRaw = MemoryUsed ?? Metrics.Memory;
|
61
|
+
|
62
|
+
const memory = (memoryRaw && bytesToGB(memoryRaw)) || i18n('no-data');
|
63
|
+
const storage = (Metrics.Storage && bytesToGB(Metrics.Storage)) || i18n('no-data');
|
64
|
+
const storageGroups = StorageGroups ?? i18n('no-data');
|
65
|
+
const blobStorage =
|
66
|
+
(StorageAllocatedSize && bytesToGB(StorageAllocatedSize)) || i18n('no-data');
|
67
|
+
const storageEfficiency =
|
68
|
+
Metrics.Storage && StorageAllocatedSize
|
69
|
+
? `${((parseInt(Metrics.Storage) * 100) / parseInt(StorageAllocatedSize)).toFixed(2)}%`
|
70
|
+
: i18n('no-data');
|
71
|
+
|
72
|
+
const cpuRaw = CoresUsed !== undefined ? Number(CoresUsed) * 1_000_000 : Metrics.CPU;
|
73
|
+
|
74
|
+
const cpu = formatCPU(cpuRaw);
|
75
|
+
|
76
|
+
const metricsInfo = [
|
77
|
+
{label: 'Type', value: Type},
|
78
|
+
{label: 'Memory', value: memory},
|
79
|
+
{label: 'CPU', value: cpu},
|
80
|
+
{label: 'Tablet storage', value: storage},
|
81
|
+
{label: 'Storage groups', value: storageGroups},
|
82
|
+
{label: 'Blob storage', value: blobStorage},
|
83
|
+
{label: 'Storage efficiency', value: storageEfficiency},
|
84
|
+
];
|
85
|
+
|
86
|
+
const tabletsInfo = StateStats.filter(
|
87
|
+
(item): item is {VolatileState: ETabletVolatileState; Count: number} => {
|
88
|
+
return item.VolatileState !== undefined && item.Count !== undefined;
|
89
|
+
},
|
90
|
+
).map((info) => {
|
91
|
+
return {label: TABLET_STATES[info.VolatileState], value: info.Count};
|
92
|
+
});
|
93
|
+
|
94
|
+
const renderName = () => {
|
95
|
+
return (
|
96
|
+
<div className={b('tenant-name-wrapper')}>
|
97
|
+
<EntityStatus
|
98
|
+
status={State}
|
99
|
+
name={Name || TENANT_DEFAULT_TITLE}
|
100
|
+
withLeftTrim
|
101
|
+
hasClipboardButton={Boolean(tenant)}
|
102
|
+
clipboardButtonAlwaysVisible
|
103
|
+
/>
|
104
|
+
</div>
|
105
|
+
);
|
106
|
+
};
|
107
|
+
|
108
|
+
if (loading && !wasLoaded) {
|
109
|
+
return (
|
110
|
+
<div className={b('loader')}>
|
111
|
+
<Loader size="m" />
|
112
|
+
</div>
|
113
|
+
);
|
114
|
+
}
|
115
|
+
|
116
|
+
return (
|
117
|
+
<div className={b()}>
|
118
|
+
<div className={b('top-label')}>{tenantType}</div>
|
119
|
+
<div className={b('top')}>
|
120
|
+
{renderName()}
|
121
|
+
{tenant && additionalTenantInfo && additionalTenantInfo(tenant.Name, tenant.Type)}
|
122
|
+
</div>
|
123
|
+
<div className={b('system-tablets')}>
|
124
|
+
{SystemTablets &&
|
125
|
+
SystemTablets.map((tablet, tabletIndex) => (
|
126
|
+
<Tablet key={tabletIndex} tablet={tablet} tenantName={Name} />
|
127
|
+
))}
|
128
|
+
</div>
|
129
|
+
<div className={b('common-info')}>
|
130
|
+
<div>
|
131
|
+
<div className={b('section-title')}>{i18n('title.pools')}</div>
|
132
|
+
{PoolStats ? (
|
133
|
+
<div className={b('section', {pools: true})}>
|
134
|
+
{PoolStats.map((pool, poolIndex) => (
|
135
|
+
<PoolUsage key={poolIndex} data={pool} />
|
136
|
+
))}
|
137
|
+
</div>
|
138
|
+
) : (
|
139
|
+
<div className="error">{i18n('no-pools-data')}</div>
|
140
|
+
)}
|
141
|
+
</div>
|
142
|
+
<InfoViewer
|
143
|
+
title={i18n('title.metrics')}
|
144
|
+
className={b('section', {metrics: true})}
|
145
|
+
info={metricsInfo}
|
146
|
+
/>
|
147
|
+
|
148
|
+
<div className={b('section')}>
|
149
|
+
<InfoViewer info={tabletsInfo} title="Tablets" />
|
150
|
+
</div>
|
151
|
+
</div>
|
152
|
+
</div>
|
153
|
+
);
|
154
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import {i18n, Lang} from '../../../../../utils/i18n';
|
2
|
+
|
3
|
+
import en from './en.json';
|
4
|
+
import ru from './ru.json';
|
5
|
+
|
6
|
+
const COMPONENT = 'ydb-diagnostics-tenant-overview';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|