ydb-embedded-ui 4.16.2 → 4.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/components/CircularProgressBar/CircularProgressBar.scss +42 -0
  3. package/dist/components/CircularProgressBar/CircularProgressBar.tsx +59 -0
  4. package/dist/components/DiagnosticCard/DiagnosticCard.scss +20 -3
  5. package/dist/components/DiagnosticCard/DiagnosticCard.tsx +5 -6
  6. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +1 -5
  7. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +40 -17
  8. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Details/Details.tsx +3 -3
  9. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Healthcheck.scss +27 -14
  10. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Healthcheck.tsx +6 -6
  11. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Preview/Preview.tsx +15 -16
  12. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/Healthcheck.scss +108 -0
  13. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckDetails.tsx +45 -0
  14. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckPreview.tsx +83 -0
  15. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTree.scss +1 -3
  16. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTree.tsx +1 -1
  17. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/IssueTreeItem.tsx +1 -1
  18. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/en.json +2 -1
  19. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/index.ts +1 -1
  20. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/ru.json +2 -1
  21. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.scss +52 -0
  22. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.tsx +48 -0
  23. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.scss +12 -0
  24. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +134 -0
  25. package/dist/containers/Tenant/Diagnostics/TenantOverview/OldTenantOverview.tsx +155 -0
  26. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +3 -5
  27. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +76 -86
  28. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +3 -1
  29. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +3 -1
  30. package/dist/containers/Tenant/Diagnostics/TenantOverview/useHealthcheck.ts +53 -0
  31. package/dist/containers/Tenant/TenantPages.tsx +1 -0
  32. package/dist/containers/UserSettings/i18n/en.json +4 -1
  33. package/dist/containers/UserSettings/i18n/ru.json +4 -1
  34. package/dist/containers/UserSettings/settings.ts +7 -0
  35. package/dist/services/api.ts +6 -4
  36. package/dist/store/reducers/healthcheckInfo.ts +20 -12
  37. package/dist/store/reducers/settings/settings.ts +5 -0
  38. package/dist/store/reducers/tenant/constants.ts +7 -0
  39. package/dist/store/reducers/tenant/tenant.ts +15 -0
  40. package/dist/store/reducers/tenant/types.ts +5 -0
  41. package/dist/store/reducers/tenants/contants.ts +6 -0
  42. package/dist/store/reducers/tenants/types.ts +4 -0
  43. package/dist/store/reducers/tenants/utils.ts +114 -7
  44. package/dist/store/state-url-mapping.js +3 -0
  45. package/dist/styles/constants.scss +2 -0
  46. package/dist/types/api/tenant.ts +3 -0
  47. package/dist/utils/constants.ts +1 -0
  48. package/dist/utils/formatCPU/formatCPU.ts +20 -0
  49. package/dist/utils/formatCPU/i18n/en.json +3 -0
  50. package/dist/utils/formatCPU/i18n/index.ts +11 -0
  51. package/dist/utils/formatCPU/i18n/ru.json +3 -0
  52. package/package.json +1 -1
  53. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Details/index.ts +0 -0
  54. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Preview/index.ts +0 -0
  55. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/index.ts +0 -0
  56. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/IssueTreeItem.scss +0 -0
  57. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/index.ts +0 -0
