ydb-embedded-ui 3.3.1 → 3.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/components/Errors/ResponseError/ResponseError.tsx +17 -0
  3. package/dist/components/Errors/ResponseError/index.ts +1 -0
  4. package/dist/components/Errors/i18n/en.json +2 -1
  5. package/dist/components/Errors/i18n/ru.json +2 -1
  6. package/dist/components/FullGroupViewer/FullGroupViewer.js +1 -1
  7. package/dist/components/InfoViewer/InfoViewer.scss +1 -1
  8. package/dist/components/InfoViewer/InfoViewer.tsx +29 -21
  9. package/dist/components/InfoViewer/formatters/index.ts +1 -0
  10. package/dist/components/InfoViewer/formatters/table.ts +40 -0
  11. package/dist/components/QueryExecutionStatus/QueryExecutionStatus.tsx +26 -8
  12. package/dist/components/QueryExecutionStatus/index.ts +1 -0
  13. package/dist/components/QueryResultTable/QueryResultTable.tsx +2 -2
  14. package/dist/containers/App/Content.js +12 -5
  15. package/dist/containers/AsideNavigation/AsideNavigation.tsx +10 -13
  16. package/dist/containers/Authentication/Authentication.scss +6 -0
  17. package/dist/containers/Authentication/Authentication.tsx +34 -15
  18. package/dist/containers/Node/NodeStructure/Pdisk.tsx +7 -10
  19. package/dist/containers/Nodes/Nodes.tsx +1 -1
  20. package/dist/containers/Nodes/getNodesColumns.tsx +4 -4
  21. package/dist/containers/Storage/PDisk/PDisk.tsx +25 -17
  22. package/dist/containers/Storage/PDisk/__tests__/colors.tsx +64 -1
  23. package/dist/containers/Storage/Storage.js +1 -1
  24. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +4 -3
  25. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +1 -1
  26. package/dist/containers/Storage/VDisk/VDisk.tsx +1 -1
  27. package/dist/containers/Storage/utils/index.ts +26 -10
  28. package/dist/containers/Tablet/Tablet.js +1 -1
  29. package/dist/containers/Tenant/Acl/Acl.js +1 -1
  30. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +2 -2
  31. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
  32. package/dist/containers/Tenant/Diagnostics/OverloadedShards/OverloadedShards.tsx +6 -1
  33. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +10 -21
  34. package/dist/containers/Tenant/{Schema/SchemaInfoViewer/SchemaInfoViewer.scss → Diagnostics/Overview/TableInfo/TableInfo.scss} +8 -10
  35. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/TableInfo.tsx +71 -0
  36. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/en.json +5 -0
  37. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/index.ts +11 -0
  38. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/ru.json +5 -0
  39. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/index.ts +1 -0
  40. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +96 -0
  41. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +7 -1
  42. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +3 -1
  43. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +8 -3
  44. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +16 -11
  45. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +37 -23
  46. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +4 -0
  47. package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +1 -1
  48. package/dist/containers/Tenants/Tenants.js +4 -3
  49. package/dist/routes.ts +1 -0
  50. package/dist/services/api.js +4 -1
  51. package/dist/store/reducers/shardsWorkload.ts +2 -1
  52. package/dist/store/reducers/storage.js +1 -1
  53. package/dist/utils/constants.ts +1 -1
  54. package/dist/utils/index.js +3 -1
  55. package/dist/utils/prepareQueryExplain.ts +1 -1
  56. package/package.json +5 -3
  57. package/dist/containers/Tenant/Diagnostics/Overview/Overview.scss +0 -13
  58. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +0 -201
