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.
Files changed (50) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/components/DiagnosticCard/DiagnosticCard.scss +5 -0
  3. package/dist/components/DiagnosticCard/DiagnosticCard.tsx +17 -0
  4. package/dist/components/EntityStatus/EntityStatus.js +2 -2
  5. package/dist/components/EntityStatus/EntityStatus.scss +15 -0
  6. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +3 -11
  7. package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/Details.tsx +20 -11
  8. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +14 -1
  9. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +26 -37
  10. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx +37 -25
  11. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +154 -0
  12. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +7 -0
  13. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/index.ts +11 -0
  14. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +7 -0
  15. package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.scss +4 -1
  16. package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +42 -11
  17. package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +1 -1
  18. package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx +11 -7
  19. package/dist/containers/Tenant/Query/i18n/en.json +3 -0
  20. package/dist/containers/Tenant/Query/i18n/ru.json +3 -0
  21. package/dist/containers/Tenant/utils/queryTemplates.ts +30 -6
  22. package/dist/containers/Tenants/Tenants.tsx +3 -1
  23. package/dist/containers/UserSettings/Setting.tsx +9 -2
  24. package/dist/containers/UserSettings/i18n/en.json +5 -1
  25. package/dist/containers/UserSettings/i18n/ru.json +5 -1
  26. package/dist/containers/UserSettings/settings.ts +25 -0
  27. package/dist/services/api.ts +16 -16
  28. package/dist/store/reducers/executeQuery.ts +33 -7
  29. package/dist/store/reducers/explainQuery.ts +12 -4
  30. package/dist/store/reducers/healthcheckInfo.ts +27 -11
  31. package/dist/store/reducers/settings/settings.ts +4 -10
  32. package/dist/store/reducers/tenant/tenant.ts +19 -1
  33. package/dist/store/reducers/tenant/types.ts +3 -1
  34. package/dist/store/reducers/tenants/selectors.ts +1 -1
  35. package/dist/store/reducers/tenants/utils.ts +2 -2
  36. package/dist/types/api/tenant.ts +19 -20
  37. package/dist/types/store/executeQuery.ts +11 -1
  38. package/dist/types/store/query.ts +2 -1
  39. package/dist/utils/autofetcher.ts +7 -7
  40. package/dist/utils/constants.ts +2 -1
  41. package/dist/utils/hooks/i18n/en.json +1 -1
  42. package/dist/utils/hooks/i18n/ru.json +1 -1
  43. package/dist/utils/hooks/useQueryModes.ts +4 -2
  44. package/dist/utils/i18n/i18n.ts +10 -4
  45. package/dist/utils/query.ts +14 -0
  46. package/dist/utils/settings.ts +10 -0
  47. package/package.json +1 -1
  48. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/PreviewItem.tsx +0 -33
  49. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/index.ts +0 -1
  50. 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,5 @@
1
+ .diagnostic-card {
2
+ min-width: 200px;
3
+ max-width: 350px;
4
+ padding: 16px;
5
+ }
@@ -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
- //@ts-ignore
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 = (id: string) => {
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
- issueTree?: IIssuesTree;
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, issueTree} = props;
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 renderHealthcheckIssues = () => {
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
- <div className={b('issues-wrapper')}>
44
- <IssueTree issueTree={issueTree} />
45
- </div>
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
- {renderHealthcheckIssues()}
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
- &__issues-wrapper {
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
- selectIssuesTreeById,
12
- selectIssuesTreesRoots,
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
- expandedIssueId?: string;
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, expandedIssueId} = props;
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 issuesTreesRoots = useTypedSelector(selectIssuesTreesRoots);
41
- const expandedIssueTree = useTypedSelector((state) =>
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
- <div className={b('loader')}>
67
+ <DiagnosticCard className={b('loader')}>
76
68
  <Loader size="m" />
77
- </div>
69
+ </DiagnosticCard>
78
70
  );
79
71
  }
80
-
81
- if (data && data['self_check_result']) {
82
- return preview ? (
83
- <Preview
84
- issuesTrees={issuesTreesRoots}
85
- selfCheckResult={selfCheckResult}
86
- loading={loading}
87
- onShowMore={showMoreHandler}
88
- onUpdate={fetchHealthcheck}
89
- />
90
- ) : (
91
- <Details
92
- issueTree={expandedIssueTree}
93
- loading={loading}
94
- onUpdate={fetchHealthcheck}
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 {IIssuesTree} from '../../../../../types/store/healthcheck';
9
-
10
- import {PreviewItem} from './PreviewItem';
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
- issuesTrees?: IIssuesTree[];
19
+ issuesStatistics?: [StatusFlag, number][];
19
20
  loading?: boolean;
20
- onShowMore?: (id: string) => void;
21
+ onShowMore?: VoidFunction;
21
22
  onUpdate: VoidFunction;
23
+ error?: IResponseError;
22
24
  }
23
25
 
24
26
  export const Preview = (props: PreviewProps) => {
25
- const {selfCheckResult, issuesTrees, loading, onShowMore, onUpdate} = props;
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 renderFirstLevelIssues = () => {
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
- ? i18n('status_message.ok')
54
- : issuesTrees?.map((issueTree) => (
55
- <PreviewItem
56
- key={issueTree.id}
57
- data={issueTree}
58
- onShowMore={onShowMore}
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
- <div className={b('preview')}>
78
+ <DiagnosticCard className={b('preview')}>
67
79
  {renderStatus()}
68
- {renderFirstLevelIssues()}
69
- </div>
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,7 @@
1
+ {
2
+ "no-data": "No data",
3
+ "no-pools-data": "No pools data",
4
+
5
+ "title.pools": "Pools",
6
+ "title.metrics": "Metrics"
7
+ }
@@ -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);
@@ -0,0 +1,7 @@
1
+ {
2
+ "no-data": "Нет данных",
3
+ "no-pools-data": "Нет данных о пулах",
4
+
5
+ "title.pools": "Пулы",
6
+ "title.metrics": "Метрики"
7
+ }
@@ -9,11 +9,14 @@
9
9
  @include flex-container();
10
10
  @include table-styles;
11
11
 
12
+ &__table-row {
13
+ cursor: pointer;
14
+ }
15
+
12
16
  &__query {
13
17
  overflow: hidden;
14
18
  flex-grow: 1;
15
19
 
16
- cursor: pointer;
17
20
  white-space: pre;
18
21
  text-overflow: ellipsis;
19
22
  }