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.
Files changed (68) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +4 -4
  3. package/dist/components/CircularProgressBar/CircularProgressBar.scss +42 -0
  4. package/dist/components/CircularProgressBar/CircularProgressBar.tsx +59 -0
  5. package/dist/components/DiagnosticCard/DiagnosticCard.scss +20 -3
  6. package/dist/components/DiagnosticCard/DiagnosticCard.tsx +5 -6
  7. package/dist/containers/Cluster/Cluster.tsx +4 -4
  8. package/dist/containers/Node/Node.tsx +4 -4
  9. package/dist/containers/Node/NodeStructure/NodeStructure.tsx +5 -5
  10. package/dist/containers/Nodes/Nodes.tsx +3 -3
  11. package/dist/containers/Storage/Storage.tsx +3 -3
  12. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +3 -3
  13. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +1 -5
  14. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +41 -18
  15. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +4 -4
  16. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Details/Details.tsx +3 -3
  17. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Healthcheck.scss +27 -14
  18. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Healthcheck.tsx +6 -6
  19. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Preview/Preview.tsx +15 -16
  20. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/Healthcheck.scss +108 -0
  21. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckDetails.tsx +45 -0
  22. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckPreview.tsx +83 -0
  23. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTree.scss +1 -3
  24. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTree.tsx +1 -1
  25. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/IssueTreeItem.tsx +1 -1
  26. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/en.json +2 -1
  27. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/index.ts +1 -1
  28. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/ru.json +2 -1
  29. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.scss +52 -0
  30. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.tsx +48 -0
  31. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.scss +12 -0
  32. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +134 -0
  33. package/dist/containers/Tenant/Diagnostics/TenantOverview/OldTenantOverview.tsx +155 -0
  34. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +3 -5
  35. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +79 -89
  36. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +3 -1
  37. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +3 -1
  38. package/dist/containers/Tenant/Diagnostics/TenantOverview/useHealthcheck.ts +53 -0
  39. package/dist/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +5 -5
  40. package/dist/containers/Tenant/Tenant.tsx +4 -4
  41. package/dist/containers/Tenant/TenantPages.tsx +1 -0
  42. package/dist/containers/Tenant/utils/queryTemplates.ts +23 -23
  43. package/dist/containers/UserSettings/i18n/en.json +4 -1
  44. package/dist/containers/UserSettings/i18n/ru.json +4 -1
  45. package/dist/containers/UserSettings/settings.ts +7 -0
  46. package/dist/services/api.ts +6 -4
  47. package/dist/store/reducers/healthcheckInfo.ts +20 -12
  48. package/dist/store/reducers/settings/settings.ts +5 -0
  49. package/dist/store/reducers/tenant/constants.ts +7 -0
  50. package/dist/store/reducers/tenant/tenant.ts +15 -0
  51. package/dist/store/reducers/tenant/types.ts +5 -0
  52. package/dist/store/reducers/tenants/contants.ts +6 -0
  53. package/dist/store/reducers/tenants/types.ts +4 -0
  54. package/dist/store/reducers/tenants/utils.ts +114 -7
  55. package/dist/store/state-url-mapping.js +3 -0
  56. package/dist/styles/constants.scss +2 -0
  57. package/dist/types/api/tenant.ts +3 -0
  58. package/dist/utils/constants.ts +1 -0
  59. package/dist/utils/formatCPU/formatCPU.ts +20 -0
  60. package/dist/utils/formatCPU/i18n/en.json +3 -0
  61. package/dist/utils/formatCPU/i18n/index.ts +11 -0
  62. package/dist/utils/formatCPU/i18n/ru.json +3 -0
  63. package/package.json +1 -1
  64. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Details/index.ts +0 -0
  65. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Preview/index.ts +0 -0
  66. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/index.ts +0 -0
  67. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/IssueTreeItem.scss +0 -0
  68. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/index.ts +0 -0
@@ -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 '../i18n';
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 isStatusOK = selfCheckResult === SelfCheckResult.GOOD;
30
-
31
- const renderStatus = () => {
28
+ const renderHeader = () => {
32
29
  const modifier = selfCheckResult.toLowerCase();
33
30
 
34
31
  return (
35
- <div className={b('status-wrapper')}>
36
- <div className={b('preview-title')}>{i18n('title.healthcheck')}</div>
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
- {isStatusOK || !issuesStatistics || !issuesStatistics.length ? (
53
+ {!issuesStatistics || !issuesStatistics.length ? (
55
54
  i18n('status_message.ok')
56
55
  ) : (
57
56
  <>
58
- <div>Issues:</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
- <DiagnosticCard className={b('preview')}>
79
- {renderStatus()}
77
+ <div className={b('preview')}>
78
+ {renderHeader()}
80
79
  {renderContent()}
81
- </DiagnosticCard>
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
+ }
@@ -1,4 +1,4 @@
1
- @import '../../../../../styles/mixins.scss';
1
+ @import '../../../../../../styles/mixins.scss';
2
2
 
3
3
  .indicator {
4
4
  width: 12px;
@@ -38,8 +38,6 @@
38
38
  .issue-tree {
39
39
  display: flex;
40
40
 
41
- width: 820px;
42
-
43
41
  &__block {
44
42
  width: 100%;
45
43
  }
@@ -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 '../../../../../types/store/healthcheck';
10
+ import {IIssuesTree} from '../../../../../../types/store/healthcheck';
11
11
 
12
12
  import {IssueTreeItem} from './IssueTreeItem';
13
13
 
@@ -1,6 +1,6 @@
1
1
  import cn from 'bem-cn-lite';
2
2
 
3
- import EntityStatus from '../../../../../../components/EntityStatus/EntityStatus';
3
+ import EntityStatus from '../../../../../../../components/EntityStatus/EntityStatus';
4
4
 
5
5
  import './IssueTreeItem.scss';
6
6
 
@@ -2,6 +2,7 @@
2
2
  "title.healthcheck": "Healthcheck",
3
3
  "label.update": "Update",
4
4
  "label.show-details": "Show details",
5
- "status_message.ok": "No issues have been found on this database",
5
+ "label.issues": "Issues:",
6
+ "status_message.ok": "No issues",
6
7
  "no-data": "no healthcheck data"
7
8
  }
@@ -1,4 +1,4 @@
1
- import {i18n, Lang} from '../../../../../utils/i18n';
1
+ import {i18n, Lang} from '../../../../../../utils/i18n';
2
2
 
3
3
  import en from './en.json';
4
4
  import ru from './ru.json';
@@ -2,6 +2,7 @@
2
2
  "title.healthcheck": "Healthcheck",
3
3
  "label.update": "Обновить",
4
4
  "label.show-details": "Посмотреть подробности",
5
- "status_message.ok": "В базе данных нет проблем",
5
+ "label.issues": "Проблемы:",
6
+ "status_message.ok": "Нет проблем",
6
7
  "no-data": "нет данных healthcheck"
7
8
  }
@@ -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
+ }
@@ -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,12 @@
1
+ .metrics-cards {
2
+ display: flex;
3
+ gap: 16px;
4
+
5
+ margin-bottom: 32px;
6
+
7
+ &__tab {
8
+ text-decoration: none;
9
+
10
+ color: inherit;
11
+ }
12
+ }
@@ -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
+ }