@@ -91,6 +91,8 @@ const initialTenantCommonInfoState = {
91
91
  function QueryEditor(props) {
92
92
  const [resultType, setResultType] = useState(RESULT_TYPES.EXECUTE);
93
93
 
94
+ const [isResultLoaded, setIsResultLoaded] = useState(false);
95
+
94
96
  const [resultVisibilityState, dispatchResultVisibilityState] = useReducer(
95
97
  paneVisibilityToggleReducerCreator(DEFAULT_IS_QUERY_RESULT_COLLAPSED),
96
98
  initialTenantCommonInfoState,
@@ -117,11 +119,12 @@ function QueryEditor(props) {
117
119
  }, []);
118
120
 
119
121
  useEffect(() => {
120
- const {showPreview} = props;
121
- if (showPreview && resultVisibilityState.collapsed) {
122
+ if (props.showPreview || isResultLoaded) {
122
123
  dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand);
124
+ } else {
125
+ dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerCollapse);
123
126
  }
124
- }, [props.showPreview, resultVisibilityState.collapsed]);
127
+ }, [props.showPreview, isResultLoaded]);
125
128
 
126
129
  useEffect(() => {
127
130
  const {
@@ -254,6 +257,7 @@ function QueryEditor(props) {
254
257
 
255
258
  setResultType(RESULT_TYPES.EXECUTE);
256
259
  sendQuery({query: input, database: path, action: runAction});
260
+ setIsResultLoaded(true);
257
261
  setShowPreview(false);
258
262
 
259
263
  const {queries, currentIndex} = history;
@@ -272,6 +276,7 @@ function QueryEditor(props) {
272
276
  } = props;
273
277
  setResultType(RESULT_TYPES.EXPLAIN);
274
278
  getExplainQuery({query: input, database: path});
279
+ setIsResultLoaded(true);
275
280
  setShowPreview(false);
276
281
  dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand);
277
282
  };
@@ -1,28 +1,33 @@
1
1
  import React, {useEffect, useRef, useState} from 'react';
2
+ import {useDispatch, useSelector} from 'react-redux';
2
3
  import cn from 'bem-cn-lite';
3
4
  import MonacoEditor from 'react-monaco-editor';
4
- import {Loader, RadioButton} from '@gravity-ui/uikit';
5
5
  import JSONTree from 'react-json-inspector';
6
- import {LANGUAGE_S_EXPRESSION_ID} from '../../../../utils/monaco';
6
+ import 'react-json-inspector/json-inspector.css';
7
+
7
8
  import {
8
9
  TextOverflow,
9
10
  getYdbPlanNodeShape,
10
11
  getCompactTopology,
11
12
  getTopology,
12
- } from '@yandex-cloud/paranoid';
13
- import {renderExplainNode} from '../../../../utils';
14
- import {explainVersions} from '../../../../store/reducers/explainQuery';
15
- import QueryExecutionStatus from '../../../../components/QueryExecutionStatus/QueryExecutionStatus';
13
+ } from '@gravity-ui/paranoid';
14
+ import {Loader, RadioButton} from '@gravity-ui/uikit';
15
+
16
16
  import Divider from '../../../../components/Divider/Divider';
17
17
  import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
18
- import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
19
18
  import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
19
+ import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus';
20
20
 
21
- import 'react-json-inspector/json-inspector.css';
22
- import './QueryExplain.scss';
23
- import {useDispatch, useSelector} from 'react-redux';
21
+ import {explainVersions} from '../../../../store/reducers/explainQuery';
24
22
  import {disableFullscreen} from '../../../../store/reducers/fullscreen';
25
23
 
24
+ import {renderExplainNode} from '../../../../utils';
25
+ import {LANGUAGE_S_EXPRESSION_ID} from '../../../../utils/monaco';
26
+
27
+ import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
28
+
29
+ import './QueryExplain.scss';
30
+
26
31
  const b = cn('kv-query-explain');
27
32
 
28
33
  const EDITOR_OPTIONS = {
@@ -263,7 +268,7 @@ function QueryExplain(props) {
263
268
  {!props.loading && (
264
269
  <React.Fragment>
265
270
  <div className={b('controls-right')}>
266
- <QueryExecutionStatus hasError={Boolean(props.error)} />
271
+ <QueryExecutionStatus error={props.error} />
267
272
  {!props.error && (
268
273
  <React.Fragment>
269
274
  <Divider />
@@ -1,20 +1,26 @@
1
1
  import React, {useEffect, useState} from 'react';
2
2
  import {useDispatch, useSelector} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
- import {RadioButton} from '@gravity-ui/uikit';
5
4
  import JSONTree from 'react-json-inspector';
6
5
 
6
+ import {RadioButton} from '@gravity-ui/uikit';
7
+
7
8
  import CopyToClipboard from '../../../../components/CopyToClipboard/CopyToClipboard';
8
9
  import Divider from '../../../../components/Divider/Divider';
10
+ import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
9
11
  import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
12
+ import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus';
13
+
10
14
  import {disableFullscreen} from '../../../../store/reducers/fullscreen';
11
15
 
12
- import './QueryResult.scss';
16
+ import {prepareQueryError} from '../../../../utils/query';
17
+
13
18
  import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
14
- import QueryExecutionStatus from '../../../../components/QueryExecutionStatus/QueryExecutionStatus';
15
- import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
19
+
16
20
  import ResultIssues from '../Issues/Issues';
17
21
 
22
+ import './QueryResult.scss';
23
+
18
24
  const b = cn('kv-query-result');
19
25
 
20
26
  const resultOptionsIds = {
@@ -94,31 +100,39 @@ function QueryResult(props) {
94
100
  };
95
101
 
96
102
  const renderIssues = () => {
97
- const error = props.error?.data;
98
-
99
- const hasIssues = error?.issues && Array.isArray(error.issues);
100
-
101
- return hasIssues ? (
102
- <React.Fragment>
103
- <ResultIssues data={error} />
104
- {isFullscreen && (
105
- <Fullscreen>
106
- <div className={b('result', {fullscreen: true})}>
107
- <ResultIssues data={error} />
108
- </div>
109
- </Fullscreen>
110
- )}
111
- </React.Fragment>
112
- ) : (
113
- <span>{error?.data ?? error}</span>
114
- );
103
+ const error = props.error;
104
+
105
+ const hasIssues = error?.data?.issues && Array.isArray(error.data.issues);
106
+
107
+ if (hasIssues) {
108
+ return (
109
+ <React.Fragment>
110
+ <ResultIssues data={error.data} />
111
+ {isFullscreen && (
112
+ <Fullscreen>
113
+ <div className={b('result', {fullscreen: true})}>
114
+ <ResultIssues data={error.data} />
115
+ </div>
116
+ </Fullscreen>
117
+ )}
118
+ </React.Fragment>
119
+ )
120
+ }
121
+
122
+ if (error) {
123
+ return (
124
+ <div className={b('error')}>
125
+ {prepareQueryError(error)}
126
+ </div>
127
+ );
128
+ }
115
129
  };
116
130
 
117
131
  return (
118
132
  <React.Fragment>
119
133
  <div className={b('controls')}>
120
134
  <div className={b('controls-right')}>
121
- <QueryExecutionStatus hasError={Boolean(props.error)} />
135
+ <QueryExecutionStatus error={props.error} />
122
136
 
123
137
  {props.stats && !props.error && (
124
138
  <React.Fragment>
@@ -20,6 +20,10 @@
20
20
  }
21
21
  }
22
22
 
23
+ &__error {
24
+ padding: 15px 10px;
25
+ }
26
+
23
27
  &__controls {
24
28
  position: sticky;
25
29
  z-index: 2;
@@ -5,7 +5,7 @@ import cn from 'bem-cn-lite';
5
5
  import find from 'lodash/find';
6
6
 
7
7
  import Icon from '../../../../components/Icon/Icon';
8
- import DataTable from '@yandex-cloud/react-data-table';
8
+ import DataTable from '@gravity-ui/react-data-table';
9
9
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
10
10
  import './SchemaViewer.scss';
11
11
 
@@ -5,7 +5,7 @@ import {connect} from 'react-redux';
5
5
  import _ from 'lodash';
6
6
  import {escapeRegExp} from 'lodash/fp';
7
7
 
8
- import DataTable from '@yandex-cloud/react-data-table';
8
+ import DataTable from '@gravity-ui/react-data-table';
9
9
  import {Loader, TextInput, Button} from '@gravity-ui/uikit';
10
10
 
11
11
  import EntityStatus from '../../components/EntityStatus/EntityStatus';
@@ -218,8 +218,9 @@ class Tenants extends React.Component {
218
218
  },
219
219
  accessor: ({Metrics = {}, CoresUsed}) => {
220
220
  if (!isNaN(Number(CoresUsed))) {
221
- const cores = Math.round(Number(CoresUsed) * 100) / 100;
222
- return cores || '—';
221
+ return Number(CoresUsed) * 100 > 1
222
+ ? formatCPU(Number(CoresUsed) * 1_000_000)
223
+ : '—';
223
224
  } else {
224
225
  return Number(Metrics.CPU) ? formatCPU(Number(Metrics.CPU)) : '—';
225
226
  }
package/dist/routes.ts CHANGED
@@ -16,6 +16,7 @@ const routes = {
16
16
  tablet: '/tablet/:id',
17
17
  tabletsFilters: '/tabletsFilters',
18
18
  clusterPage: '/clusters/:name',
19
+ auth: '/auth',
19
20
  };
20
21
 
21
22
  export const CLUSTER_PAGES = {
@@ -185,7 +185,10 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
185
185
  timeout: 600000,
186
186
  },
187
187
  null,
188
- {concurrentId},
188
+ {
189
+ concurrentId,
190
+ timeout: 9 * 60 * 1000,
191
+ },
189
192
  );
190
193
  }
191
194
  getExplainQuery(query, database) {
@@ -77,7 +77,8 @@ function createShardQuery(
77
77
  DataSize,
78
78
  NodeId,
79
79
  PeakTime,
80
- InFlightTxCount
80
+ InFlightTxCount,
81
+ IntervalEnd
81
82
  FROM \`.sys/top_partitions_one_hour\`
82
83
  WHERE ${where}
83
84
  ${orderBy}
@@ -420,7 +420,7 @@ export const getUsageFilterOptions = createSelector(getVisibleEntitiesList, (ent
420
420
  entities.forEach((entity) => {
421
421
  const usage = getUsage(entity, 5);
422
422
 
423
- if (!Object.hasOwn(items, usage)) {
423
+ if (!Object.prototype.hasOwnProperty.call(items, usage)) {
424
424
  items[usage] = 0;
425
425
  }
426
426
 
@@ -1,4 +1,4 @@
1
- import DataTable from '@yandex-cloud/react-data-table';
1
+ import DataTable from '@gravity-ui/react-data-table';
2
2
 
3
3
  const SECOND = 1000;
4
4
 
@@ -1,6 +1,8 @@
1
1
  import numeral from 'numeral';
2
2
  import locales from 'numeral/locales'; // eslint-disable-line no-unused-vars
3
3
 
4
+ import {dateTimeParse} from '@gravity-ui/date-utils';
5
+
4
6
  import {i18n} from './i18n';
5
7
  import {MEGABYTE, TERABYTE, GIGABYTE, DAY_IN_SECONDS} from './constants';
6
8
  import {isNumeric} from './utils';
@@ -76,7 +78,7 @@ export const formatDateTime = (value) => {
76
78
  return '';
77
79
  }
78
80
 
79
- return value > 0 ? new Date(Number(value)).toUTCString() : 'N/A';
81
+ return value > 0 ? dateTimeParse(Number(value)).format('YYYY-MM-DD HH:mm') : 'N/A';
80
82
  };
81
83
 
82
84
  export const calcUptimeInSeconds = (milliseconds) => {
@@ -5,7 +5,7 @@ import {
5
5
  TopologyNodeDataStatsSection,
6
6
  ExplainPlanNodeData,
7
7
  TopologyNodeDataStatsItem,
8
- } from '@yandex-cloud/paranoid';
8
+ } from '@gravity-ui/paranoid';
9
9
 
10
10
  interface PlanOperator {
11
11
  Name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "3.3.1",
3
+ "version": "3.3.3",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -10,10 +10,11 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@gravity-ui/axios-wrapper": "^1.3.0",
13
+ "@gravity-ui/date-utils": "^1.1.1",
13
14
  "@gravity-ui/i18n": "^1.0.0",
14
15
  "@gravity-ui/navigation": "^0.3.1",
15
- "@yandex-cloud/paranoid": "^1.3.0",
16
- "@yandex-cloud/react-data-table": "^1.0.2",
16
+ "@gravity-ui/paranoid": "^1.4.0",
17
+ "@gravity-ui/react-data-table": "^1.0.3",
17
18
  "axios": "0.19.2",
18
19
  "bem-cn-lite": "4.0.0",
19
20
  "history": "4.10.1",
@@ -107,6 +108,7 @@
107
108
  "@testing-library/react": "^11.2.7",
108
109
  "@testing-library/user-event": "^12.8.3",
109
110
  "@types/lodash": "^4.14.178",
111
+ "@types/numeral": "^2.0.2",
110
112
  "@types/qs": "^6.9.7",
111
113
  "@types/react": "^17.0.44",
112
114
  "@types/react-dom": "^17.0.11",
@@ -1,13 +0,0 @@
1
- .kv-tenant-overview {
2
- &__title {
3
- margin: 0;
4
- margin-bottom: 15px;
5
-
6
- font-weight: 600;
7
- }
8
- &__loader {
9
- display: flex;
10
- flex-grow: 1;
11
- justify-content: center;
12
- }
13
- }
@@ -1,201 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import cn from 'bem-cn-lite';
4
-
5
- import {formatCPU, formatBytes, formatNumber, formatBps, formatDateTime} from '../../../../utils';
6
-
7
- import {InfoViewer, createInfoFormatter} from '../../../../components/InfoViewer';
8
-
9
- import {getEntityName} from '../../utils';
10
-
11
- import './SchemaInfoViewer.scss';
12
-
13
- const b = cn('schema-info-viewer');
14
-
15
- const formatTabletMetricsItem = createInfoFormatter({
16
- values: {
17
- CPU: formatCPU,
18
- Memory: formatBytes,
19
- Storage: formatBytes,
20
- Network: formatBps,
21
- ReadThroughput: formatBps,
22
- WriteThroughput: formatBps,
23
- },
24
- defaultValueFormatter: formatNumber,
25
- });
26
-
27
- const formatFollowerGroupItem = createInfoFormatter({
28
- values: {
29
- FollowerCount: formatNumber,
30
- },
31
- });
32
-
33
- const formatPartitionConfigItem = createInfoFormatter({
34
- values: {
35
- FollowerCount: formatNumber,
36
- CrossDataCenterFollowerCount: formatNumber,
37
- },
38
- });
39
-
40
- const formatTableStatsItem = createInfoFormatter({
41
- values: {
42
- DataSize: formatBytes,
43
- IndexSize: formatBytes,
44
- LastAccessTime: formatDateTime,
45
- LastUpdateTime: formatDateTime,
46
- },
47
- defaultValueFormatter: formatNumber,
48
- });
49
-
50
- const formatTableStats = (fields) =>
51
- Object.entries(fields)
52
- .map(([label, value]) => formatTableStatsItem(label, value))
53
- .filter(({value}) => Boolean(value));
54
-
55
- class SchemaInfoViewer extends React.Component {
56
- static propTypes = {
57
- data: PropTypes.object.isRequired,
58
- };
59
-
60
- renderItem(itemData, title) {
61
- if (!Array.isArray(itemData) || !itemData.length) {
62
- return null;
63
- }
64
-
65
- return (
66
- <div className={b('item')}>
67
- <InfoViewer title={title} info={itemData} />
68
- </div>
69
- );
70
- }
71
-
72
- renderContent(data) {
73
- const {PathDescription = {}} = data;
74
- const entityName = getEntityName(PathDescription);
75
-
76
- const {
77
- TableStats = {},
78
- TabletMetrics = {},
79
- Table: {PartitionConfig = {}} = {},
80
- } = PathDescription;
81
- const {
82
- PartCount,
83
- RowCount,
84
- DataSize,
85
- IndexSize,
86
-
87
- LastAccessTime,
88
- LastUpdateTime,
89
-
90
- ImmediateTxCompleted,
91
- PlannedTxCompleted,
92
- TxRejectedByOverload,
93
- TxRejectedBySpace,
94
- TxCompleteLagMsec,
95
- InFlightTxCount,
96
-
97
- RowUpdates,
98
- RowDeletes,
99
- RowReads,
100
- RangeReads,
101
- RangeReadRows,
102
-
103
- ...restTableStats
104
- } = TableStats;
105
- const {FollowerGroups, FollowerCount, CrossDataCenterFollowerCount} = PartitionConfig;
106
-
107
- const generalTableInfo = formatTableStats({
108
- PartCount,
109
- RowCount,
110
- DataSize,
111
- IndexSize,
112
- ...restTableStats,
113
- });
114
-
115
- const tableStatsInfo = [
116
- formatTableStats({
117
- LastAccessTime,
118
- LastUpdateTime,
119
- }),
120
- formatTableStats({
121
- ImmediateTxCompleted,
122
- PlannedTxCompleted,
123
- TxRejectedByOverload,
124
- TxRejectedBySpace,
125
- TxCompleteLagMsec,
126
- InFlightTxCount,
127
- }),
128
- formatTableStats({
129
- RowUpdates,
130
- RowDeletes,
131
- RowReads,
132
- RangeReads,
133
- RangeReadRows,
134
- }),
135
- ];
136
-
137
- const tabletMetricsInfo = Object.keys(TabletMetrics).map((key) =>
138
- formatTabletMetricsItem(key, TabletMetrics[key]),
139
- );
140
-
141
- const partitionConfigInfo = [];
142
-
143
- if (Array.isArray(FollowerGroups) && FollowerGroups.length > 0) {
144
- partitionConfigInfo.push(
145
- ...Object.keys(FollowerGroups[0]).map((key) =>
146
- formatFollowerGroupItem(key, FollowerGroups[0][key]),
147
- ),
148
- );
149
- } else if (FollowerCount !== undefined) {
150
- partitionConfigInfo.push(formatPartitionConfigItem('FollowerCount', FollowerCount));
151
- } else if (CrossDataCenterFollowerCount !== undefined) {
152
- partitionConfigInfo.push(
153
- formatPartitionConfigItem(
154
- 'CrossDataCenterFollowerCount',
155
- CrossDataCenterFollowerCount,
156
- ),
157
- );
158
- }
159
-
160
- if (
161
- [generalTableInfo, tabletMetricsInfo, partitionConfigInfo, tableStatsInfo.flat()].flat()
162
- .length === 0
163
- ) {
164
- return <div className={b('title')}>{entityName}</div>;
165
- }
166
-
167
- return (
168
- <div>
169
- <div>{this.renderItem(generalTableInfo, entityName)}</div>
170
- <div className={b('row')}>
171
- {tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (
172
- <div className={b('col')}>
173
- {this.renderItem(tabletMetricsInfo, 'Tablet Metrics')}
174
- {this.renderItem(partitionConfigInfo, 'Partition Config')}
175
- </div>
176
- ) : null}
177
- <div className={b('col')}>
178
- {tableStatsInfo.map((info, index) => (
179
- <React.Fragment key={index}>
180
- {this.renderItem(info, index === 0 ? 'Table Stats' : undefined)}
181
- </React.Fragment>
182
- ))}
183
- </div>
184
- </div>
185
- </div>
186
- );
187
- }
188
-
189
- render() {
190
- const {data} = this.props;
191
- const entityName = getEntityName(data?.PathDescription);
192
-
193
- if (data) {
194
- return <div className={b()}>{this.renderContent(data)}</div>;
195
- } else {
196
- return <div className="error">No {entityName} data</div>;
197
- }
198
- }
199
- }
200
-
201
- export default SchemaInfoViewer;