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
@@ -7,7 +7,7 @@ import {TPDiskState} from '../../../../types/api/pdisk';
7
7
  import {PDisk} from '../PDisk';
8
8
 
9
9
  describe('PDisk state', () => {
10
- it('Should determine severity based on State', () => {
10
+ it('Should determine severity based on State if space severity is OK', () => {
11
11
  const {getAllByRole} = renderWithStore(
12
12
  <MemoryRouter>
13
13
  <PDisk nodeId={1} data={{State: TPDiskState.Normal}} />
@@ -20,6 +20,55 @@ describe('PDisk state', () => {
20
20
  expect(normalDisk.className).not.toBe(erroredDisk.className);
21
21
  });
22
22
 
23
+ it('Should determine severity based on space utilization if state severity is OK', () => {
24
+ const {getAllByRole} = renderWithStore(
25
+ <MemoryRouter>
26
+ <PDisk
27
+ nodeId={1}
28
+ data={{State: TPDiskState.Normal, AvailableSize: '100', TotalSize: '100'}}
29
+ />
30
+ <PDisk
31
+ nodeId={2}
32
+ data={{State: TPDiskState.Normal, AvailableSize: '14', TotalSize: '100'}}
33
+ />
34
+ <PDisk
35
+ nodeId={3}
36
+ data={{State: TPDiskState.Normal, AvailableSize: '4', TotalSize: '100'}}
37
+ />
38
+ </MemoryRouter>,
39
+ );
40
+
41
+ const [disk1, disk2, disk3] = getAllByRole('meter');
42
+
43
+ expect(disk1.className).toMatch(/_green\b/i);
44
+ expect(disk2.className).toMatch(/_yellow\b/i);
45
+ expect(disk3.className).toMatch(/_red\b/i);
46
+ });
47
+
48
+ it('Should determine severity based on max severity of state and space utilization ', () => {
49
+ const {getAllByRole} = renderWithStore(
50
+ <MemoryRouter>
51
+ <PDisk
52
+ nodeId={1}
53
+ data={{
54
+ State: TPDiskState.ChunkQuotaError,
55
+ AvailableSize: '100',
56
+ TotalSize: '100',
57
+ }}
58
+ />
59
+ <PDisk
60
+ nodeId={2}
61
+ data={{State: TPDiskState.Normal, AvailableSize: '4', TotalSize: '100'}}
62
+ />
63
+ </MemoryRouter>,
64
+ );
65
+
66
+ const [disk1, disk2] = getAllByRole('meter');
67
+
68
+ expect(disk1.className).toMatch(/_red\b/i);
69
+ expect(disk2.className).toMatch(/_red\b/i);
70
+ });
71
+
23
72
  it('Should display as unavailabe when no State is provided', () => {
24
73
  const {getAllByRole} = renderWithStore(
25
74
  <MemoryRouter>
@@ -34,4 +83,18 @@ describe('PDisk state', () => {
34
83
  expect(disk1.className).toMatch(/_grey\b/i);
35
84
  expect(disk2.className).not.toMatch(/_grey\b/i);
36
85
  });
86
+
87
+ it('Should display as unavailabe when no State is provided event if space severity is not OK', () => {
88
+ const {getAllByRole} = renderWithStore(
89
+ <MemoryRouter>
90
+ <PDisk nodeId={1} data={{AvailableSize: '14', TotalSize: '100'}} />
91
+ <PDisk nodeId={2} data={{AvailableSize: '4', TotalSize: '100'}} />
92
+ </MemoryRouter>,
93
+ );
94
+
95
+ const [disk1, disk2] = getAllByRole('meter');
96
+
97
+ expect(disk1.className).toMatch(/_grey\b/i);
98
+ expect(disk2.className).toMatch(/_grey\b/i);
99
+ });
37
100
  });
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import {connect} from 'react-redux';
4
4
  import cn from 'bem-cn-lite';
5
- import DataTable from '@yandex-cloud/react-data-table';
5
+ import DataTable from '@gravity-ui/react-data-table';
6
6
  import {RadioButton} from '@gravity-ui/uikit';
7
7
 
8
8
  import {Search} from '../../components/Search';
@@ -1,6 +1,6 @@
1
1
  import _ from 'lodash';
2
2
  import cn from 'bem-cn-lite';
3
- import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-table';
3
+ import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
4
4
  import {Icon, Label, Popover, PopoverBehavior} from '@gravity-ui/uikit';
5
5
 
6
6
  import shieldIcon from '../../../assets/icons/shield.svg';
@@ -20,7 +20,7 @@ import {getUsage, isFullDonorData} from '../../../utils/storage';
20
20
 
21
21
  import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
22
22
  import {VDisk} from '../VDisk';
23
- import {getDegradedSeverity, getUsageSeverity} from '../utils';
23
+ import {getDegradedSeverity, getUsageSeverityForStorageGroup} from '../utils';
24
24
 
25
25
  import i18n from './i18n';
26
26
  import './StorageGroups.scss';
@@ -127,6 +127,7 @@ function StorageGroups({
127
127
  {
128
128
  name: TableColumnsIds.Type,
129
129
  header: tableColumnsNames[TableColumnsIds.Type],
130
+ // prettier-ignore
130
131
  render: ({value, row}) => (
131
132
  <>
132
133
  <Label>{(value as string) || '—'}</Label>
@@ -164,7 +165,7 @@ function StorageGroups({
164
165
  // but the absence of a value is more clear
165
166
  return row.Limit ? (
166
167
  <Label
167
- theme={getUsageSeverity(usage)}
168
+ theme={getUsageSeverityForStorageGroup(usage)}
168
169
  className={b('usage-label', {overload: usage >= 90})}
169
170
  >
170
171
  {usage}%
@@ -1,7 +1,7 @@
1
1
  import _ from 'lodash';
2
2
  import cn from 'bem-cn-lite';
3
3
 
4
- import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-table';
4
+ import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
5
5
  import {Popover, PopoverBehavior} from '@gravity-ui/uikit';
6
6
 
7
7
  import {VisibleEntities} from '../../../store/reducers/storage';
@@ -123,7 +123,7 @@ export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
123
123
  routes.node,
124
124
  {id: data.NodeId, activeTab: STRUCTURE},
125
125
  {
126
- pdiskId: data.PDisk?.PDiskId,
126
+ pdiskId: data.PDiskId ?? data.PDisk?.PDiskId,
127
127
  vdiskId: stringifyVdiskId(data.VDiskId),
128
128
  },
129
129
  )}
@@ -1,12 +1,14 @@
1
1
  import type {IStoragePoolGroup} from '../../../types/store/storage';
2
+ import {EFlag} from '../../../types/api/enums';
2
3
 
3
4
  export * from './constants';
4
5
 
5
- const generateEvaluator = <
6
- OkLevel extends string,
7
- WarnLevel extends string,
8
- CritLevel extends string
9
- >(warn: number, crit: number, levels: [OkLevel, WarnLevel, CritLevel]) =>
6
+ const generateEvaluator =
7
+ <OkLevel extends string, WarnLevel extends string, CritLevel extends string>(
8
+ warn: number,
9
+ crit: number,
10
+ levels: [OkLevel, WarnLevel, CritLevel],
11
+ ) =>
10
12
  (value: number) => {
11
13
  if (0 <= value && value < warn) {
12
14
  return levels[0];
@@ -34,12 +36,26 @@ const canEvaluateErasureSpecies = (value?: string): value is keyof typeof degrad
34
36
  value !== undefined && value in degradationEvaluators;
35
37
 
36
38
  export const getDegradedSeverity = (group: IStoragePoolGroup) => {
37
- const evaluate = canEvaluateErasureSpecies(group.ErasureSpecies) ?
38
- degradationEvaluators[group.ErasureSpecies] :
39
- defaultDegradationEvaluator;
39
+ const evaluate = canEvaluateErasureSpecies(group.ErasureSpecies)
40
+ ? degradationEvaluators[group.ErasureSpecies]
41
+ : defaultDegradationEvaluator;
40
42
 
41
43
  return evaluate(group.Missing);
42
44
  };
43
45
 
44
- export const getUsageSeverity = generateEvaluator(80, 85, ['success', 'warning', 'danger']);
45
- export const getUsageSeverityForEntityStatus = generateEvaluator(80, 85, ['Green', 'Yellow', 'Red']);
46
+ export const getUsageSeverityForStorageGroup = generateEvaluator(80, 85, [
47
+ 'success',
48
+ 'warning',
49
+ 'danger',
50
+ ]);
51
+ export const getUsageSeverityForEntityStatus = generateEvaluator(80, 85, [
52
+ 'Green',
53
+ 'Yellow',
54
+ 'Red',
55
+ ]);
56
+
57
+ export const getUsageSeverityForPDisk = generateEvaluator(85, 95, [
58
+ EFlag.Green,
59
+ EFlag.Yellow,
60
+ EFlag.Red,
61
+ ]);
@@ -16,7 +16,7 @@ import {Tag} from '../../components/Tag/Tag';
16
16
  import Icon from '../../components/Icon/Icon';
17
17
  import EmptyState from '../../components/EmptyState/EmptyState';
18
18
  import {Link as ExternalLink, Button, Loader} from '@gravity-ui/uikit';
19
- import DataTable from '@yandex-cloud/react-data-table';
19
+ import DataTable from '@gravity-ui/react-data-table';
20
20
  import CriticalActionDialog from '../../components/CriticalActionDialog/CriticalActionDialog';
21
21
  import routes, {createHref} from '../../routes';
22
22
  import {getDefaultNodePath} from '../Node/NodePages';
@@ -4,7 +4,7 @@ import cn from 'bem-cn-lite';
4
4
  import _ from 'lodash';
5
5
  import {connect} from 'react-redux';
6
6
  import {Loader} from '@gravity-ui/uikit';
7
- import DataTable from '@yandex-cloud/react-data-table';
7
+ import DataTable from '@gravity-ui/react-data-table';
8
8
  import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
9
9
 
10
10
  import './Acl.scss';
@@ -1,9 +1,9 @@
1
1
  import {useCallback, useEffect, useState} from 'react';
2
2
  import {useDispatch} from 'react-redux';
3
3
  import block from 'bem-cn-lite';
4
- import { escapeRegExp } from 'lodash/fp';
4
+ import {escapeRegExp} from 'lodash/fp';
5
5
 
6
- import DataTable, {Column} from '@yandex-cloud/react-data-table';
6
+ import DataTable, {Column} from '@gravity-ui/react-data-table';
7
7
 
8
8
  import type {EPathType} from '../../../../types/api/schema';
9
9
  import {Loader} from '../../../../components/Loader';
@@ -2,7 +2,7 @@ import {useEffect, useMemo} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import {connect} from 'react-redux';
4
4
  import {Loader} from '@gravity-ui/uikit';
5
- import DataTable from '@yandex-cloud/react-data-table';
5
+ import DataTable from '@gravity-ui/react-data-table';
6
6
 
7
7
  import Icon from '../../../../components/Icon/Icon';
8
8
 
@@ -2,7 +2,7 @@ import {useState, useContext, useEffect, useMemo} from 'react';
2
2
  import {useDispatch} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
- import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-table';
5
+ import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
6
6
  import {Loader} from '@gravity-ui/uikit';
7
7
 
8
8
  import {DateRange, DateRangeValues} from '../../../../components/DateRange';
@@ -53,6 +53,7 @@ const tableColumnsNames = {
53
53
  NodeId: 'NodeId',
54
54
  PeakTime: 'PeakTime',
55
55
  InFlightTxCount: 'InFlightTxCount',
56
+ IntervalEnd: 'IntervalEnd',
56
57
  };
57
58
 
58
59
  function prepareCPUWorkloadValue(value: string) {
@@ -227,6 +228,10 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
227
228
  align: DataTable.RIGHT,
228
229
  sortable: false,
229
230
  },
231
+ {
232
+ name: tableColumnsNames.IntervalEnd,
233
+ render: ({value}) => formatDateTime(new Date(value as string).getTime()),
234
+ }
230
235
  ];
231
236
  }, [dispatch, history, tenantPath]);
232
237
 
@@ -1,15 +1,15 @@
1
1
  import {ReactNode, useCallback, useMemo} from 'react';
2
2
  import {shallowEqual, useDispatch, useSelector} from 'react-redux';
3
- import cn from 'bem-cn-lite';
4
3
 
5
- import {Loader} from '@gravity-ui/uikit';
4
+ import {Loader} from '../../../../components/Loader';
6
5
 
7
6
  //@ts-ignore
8
- import SchemaInfoViewer from '../../Schema/SchemaInfoViewer/SchemaInfoViewer';
9
7
  import {TableIndexInfo} from '../../../../components/InfoViewer/schemaInfo';
8
+ import {ResponseError} from '../../../../components/Errors/ResponseError';
10
9
 
11
10
  import {TopicInfo} from './TopicInfo';
12
11
  import {ChangefeedInfo} from './ChangefeedInfo';
12
+ import {TableInfo} from './TableInfo';
13
13
 
14
14
  import {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema';
15
15
  import {
@@ -33,8 +33,6 @@ import {
33
33
  } from '../../../../store/reducers/olapStats';
34
34
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
35
35
 
36
- import './Overview.scss';
37
-
38
36
  function prepareOlapTableGeneral(item?: TEvDescribeSchemeResult, olapStats?: any[]) {
39
37
  const tableData = item?.PathDescription?.ColumnTableDescription;
40
38
 
@@ -70,8 +68,6 @@ interface OverviewProps {
70
68
  tenantName?: string;
71
69
  }
72
70
 
73
- const b = cn('kv-tenant-overview');
74
-
75
71
  function Overview({type, tenantName, className}: OverviewProps) {
76
72
  const dispatch = useDispatch();
77
73
 
@@ -81,6 +77,7 @@ function Overview({type, tenantName, className}: OverviewProps) {
81
77
  wasLoaded,
82
78
  autorefresh,
83
79
  currentSchemaPath,
80
+ error,
84
81
  } = useSelector((state: any) => state.schema);
85
82
 
86
83
  const {data: {result: olapStats} = {result: undefined}, loading: olapStatsLoading} =
@@ -144,14 +141,6 @@ function Overview({type, tenantName, className}: OverviewProps) {
144
141
  : currentItem;
145
142
  }, [type, olapStats, currentItem]);
146
143
 
147
- const renderLoader = () => {
148
- return (
149
- <div className={b('loader')}>
150
- <Loader size="m" />
151
- </div>
152
- );
153
- };
154
-
155
144
  const renderContent = () => {
156
145
  // verbose mapping to guarantee a correct render for new path types
157
146
  // TS will error when a new type is added but not mapped here
@@ -170,15 +159,15 @@ function Overview({type, tenantName, className}: OverviewProps) {
170
159
  [EPathType.EPathTypePersQueueGroup]: () => <TopicInfo data={schemaData} />,
171
160
  };
172
161
 
173
- return (
174
- (type && pathTypeToComponent[type]?.()) || (
175
- <SchemaInfoViewer fullPath={currentItem.Path} data={schemaData} />
176
- )
177
- );
162
+ return (type && pathTypeToComponent[type]?.()) || <TableInfo data={schemaData} />;
178
163
  };
179
164
 
180
165
  if ((loading && !wasLoaded) || (isEntityWithMergedImpl && !mergedChildrenPaths)) {
181
- return renderLoader();
166
+ return <Loader size="m" />;
167
+ }
168
+
169
+ if (error) {
170
+ return <ResponseError error={error} />;
182
171
  }
183
172
 
184
173
  return <div className={className}>{renderContent()}</div>;
@@ -1,6 +1,12 @@
1
- .schema-info-viewer {
1
+ @import '../../../../../styles/mixins.scss';
2
+
3
+ .ydb-diagnostics-table-info {
2
4
  overflow: auto;
3
5
 
6
+ &__title {
7
+ @include info-viewer-title();
8
+ }
9
+
4
10
  &__row {
5
11
  display: flex;
6
12
  flex-wrap: wrap;
@@ -19,19 +25,11 @@
19
25
  }
20
26
  }
21
27
 
22
- &__item {
28
+ &__info-block {
23
29
  margin-bottom: 20px;
24
30
 
25
31
  .info-viewer__items {
26
32
  grid-template-columns: minmax(max-content, 280px);
27
33
  }
28
34
  }
29
-
30
- &__title {
31
- margin: 15px 0 10px;
32
-
33
- font-size: var(--yc-text-body-2-font-size);
34
- font-weight: 600;
35
- line-height: var(--yc-text-body-2-line-height);
36
- }
37
35
  }
@@ -0,0 +1,71 @@
1
+ import {useMemo} from 'react';
2
+ import cn from 'bem-cn-lite';
3
+
4
+ import type {TEvDescribeSchemeResult} from '../../../../../types/api/schema';
5
+
6
+ import {InfoViewer} from '../../../../../components/InfoViewer';
7
+
8
+ import {getEntityName} from '../../../utils';
9
+
10
+ import {prepareTableInfo} from './prepareTableInfo';
11
+
12
+ import i18n from './i18n';
13
+
14
+ import './TableInfo.scss';
15
+
16
+ const b = cn('ydb-diagnostics-table-info');
17
+
18
+ interface TableInfoProps {
19
+ data?: TEvDescribeSchemeResult;
20
+ }
21
+
22
+ export const TableInfo = ({data}: TableInfoProps) => {
23
+ const entityName = getEntityName(data?.PathDescription);
24
+
25
+ const {
26
+ generalTableInfo = [],
27
+ tableStatsInfo = [],
28
+ tabletMetricsInfo = [],
29
+ partitionConfigInfo = [],
30
+ } = useMemo(() => prepareTableInfo(data), [data]);
31
+
32
+ return (
33
+ <div className={b()}>
34
+ <InfoViewer
35
+ info={generalTableInfo}
36
+ title={entityName}
37
+ className={b('info-block')}
38
+ renderEmptyState={() => <div className={b('title')}>{entityName}</div>}
39
+ />
40
+ <div className={b('row')}>
41
+ {tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (
42
+ <div className={b('col')}>
43
+ <InfoViewer
44
+ info={tabletMetricsInfo}
45
+ title={i18n('tabletMetrics')}
46
+ className={b('info-block')}
47
+ renderEmptyState={() => null}
48
+ />
49
+ <InfoViewer
50
+ info={partitionConfigInfo}
51
+ title={i18n('partitionConfig')}
52
+ className={b('info-block')}
53
+ renderEmptyState={() => null}
54
+ />
55
+ </div>
56
+ ) : null}
57
+ <div className={b('col')}>
58
+ {tableStatsInfo.map((info, index) => (
59
+ <InfoViewer
60
+ key={index}
61
+ info={info}
62
+ title={index === 0 ? i18n('tableStats') : undefined}
63
+ className={b('info-block')}
64
+ renderEmptyState={() => null}
65
+ />
66
+ ))}
67
+ </div>
68
+ </div>
69
+ </div>
70
+ );
71
+ };
@@ -0,0 +1,5 @@
1
+ {
2
+ "tableStats": "Table Stats",
3
+ "tabletMetrics": "Tablet Metrics",
4
+ "partitionConfig": "Partition Config"
5
+ }
@@ -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-overview-table-info';
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,5 @@
1
+ {
2
+ "tableStats": "Статистика таблицы",
3
+ "tabletMetrics": "Метрики таблетки",
4
+ "partitionConfig": "Конфигурация партиции"
5
+ }
@@ -0,0 +1 @@
1
+ export * from './TableInfo';
@@ -0,0 +1,96 @@
1
+ import type {TEvDescribeSchemeResult} from '../../../../../types/api/schema';
2
+
3
+ import {formatObject} from '../../../../../components/InfoViewer';
4
+ import {
5
+ formatFollowerGroupItem,
6
+ formatPartitionConfigItem,
7
+ formatTableStatsItem,
8
+ formatTabletMetricsItem,
9
+ } from '../../../../../components/InfoViewer/formatters';
10
+
11
+ export const prepareTableInfo = (data?: TEvDescribeSchemeResult) => {
12
+ if (!data) {
13
+ return {};
14
+ }
15
+
16
+ const {PathDescription = {}} = data;
17
+
18
+ const {
19
+ TableStats = {},
20
+ TabletMetrics = {},
21
+ Table: {PartitionConfig = {}} = {},
22
+ } = PathDescription;
23
+
24
+ const {
25
+ PartCount,
26
+ RowCount,
27
+ DataSize,
28
+ IndexSize,
29
+
30
+ LastAccessTime,
31
+ LastUpdateTime,
32
+
33
+ ImmediateTxCompleted,
34
+ PlannedTxCompleted,
35
+ TxRejectedByOverload,
36
+ TxRejectedBySpace,
37
+ TxCompleteLagMsec,
38
+ InFlightTxCount,
39
+
40
+ RowUpdates,
41
+ RowDeletes,
42
+ RowReads,
43
+ RangeReads,
44
+ RangeReadRows,
45
+
46
+ ...restTableStats
47
+ } = TableStats;
48
+
49
+ const {FollowerGroups, FollowerCount, CrossDataCenterFollowerCount} = PartitionConfig;
50
+
51
+ const generalTableInfo = formatObject(formatTableStatsItem, {
52
+ PartCount,
53
+ RowCount,
54
+ DataSize,
55
+ IndexSize,
56
+ ...restTableStats,
57
+ });
58
+
59
+ const tableStatsInfo = [
60
+ formatObject(formatTableStatsItem, {
61
+ LastAccessTime,
62
+ LastUpdateTime,
63
+ }),
64
+ formatObject(formatTableStatsItem, {
65
+ ImmediateTxCompleted,
66
+ PlannedTxCompleted,
67
+ TxRejectedByOverload,
68
+ TxRejectedBySpace,
69
+ TxCompleteLagMsec,
70
+ InFlightTxCount,
71
+ }),
72
+ formatObject(formatTableStatsItem, {
73
+ RowUpdates,
74
+ RowDeletes,
75
+ RowReads,
76
+ RangeReads,
77
+ RangeReadRows,
78
+ }),
79
+ ];
80
+
81
+ const tabletMetricsInfo = formatObject(formatTabletMetricsItem, TabletMetrics);
82
+
83
+ let partitionConfigInfo = [];
84
+
85
+ if (Array.isArray(FollowerGroups) && FollowerGroups.length > 0) {
86
+ partitionConfigInfo = formatObject(formatFollowerGroupItem, FollowerGroups[0]);
87
+ } else if (FollowerCount !== undefined) {
88
+ partitionConfigInfo.push(formatPartitionConfigItem('FollowerCount', FollowerCount));
89
+ } else if (CrossDataCenterFollowerCount !== undefined) {
90
+ partitionConfigInfo.push(
91
+ formatPartitionConfigItem('CrossDataCenterFollowerCount', CrossDataCenterFollowerCount),
92
+ );
93
+ }
94
+
95
+ return {generalTableInfo, tableStatsInfo, tabletMetricsInfo, partitionConfigInfo};
96
+ };
@@ -2,7 +2,7 @@ import {useCallback, useEffect, useRef, useState} from 'react';
2
2
  import {useDispatch} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
- import DataTable, {Column, Settings} from '@yandex-cloud/react-data-table';
5
+ import DataTable, {Column, Settings} from '@gravity-ui/react-data-table';
6
6
  import {Loader} from '@gravity-ui/uikit';
7
7
 
8
8
  import {DateRange, DateRangeValues} from '../../../../components/DateRange';
@@ -21,6 +21,7 @@ import type {EPathType} from '../../../../types/api/schema';
21
21
  import type {ITopQueriesFilters} from '../../../../types/store/executeTopQueries';
22
22
  import type {IQueryResult} from '../../../../types/store/query';
23
23
 
24
+ import {formatDateTime} from '../../../../utils';
24
25
  import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
25
26
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
26
27
  import {prepareQueryError} from '../../../../utils/query';
@@ -51,6 +52,11 @@ const COLUMNS: Column<KeyValueRow>[] = [
51
52
  sortable: false,
52
53
  render: ({value}) => <TruncatedQuery value={value} maxQueryHeight={MAX_QUERY_HEIGHT} />,
53
54
  },
55
+ {
56
+ name: 'IntervalEnd',
57
+ width: 140,
58
+ render: ({value}) => formatDateTime(new Date(value as string).getTime()),
59
+ },
54
60
  ];
55
61
 
56
62
  interface TopQueriesProps {
@@ -26,6 +26,8 @@ import {
26
26
  TColumnTableDescription,
27
27
  TDirEntry,
28
28
  } from '../../../types/api/schema';
29
+
30
+ import {formatDateTime} from '../../../utils';
29
31
  import {isColumnEntityType, isIndexTable, isTableType} from '../utils/schema';
30
32
 
31
33
  import {
@@ -201,7 +203,7 @@ function ObjectSummary(props: ObjectSummaryProps) {
201
203
  const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep);
202
204
  let createTime = '';
203
205
  if (startTimeInMilliseconds) {
204
- createTime = new Date(startTimeInMilliseconds).toUTCString();
206
+ createTime = formatDateTime(startTimeInMilliseconds);
205
207
  }
206
208
 
207
209
  component = <InfoViewer info={[{label: 'Create time', value: createTime}]} />;