@@ -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
+ }
@@ -0,0 +1,155 @@
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 type {ETabletVolatileState} from '../../../../types/api/tenant';
17
+ import type {AdditionalTenantsProps} from '../../../../types/additionalProps';
18
+ import {getTenantInfo, setDataWasNotLoaded} from '../../../../store/reducers/tenant/tenant';
19
+
20
+ import i18n from './i18n';
21
+ import './TenantOverview.scss';
22
+
23
+ const b = cn('tenant-overview');
24
+
25
+ interface OldTenantOverviewProps {
26
+ tenantName: string;
27
+ additionalTenantProps?: AdditionalTenantsProps;
28
+ }
29
+
30
+ export function OldTenantOverview({tenantName, additionalTenantProps}: OldTenantOverviewProps) {
31
+ const {tenant, loading, wasLoaded} = useTypedSelector((state) => state.tenant);
32
+ const {autorefresh} = useTypedSelector((state) => state.schema);
33
+ const dispatch = useDispatch();
34
+ const fetchTenant = useCallback(
35
+ (isBackground = true) => {
36
+ if (!isBackground) {
37
+ dispatch(setDataWasNotLoaded());
38
+ }
39
+ dispatch(getTenantInfo({path: tenantName}));
40
+ },
41
+ [dispatch, tenantName],
42
+ );
43
+
44
+ useAutofetcher(fetchTenant, [fetchTenant], autorefresh);
45
+
46
+ const {
47
+ Metrics = {},
48
+ PoolStats,
49
+ StateStats = [],
50
+ MemoryUsed,
51
+ Name,
52
+ State,
53
+ CoresUsed,
54
+ StorageGroups,
55
+ StorageAllocatedSize,
56
+ Type,
57
+ SystemTablets,
58
+ } = tenant || {};
59
+
60
+ const tenantType = mapDatabaseTypeToDBName(Type);
61
+ const memoryRaw = MemoryUsed ?? Metrics.Memory;
62
+
63
+ const memory = (memoryRaw && bytesToGB(memoryRaw)) || i18n('no-data');
64
+ const storage = (Metrics.Storage && bytesToGB(Metrics.Storage)) || i18n('no-data');
65
+ const storageGroups = StorageGroups ?? i18n('no-data');
66
+ const blobStorage =
67
+ (StorageAllocatedSize && bytesToGB(StorageAllocatedSize)) || i18n('no-data');
68
+ const storageEfficiency =
69
+ Metrics.Storage && StorageAllocatedSize
70
+ ? `${((parseInt(Metrics.Storage) * 100) / parseInt(StorageAllocatedSize)).toFixed(2)}%`
71
+ : i18n('no-data');
72
+
73
+ const cpuRaw = CoresUsed !== undefined ? Number(CoresUsed) * 1_000_000 : Metrics.CPU;
74
+
75
+ const cpu = formatCPU(cpuRaw);
76
+
77
+ const metricsInfo = [
78
+ {label: 'Type', value: Type},
79
+ {label: 'Memory', value: memory},
80
+ {label: 'CPU', value: cpu},
81
+ {label: 'Tablet storage', value: storage},
82
+ {label: 'Storage groups', value: storageGroups},
83
+ {label: 'Blob storage', value: blobStorage},
84
+ {label: 'Storage efficiency', value: storageEfficiency},
85
+ ];
86
+
87
+ const tabletsInfo = StateStats.filter(
88
+ (item): item is {VolatileState: ETabletVolatileState; Count: number} => {
89
+ return item.VolatileState !== undefined && item.Count !== undefined;
90
+ },
91
+ ).map((info) => {
92
+ return {label: TABLET_STATES[info.VolatileState], value: info.Count};
93
+ });
94
+
95
+ const renderName = () => {
96
+ return (
97
+ <div className={b('tenant-name-wrapper')}>
98
+ <EntityStatus
99
+ status={State}
100
+ name={Name || TENANT_DEFAULT_TITLE}
101
+ withLeftTrim
102
+ hasClipboardButton={Boolean(tenant)}
103
+ clipboardButtonAlwaysVisible
104
+ />
105
+ </div>
106
+ );
107
+ };
108
+
109
+ if (loading && !wasLoaded) {
110
+ return (
111
+ <div className={b('loader')}>
112
+ <Loader size="m" />
113
+ </div>
114
+ );
115
+ }
116
+
117
+ return (
118
+ <div className={b()}>
119
+ <div className={b('top-label')}>{tenantType}</div>
120
+ <div className={b('top')}>
121
+ {renderName()}
122
+ {additionalTenantProps?.getMonitoringLink?.(Name, Type)}
123
+ </div>
124
+ <div className={b('system-tablets')}>
125
+ {SystemTablets &&
126
+ SystemTablets.map((tablet, tabletIndex) => (
127
+ <Tablet key={tabletIndex} tablet={tablet} tenantName={Name} />
128
+ ))}
129
+ </div>
130
+ <div className={b('common-info')}>
131
+ <div>
132
+ <div className={b('section-title')}>{i18n('title.pools')}</div>
133
+ {PoolStats ? (
134
+ <div className={b('section', {pools: true})}>
135
+ {PoolStats.map((pool, poolIndex) => (
136
+ <PoolUsage key={poolIndex} data={pool} />
137
+ ))}
138
+ </div>
139
+ ) : (
140
+ <div className="error">{i18n('no-pools-data')}</div>
141
+ )}
142
+ </div>
143
+ <InfoViewer
144
+ title={i18n('title.metrics')}
145
+ className={b('section', {metrics: true})}
146
+ info={metricsInfo}
147
+ />
148
+
149
+ <div className={b('section')}>
150
+ <InfoViewer info={tabletsInfo} title="Tablets" />
151
+ </div>
152
+ </div>
153
+ </div>
154
+ );
155
+ }
@@ -1,7 +1,6 @@
1
- @use '../DetailedOverview/DetailedOverview.scss' as detailedOverview;
2
-
3
1
  .tenant-overview {
4
2
  padding-bottom: 20px;
3
+
5
4
  &__loader {
6
5
  display: flex;
7
6
  justify-content: center;
@@ -22,10 +21,10 @@
22
21
  }
23
22
 
24
23
  &__top-label {
25
- margin-bottom: detailedOverview.$section-title-margin;
24
+ margin-bottom: var(--diagnostics-section-title-margin);
26
25
 
27
26
  font-weight: 600;
28
- line-height: detailedOverview.$section-title-line-height;
27
+ line-height: 24px;
29
28
  gap: 10px;
30
29
  }
31
30
 
@@ -50,7 +49,6 @@
50
49
  font-size: var(--yc-text-body-2-font-size);
51
50
  font-weight: 500;
52
51
  line-height: var(--yc-text-body-2-line-height);
53
- //text-transform: uppercase;
54
52
  }
55
53
 
56
54
  &__section {