ydb-embedded-ui 4.16.1 → 4.17.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +23 -0
- package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +4 -4
- package/dist/components/CircularProgressBar/CircularProgressBar.scss +42 -0
- package/dist/components/CircularProgressBar/CircularProgressBar.tsx +59 -0
- package/dist/components/DiagnosticCard/DiagnosticCard.scss +20 -3
- package/dist/components/DiagnosticCard/DiagnosticCard.tsx +5 -6
- package/dist/containers/Cluster/Cluster.tsx +4 -4
- package/dist/containers/Node/Node.tsx +4 -4
- package/dist/containers/Node/NodeStructure/NodeStructure.tsx +5 -5
- package/dist/containers/Nodes/Nodes.tsx +3 -3
- package/dist/containers/Storage/Storage.tsx +3 -3
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +3 -3
- package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +1 -5
- package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +41 -18
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +4 -4
- package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Details/Details.tsx +3 -3
- package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Healthcheck.scss +27 -14
- package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Healthcheck.tsx +6 -6
- package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Preview/Preview.tsx +15 -16
- package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/Healthcheck.scss +108 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckDetails.tsx +45 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckPreview.tsx +83 -0
- package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTree.scss +1 -3
- package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTree.tsx +1 -1
- package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/IssueTreeItem.tsx +1 -1
- package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/en.json +2 -1
- package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/index.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/ru.json +2 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.scss +52 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.tsx +48 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.scss +12 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +134 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/OldTenantOverview.tsx +155 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +3 -5
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +79 -89
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +3 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +3 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/useHealthcheck.ts +53 -0
- package/dist/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +5 -5
- package/dist/containers/Tenant/Tenant.tsx +4 -4
- package/dist/containers/Tenant/TenantPages.tsx +1 -0
- package/dist/containers/Tenant/utils/queryTemplates.ts +23 -23
- package/dist/containers/UserSettings/i18n/en.json +4 -1
- package/dist/containers/UserSettings/i18n/ru.json +4 -1
- package/dist/containers/UserSettings/settings.ts +7 -0
- package/dist/services/api.ts +6 -4
- package/dist/store/reducers/healthcheckInfo.ts +20 -12
- package/dist/store/reducers/settings/settings.ts +5 -0
- package/dist/store/reducers/tenant/constants.ts +7 -0
- package/dist/store/reducers/tenant/tenant.ts +15 -0
- package/dist/store/reducers/tenant/types.ts +5 -0
- package/dist/store/reducers/tenants/contants.ts +6 -0
- package/dist/store/reducers/tenants/types.ts +4 -0
- package/dist/store/reducers/tenants/utils.ts +114 -7
- package/dist/store/state-url-mapping.js +3 -0
- package/dist/styles/constants.scss +2 -0
- package/dist/types/api/tenant.ts +3 -0
- package/dist/utils/constants.ts +1 -0
- package/dist/utils/formatCPU/formatCPU.ts +20 -0
- package/dist/utils/formatCPU/i18n/en.json +3 -0
- package/dist/utils/formatCPU/i18n/index.ts +11 -0
- package/dist/utils/formatCPU/i18n/ru.json +3 -0
- package/package.json +1 -1
- /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Details/index.ts +0 -0
- /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Preview/index.ts +0 -0
- /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/index.ts +0 -0
- /package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/IssueTreeItem.scss +0 -0
- /package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/index.ts +0 -0
package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Preview/Preview.tsx
RENAMED
@@ -6,13 +6,12 @@ import updateArrow from '../../../../../assets/icons/update-arrow.svg';
|
|
6
6
|
|
7
7
|
import {SelfCheckResult, type StatusFlag} from '../../../../../types/api/healthcheck';
|
8
8
|
import type {IResponseError} from '../../../../../types/api/error';
|
9
|
-
import {DiagnosticCard} from '../../../../../components/DiagnosticCard/DiagnosticCard';
|
10
9
|
import EntityStatus from '../../../../../components/EntityStatus/EntityStatus';
|
11
10
|
import {ResponseError} from '../../../../../components/Errors/ResponseError';
|
12
11
|
|
13
|
-
import i18n from '
|
12
|
+
import i18n from '../../TenantOverview/Healthcheck/i18n';
|
14
13
|
|
15
|
-
const b = cn('healthcheck');
|
14
|
+
const b = cn('old-healthcheck');
|
16
15
|
|
17
16
|
interface PreviewProps {
|
18
17
|
selfCheckResult: SelfCheckResult;
|
@@ -26,20 +25,20 @@ interface PreviewProps {
|
|
26
25
|
export const Preview = (props: PreviewProps) => {
|
27
26
|
const {selfCheckResult, issuesStatistics, loading, onShowMore, onUpdate, error} = props;
|
28
27
|
|
29
|
-
const
|
30
|
-
|
31
|
-
const renderStatus = () => {
|
28
|
+
const renderHeader = () => {
|
32
29
|
const modifier = selfCheckResult.toLowerCase();
|
33
30
|
|
34
31
|
return (
|
35
|
-
<div className={b('
|
36
|
-
<div className={b('preview-title')}>
|
32
|
+
<div className={b('preview-header')}>
|
33
|
+
<div className={b('preview-title-wrapper')}>
|
34
|
+
<div className={b('preview-title')}>{i18n('title.healthcheck')}</div>
|
35
|
+
<Button size="s" onClick={onUpdate} loading={loading} view="flat-secondary">
|
36
|
+
<Icon data={updateArrow} width={20} height={20} />
|
37
|
+
</Button>
|
38
|
+
</div>
|
37
39
|
<div className={b('self-check-status-indicator', {[modifier]: true})}>
|
38
40
|
{selfCheckResult}
|
39
41
|
</div>
|
40
|
-
<Button size="s" onClick={onUpdate} loading={loading} view="flat-secondary">
|
41
|
-
<Icon data={updateArrow} width={20} height={20} />
|
42
|
-
</Button>
|
43
42
|
</div>
|
44
43
|
);
|
45
44
|
};
|
@@ -51,11 +50,11 @@ export const Preview = (props: PreviewProps) => {
|
|
51
50
|
|
52
51
|
return (
|
53
52
|
<div className={b('preview-content')}>
|
54
|
-
{
|
53
|
+
{!issuesStatistics || !issuesStatistics.length ? (
|
55
54
|
i18n('status_message.ok')
|
56
55
|
) : (
|
57
56
|
<>
|
58
|
-
<div>
|
57
|
+
<div>{i18n('label.issues')}</div>
|
59
58
|
<div className={b('issues-statistics')}>
|
60
59
|
{issuesStatistics.map(([status, count]) => (
|
61
60
|
<EntityStatus
|
@@ -75,9 +74,9 @@ export const Preview = (props: PreviewProps) => {
|
|
75
74
|
};
|
76
75
|
|
77
76
|
return (
|
78
|
-
<
|
79
|
-
{
|
77
|
+
<div className={b('preview')}>
|
78
|
+
{renderHeader()}
|
80
79
|
{renderContent()}
|
81
|
-
</
|
80
|
+
</div>
|
82
81
|
);
|
83
82
|
};
|
@@ -0,0 +1,108 @@
|
|
1
|
+
@import '../../../../../styles/mixins.scss';
|
2
|
+
|
3
|
+
.healthcheck {
|
4
|
+
display: flex;
|
5
|
+
|
6
|
+
&_expanded {
|
7
|
+
// Since most of the inner containers have fixed width, we can set fixed width here as well
|
8
|
+
// Thus we will get rid of unneeded layout shift when scrollbar appear
|
9
|
+
min-width: 885px;
|
10
|
+
}
|
11
|
+
|
12
|
+
&__issue-preview {
|
13
|
+
margin-bottom: 15px;
|
14
|
+
}
|
15
|
+
|
16
|
+
&__message-container {
|
17
|
+
padding: 15px 0;
|
18
|
+
}
|
19
|
+
|
20
|
+
&__details {
|
21
|
+
width: 872px;
|
22
|
+
}
|
23
|
+
|
24
|
+
&__details-content-wrapper {
|
25
|
+
overflow-x: hidden;
|
26
|
+
}
|
27
|
+
|
28
|
+
&__preview {
|
29
|
+
height: 100%;
|
30
|
+
}
|
31
|
+
|
32
|
+
&__preview-header {
|
33
|
+
margin-bottom: var(--diagnostics-section-title-margin);
|
34
|
+
gap: 8px;
|
35
|
+
}
|
36
|
+
|
37
|
+
&__preview-title {
|
38
|
+
font-weight: 600;
|
39
|
+
@include lead-typography();
|
40
|
+
}
|
41
|
+
|
42
|
+
&__preview-content {
|
43
|
+
line-height: 24px;
|
44
|
+
}
|
45
|
+
|
46
|
+
&__preview-title-wrapper {
|
47
|
+
display: flex;
|
48
|
+
align-items: center;
|
49
|
+
|
50
|
+
margin-bottom: 4px;
|
51
|
+
|
52
|
+
gap: 8px;
|
53
|
+
}
|
54
|
+
|
55
|
+
&__issues-statistics {
|
56
|
+
display: flex;
|
57
|
+
flex-wrap: wrap;
|
58
|
+
align-items: center;
|
59
|
+
|
60
|
+
margin: 8px 0;
|
61
|
+
|
62
|
+
gap: 10px;
|
63
|
+
}
|
64
|
+
|
65
|
+
&__self-check-status-indicator {
|
66
|
+
display: inline-block;
|
67
|
+
|
68
|
+
padding: 0 8px;
|
69
|
+
|
70
|
+
font-size: 13px;
|
71
|
+
line-height: 24px;
|
72
|
+
|
73
|
+
border-radius: 4px;
|
74
|
+
|
75
|
+
&_good,
|
76
|
+
&_green {
|
77
|
+
color: var(--yc-color-text-positive);
|
78
|
+
background-color: var(--yc-color-base-positive);
|
79
|
+
}
|
80
|
+
&_degraded,
|
81
|
+
&_yellow {
|
82
|
+
color: var(--yc-color-text-warning);
|
83
|
+
background-color: var(--yc-color-base-warning);
|
84
|
+
}
|
85
|
+
|
86
|
+
&_blue {
|
87
|
+
color: var(--yc-color-text-info);
|
88
|
+
background-color: var(--yc-color-base-info);
|
89
|
+
}
|
90
|
+
|
91
|
+
&_emergency,
|
92
|
+
&_red {
|
93
|
+
color: var(--yc-color-text-danger);
|
94
|
+
background-color: var(--yc-color-base-danger);
|
95
|
+
}
|
96
|
+
&_unspecified,
|
97
|
+
&_gray,
|
98
|
+
&_grey {
|
99
|
+
color: var(--yc-color-text-misc);
|
100
|
+
background-color: var(--yc-color-base-misc);
|
101
|
+
}
|
102
|
+
&_maintenance_required,
|
103
|
+
&_orange {
|
104
|
+
color: var(--yc-color-text-warning-heavy);
|
105
|
+
background-color: var(--yc-color-infographics-warning-light);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import cn from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import type {IResponseError} from '../../../../../types/api/error';
|
4
|
+
import type {IIssuesTree} from '../../../../../types/store/healthcheck';
|
5
|
+
import {ResponseError} from '../../../../../components/Errors/ResponseError';
|
6
|
+
|
7
|
+
import IssueTree from './IssuesViewer/IssueTree';
|
8
|
+
|
9
|
+
import i18n from './i18n';
|
10
|
+
import './Healthcheck.scss';
|
11
|
+
|
12
|
+
const b = cn('healthcheck');
|
13
|
+
|
14
|
+
interface HealthcheckDetailsProps {
|
15
|
+
issueTrees?: IIssuesTree[];
|
16
|
+
error?: IResponseError;
|
17
|
+
}
|
18
|
+
|
19
|
+
export function HealthcheckDetails(props: HealthcheckDetailsProps) {
|
20
|
+
const {issueTrees, error} = props;
|
21
|
+
|
22
|
+
const renderContent = () => {
|
23
|
+
if (error) {
|
24
|
+
return <ResponseError error={error} defaultMessage={i18n('no-data')} />;
|
25
|
+
}
|
26
|
+
|
27
|
+
if (!issueTrees || !issueTrees.length) {
|
28
|
+
return i18n('status_message.ok');
|
29
|
+
}
|
30
|
+
|
31
|
+
return (
|
32
|
+
<>
|
33
|
+
{issueTrees.map((issueTree) => (
|
34
|
+
<IssueTree key={issueTree.id} issueTree={issueTree} />
|
35
|
+
))}
|
36
|
+
</>
|
37
|
+
);
|
38
|
+
};
|
39
|
+
|
40
|
+
return (
|
41
|
+
<div className={b('details')}>
|
42
|
+
<div className={b('details-content-wrapper')}>{renderContent()}</div>
|
43
|
+
</div>
|
44
|
+
);
|
45
|
+
}
|
@@ -0,0 +1,83 @@
|
|
1
|
+
import cn from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import {Button, Icon} from '@gravity-ui/uikit';
|
4
|
+
|
5
|
+
import updateArrow from '../../../../../assets/icons/update-arrow.svg';
|
6
|
+
|
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';
|
12
|
+
|
13
|
+
import i18n from './i18n';
|
14
|
+
import './Healthcheck.scss';
|
15
|
+
|
16
|
+
const b = cn('healthcheck');
|
17
|
+
|
18
|
+
interface HealthcheckPreviewProps {
|
19
|
+
selfCheckResult: SelfCheckResult;
|
20
|
+
issuesStatistics?: [StatusFlag, number][];
|
21
|
+
loading?: boolean;
|
22
|
+
onUpdate: VoidFunction;
|
23
|
+
error?: IResponseError;
|
24
|
+
active?: boolean;
|
25
|
+
}
|
26
|
+
|
27
|
+
export function HealthcheckPreview(props: HealthcheckPreviewProps) {
|
28
|
+
const {selfCheckResult, issuesStatistics, loading, onUpdate, error, active} = props;
|
29
|
+
|
30
|
+
const renderHeader = () => {
|
31
|
+
const modifier = selfCheckResult.toLowerCase();
|
32
|
+
|
33
|
+
return (
|
34
|
+
<div className={b('preview-header')}>
|
35
|
+
<div className={b('preview-title-wrapper')}>
|
36
|
+
<div className={b('preview-title')}>{i18n('title.healthcheck')}</div>
|
37
|
+
<Button size="s" onClick={onUpdate} loading={loading} view="flat-secondary">
|
38
|
+
<Icon data={updateArrow} width={20} height={20} />
|
39
|
+
</Button>
|
40
|
+
</div>
|
41
|
+
<div className={b('self-check-status-indicator', {[modifier]: true})}>
|
42
|
+
{selfCheckResult}
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
);
|
46
|
+
};
|
47
|
+
|
48
|
+
const renderContent = () => {
|
49
|
+
if (error) {
|
50
|
+
return <ResponseError error={error} defaultMessage={i18n('no-data')} />;
|
51
|
+
}
|
52
|
+
|
53
|
+
return (
|
54
|
+
<div className={b('preview-content')}>
|
55
|
+
{!issuesStatistics || !issuesStatistics.length ? (
|
56
|
+
i18n('status_message.ok')
|
57
|
+
) : (
|
58
|
+
<>
|
59
|
+
<div>{i18n('label.issues')}</div>
|
60
|
+
<div className={b('issues-statistics')}>
|
61
|
+
{issuesStatistics.map(([status, count]) => (
|
62
|
+
<EntityStatus
|
63
|
+
key={status}
|
64
|
+
mode="icons"
|
65
|
+
status={status}
|
66
|
+
label={count.toString()}
|
67
|
+
size="l"
|
68
|
+
/>
|
69
|
+
))}
|
70
|
+
</div>
|
71
|
+
</>
|
72
|
+
)}
|
73
|
+
</div>
|
74
|
+
);
|
75
|
+
};
|
76
|
+
|
77
|
+
return (
|
78
|
+
<DiagnosticCard className={b('preview')} active={active}>
|
79
|
+
{renderHeader()}
|
80
|
+
{renderContent()}
|
81
|
+
</DiagnosticCard>
|
82
|
+
);
|
83
|
+
}
|
@@ -7,7 +7,7 @@ import JSONTree from 'react-json-inspector';
|
|
7
7
|
|
8
8
|
import {TreeView} from 'ydb-ui-components';
|
9
9
|
|
10
|
-
import {IIssuesTree} from '
|
10
|
+
import {IIssuesTree} from '../../../../../../types/store/healthcheck';
|
11
11
|
|
12
12
|
import {IssueTreeItem} from './IssueTreeItem';
|
13
13
|
|
package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/en.json
RENAMED
@@ -2,6 +2,7 @@
|
|
2
2
|
"title.healthcheck": "Healthcheck",
|
3
3
|
"label.update": "Update",
|
4
4
|
"label.show-details": "Show details",
|
5
|
-
"
|
5
|
+
"label.issues": "Issues:",
|
6
|
+
"status_message.ok": "No issues",
|
6
7
|
"no-data": "no healthcheck data"
|
7
8
|
}
|
package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/ru.json
RENAMED
@@ -2,6 +2,7 @@
|
|
2
2
|
"title.healthcheck": "Healthcheck",
|
3
3
|
"label.update": "Обновить",
|
4
4
|
"label.show-details": "Посмотреть подробности",
|
5
|
-
"
|
5
|
+
"label.issues": "Проблемы:",
|
6
|
+
"status_message.ok": "Нет проблем",
|
6
7
|
"no-data": "нет данных healthcheck"
|
7
8
|
}
|
package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.scss
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
@import '../../../../../../styles/mixins.scss';
|
2
|
+
|
3
|
+
.ydb-metrics-card {
|
4
|
+
$b: &;
|
5
|
+
&__header {
|
6
|
+
display: flex;
|
7
|
+
justify-content: space-between;
|
8
|
+
align-items: center;
|
9
|
+
|
10
|
+
margin-bottom: 10px;
|
11
|
+
}
|
12
|
+
|
13
|
+
&__label {
|
14
|
+
font-weight: 600;
|
15
|
+
@include lead-typography();
|
16
|
+
}
|
17
|
+
|
18
|
+
&__content {
|
19
|
+
display: flex;
|
20
|
+
flex-direction: column;
|
21
|
+
align-items: center;
|
22
|
+
|
23
|
+
font-size: var(--yc-text-header-1-font-size);
|
24
|
+
line-height: var(--yc-text-header-1-line-height);
|
25
|
+
text-align: center;
|
26
|
+
|
27
|
+
color: var(--yc-color-text-secondary);
|
28
|
+
}
|
29
|
+
|
30
|
+
&__progress {
|
31
|
+
font-size: 36px;
|
32
|
+
font-weight: 500;
|
33
|
+
line-height: 40px;
|
34
|
+
text-align: center;
|
35
|
+
}
|
36
|
+
|
37
|
+
&__resources {
|
38
|
+
font-size: var(--yc-text-body-1-font-size);
|
39
|
+
|
40
|
+
color: var(--yc-color-text-secondary);
|
41
|
+
}
|
42
|
+
|
43
|
+
&_active {
|
44
|
+
#{$b}__content {
|
45
|
+
color: var(--yc-color-text-complementary);
|
46
|
+
}
|
47
|
+
|
48
|
+
#{$b}__progress-bar-circle-bg {
|
49
|
+
stroke: var(--yc-color-base-float);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.tsx
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
import cn from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import {CircularProgressBar} from '../../../../../../components/CircularProgressBar/CircularProgressBar';
|
4
|
+
import {DiagnosticCard} from '../../../../../../components/DiagnosticCard/DiagnosticCard';
|
5
|
+
import {formatUsage} from '../../../../../../store/reducers/tenants/utils';
|
6
|
+
import type {MetricStatus} from '../../../../../../store/reducers/tenants/types';
|
7
|
+
|
8
|
+
import i18n from '../../i18n';
|
9
|
+
|
10
|
+
import './MetricCard.scss';
|
11
|
+
|
12
|
+
const b = cn('ydb-metrics-card');
|
13
|
+
|
14
|
+
interface MetricCardProps {
|
15
|
+
active?: boolean;
|
16
|
+
progress?: number;
|
17
|
+
label?: string;
|
18
|
+
status?: MetricStatus;
|
19
|
+
resourcesUsed?: string;
|
20
|
+
}
|
21
|
+
|
22
|
+
export function MetricCard({active, progress, label, status, resourcesUsed}: MetricCardProps) {
|
23
|
+
const renderContent = () => {
|
24
|
+
return (
|
25
|
+
<div className={b('content')}>
|
26
|
+
{progress && <div className={b('progress')}>{formatUsage(progress)}</div>}
|
27
|
+
{resourcesUsed ? (
|
28
|
+
<div className={b('resources')}>{resourcesUsed}</div>
|
29
|
+
) : (
|
30
|
+
i18n('no-data')
|
31
|
+
)}
|
32
|
+
</div>
|
33
|
+
);
|
34
|
+
};
|
35
|
+
return (
|
36
|
+
<DiagnosticCard className={b({active})} active={active}>
|
37
|
+
<div className={b('header')}>{label && <div className={b('label')}>{label}</div>}</div>
|
38
|
+
<CircularProgressBar
|
39
|
+
size={172}
|
40
|
+
strokeWidth={11}
|
41
|
+
progress={progress || 0}
|
42
|
+
content={renderContent()}
|
43
|
+
status={status}
|
44
|
+
circleBgClassName={b('progress-bar-circle-bg')}
|
45
|
+
/>
|
46
|
+
</DiagnosticCard>
|
47
|
+
);
|
48
|
+
}
|
@@ -0,0 +1,134 @@
|
|
1
|
+
import cn from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import {Link, useLocation} from 'react-router-dom';
|
4
|
+
|
5
|
+
import type {TenantMetricsTab} from '../../../../../store/reducers/tenant/types';
|
6
|
+
import {TENANT_METRICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
|
7
|
+
import {useTypedSelector} from '../../../../../utils/hooks';
|
8
|
+
import {parseQuery} from '../../../../../routes';
|
9
|
+
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
|
10
|
+
import {
|
11
|
+
calculateUsage,
|
12
|
+
cpuUsageToStatus,
|
13
|
+
storageUsageToStatus,
|
14
|
+
memoryUsageToStatus,
|
15
|
+
formatTenantMetrics,
|
16
|
+
} from '../../../../../store/reducers/tenants/utils';
|
17
|
+
import type {SelfCheckResult, StatusFlag} from '../../../../../types/api/healthcheck';
|
18
|
+
import type {IResponseError} from '../../../../../types/api/error';
|
19
|
+
import {HealthcheckPreview} from '../Healthcheck/HealthcheckPreview';
|
20
|
+
import {MetricCard} from './MetricCard/MetricCard';
|
21
|
+
|
22
|
+
import './MetricsCards.scss';
|
23
|
+
|
24
|
+
const b = cn('metrics-cards');
|
25
|
+
|
26
|
+
export interface TenantMetrics {
|
27
|
+
memoryUsed?: number;
|
28
|
+
memoryLimit?: number;
|
29
|
+
cpuUsed?: number;
|
30
|
+
cpuLimit?: number;
|
31
|
+
storageUsed?: number;
|
32
|
+
storageLimit?: number;
|
33
|
+
}
|
34
|
+
|
35
|
+
interface MetricsCardsProps {
|
36
|
+
metrics?: TenantMetrics;
|
37
|
+
issuesStatistics?: [StatusFlag, number][];
|
38
|
+
selfCheckResult: SelfCheckResult;
|
39
|
+
fetchHealthcheck: VoidFunction;
|
40
|
+
healthcheckLoading?: boolean;
|
41
|
+
healthcheckError?: IResponseError;
|
42
|
+
}
|
43
|
+
|
44
|
+
export function MetricsCards({
|
45
|
+
metrics,
|
46
|
+
issuesStatistics,
|
47
|
+
selfCheckResult,
|
48
|
+
fetchHealthcheck,
|
49
|
+
healthcheckLoading,
|
50
|
+
healthcheckError,
|
51
|
+
}: MetricsCardsProps) {
|
52
|
+
const location = useLocation();
|
53
|
+
|
54
|
+
const {memoryUsed, memoryLimit, cpuUsed, cpuLimit, storageUsed, storageLimit} = metrics || {};
|
55
|
+
|
56
|
+
const {metricsTab} = useTypedSelector((state) => state.tenant);
|
57
|
+
|
58
|
+
const cpuUsage = calculateUsage(cpuUsed, cpuLimit);
|
59
|
+
const storageUsage = calculateUsage(storageUsed, storageLimit);
|
60
|
+
const memoryUsage = calculateUsage(memoryUsed, memoryLimit);
|
61
|
+
|
62
|
+
const cpuStatus = cpuUsageToStatus(cpuUsage);
|
63
|
+
const storageStatus = storageUsageToStatus(storageUsage);
|
64
|
+
const memoryStatus = memoryUsageToStatus(memoryUsage);
|
65
|
+
|
66
|
+
const {cpu, storage, memory} = formatTenantMetrics({
|
67
|
+
cpu: cpuUsed,
|
68
|
+
storage: storageUsed,
|
69
|
+
memory: memoryUsed,
|
70
|
+
});
|
71
|
+
|
72
|
+
const queryParams = parseQuery(location);
|
73
|
+
|
74
|
+
const tabLinks: Record<TenantMetricsTab, string> = {
|
75
|
+
[TENANT_METRICS_TABS_IDS.cpu]: getTenantPath({
|
76
|
+
...queryParams,
|
77
|
+
[TenantTabsGroups.metricsTab]: TENANT_METRICS_TABS_IDS.cpu,
|
78
|
+
}),
|
79
|
+
[TENANT_METRICS_TABS_IDS.storage]: getTenantPath({
|
80
|
+
...queryParams,
|
81
|
+
[TenantTabsGroups.metricsTab]: TENANT_METRICS_TABS_IDS.storage,
|
82
|
+
}),
|
83
|
+
[TENANT_METRICS_TABS_IDS.memory]: getTenantPath({
|
84
|
+
...queryParams,
|
85
|
+
[TenantTabsGroups.metricsTab]: TENANT_METRICS_TABS_IDS.memory,
|
86
|
+
}),
|
87
|
+
[TENANT_METRICS_TABS_IDS.healthcheck]: getTenantPath({
|
88
|
+
...queryParams,
|
89
|
+
[TenantTabsGroups.metricsTab]: TENANT_METRICS_TABS_IDS.healthcheck,
|
90
|
+
}),
|
91
|
+
};
|
92
|
+
|
93
|
+
return (
|
94
|
+
<div className={b()}>
|
95
|
+
<Link to={tabLinks.cpu} className={b('tab')}>
|
96
|
+
<MetricCard
|
97
|
+
label="CPU"
|
98
|
+
progress={cpuUsage}
|
99
|
+
status={cpuStatus}
|
100
|
+
resourcesUsed={cpu}
|
101
|
+
active={metricsTab === TENANT_METRICS_TABS_IDS.cpu}
|
102
|
+
/>
|
103
|
+
</Link>
|
104
|
+
<Link to={tabLinks.storage} className={b('tab')}>
|
105
|
+
<MetricCard
|
106
|
+
label="Storage"
|
107
|
+
progress={storageUsage}
|
108
|
+
status={storageStatus}
|
109
|
+
resourcesUsed={storage}
|
110
|
+
active={metricsTab === TENANT_METRICS_TABS_IDS.storage}
|
111
|
+
/>
|
112
|
+
</Link>
|
113
|
+
<Link to={tabLinks.memory} className={b('tab')}>
|
114
|
+
<MetricCard
|
115
|
+
label="Memory"
|
116
|
+
progress={memoryUsage}
|
117
|
+
status={memoryStatus}
|
118
|
+
resourcesUsed={memory}
|
119
|
+
active={metricsTab === TENANT_METRICS_TABS_IDS.memory}
|
120
|
+
/>
|
121
|
+
</Link>
|
122
|
+
<Link to={tabLinks.healthcheck} className={b('tab')}>
|
123
|
+
<HealthcheckPreview
|
124
|
+
selfCheckResult={selfCheckResult}
|
125
|
+
issuesStatistics={issuesStatistics}
|
126
|
+
onUpdate={fetchHealthcheck}
|
127
|
+
loading={healthcheckLoading}
|
128
|
+
error={healthcheckError}
|
129
|
+
active={metricsTab === TENANT_METRICS_TABS_IDS.healthcheck}
|
130
|
+
/>
|
131
|
+
</Link>
|
132
|
+
</div>
|
133
|
+
);
|
134
|
+
}
|