ydb-embedded-ui 3.0.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +2 -0
  3. package/dist/components/DateRange/DateRange.scss +11 -0
  4. package/dist/components/DateRange/DateRange.tsx +75 -0
  5. package/dist/components/DateRange/index.ts +1 -0
  6. package/dist/components/Illustration/Illustration.tsx +4 -11
  7. package/dist/components/InfoViewer/InfoViewer.scss +2 -0
  8. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +1 -1
  9. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +16 -0
  10. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +4 -5
  11. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +7 -7
  12. package/dist/containers/Tenant/Diagnostics/OverloadedShards/OverloadedShards.scss +27 -0
  13. package/dist/containers/Tenant/Diagnostics/{TopShards/TopShards.tsx → OverloadedShards/OverloadedShards.tsx} +75 -20
  14. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/en.json +4 -0
  15. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/index.ts +11 -0
  16. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/ru.json +4 -0
  17. package/dist/containers/Tenant/Diagnostics/OverloadedShards/index.ts +1 -0
  18. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.scss +16 -19
  19. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +202 -0
  20. package/dist/containers/Tenant/Diagnostics/TopQueries/i18n/en.json +4 -0
  21. package/dist/containers/Tenant/Diagnostics/TopQueries/i18n/index.ts +11 -0
  22. package/dist/containers/Tenant/Diagnostics/TopQueries/i18n/ru.json +4 -0
  23. package/dist/containers/Tenant/Diagnostics/TopQueries/index.ts +1 -0
  24. package/dist/containers/UserSettings/UserSettings.tsx +1 -1
  25. package/dist/services/api.d.ts +7 -0
  26. package/dist/store/reducers/describe.ts +4 -1
  27. package/dist/store/reducers/executeTopQueries.ts +170 -0
  28. package/dist/store/reducers/settings.js +1 -1
  29. package/dist/store/reducers/shardsWorkload.ts +91 -25
  30. package/dist/store/reducers/storage.js +2 -0
  31. package/dist/store/reducers/{tablets.js → tablets.ts} +30 -17
  32. package/dist/store/state-url-mapping.js +16 -0
  33. package/dist/types/api/compute.ts +52 -0
  34. package/dist/types/api/consumer.ts +257 -0
  35. package/dist/types/api/enums.ts +2 -2
  36. package/dist/types/api/nodes.ts +5 -2
  37. package/dist/types/api/pdisk.ts +3 -0
  38. package/dist/types/api/schema.ts +1 -0
  39. package/dist/types/api/storage.ts +31 -28
  40. package/dist/types/api/tablet.ts +18 -2
  41. package/dist/types/api/tenant.ts +4 -1
  42. package/dist/types/api/topic.ts +157 -0
  43. package/dist/types/api/vdisk.ts +3 -0
  44. package/dist/types/store/executeTopQueries.ts +29 -0
  45. package/dist/types/store/schema.ts +3 -3
  46. package/dist/types/store/shardsWorkload.ts +11 -2
  47. package/dist/types/store/tablets.ts +42 -0
  48. package/dist/utils/getNodesColumns.js +8 -1
  49. package/dist/utils/query.ts +1 -1
  50. package/package.json +3 -3
  51. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.js +0 -188
  52. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.scss +0 -7
  53. package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +0 -1
  54. package/dist/store/reducers/executeTopQueries.js +0 -66
  55. package/dist/types/api/consumers.ts +0 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.2.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.1.0...v3.2.0) (2023-01-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * **Nodes:** display rack in table ([3b8cdd5](https://github.com/ydb-platform/ydb-embedded-ui/commit/3b8cdd5b472f98132b2faaa9b71b8911750545a6))
9
+ * **StorageNodes:** display datacenter in table ([4507bfd](https://github.com/ydb-platform/ydb-embedded-ui/commit/4507bfde839b0aafa3722828b7528885c6ac8f84))
10
+ * **TopQueries:** date range filter ([b9a8e95](https://github.com/ydb-platform/ydb-embedded-ui/commit/b9a8e9504fa68556a724b214ee91b73ec900d37e))
11
+ * **TopQueries:** filter by query text ([2c8ea97](https://github.com/ydb-platform/ydb-embedded-ui/commit/2c8ea97dd215ea59165cf05315bc5809cf7fafd7))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * **InfoViewer:** min width for values ([64a4fd4](https://github.com/ydb-platform/ydb-embedded-ui/commit/64a4fd4de16738a9e2fac9cb4fba94eafc938762))
17
+ * **Nodes:** open external link in new tab ([b7c3ddd](https://github.com/ydb-platform/ydb-embedded-ui/commit/b7c3ddd1e611f2b61466e3eda51f3341f8407588))
18
+ * **TopQueries:** proper table dynamic render type ([9add6ca](https://github.com/ydb-platform/ydb-embedded-ui/commit/9add6ca9fbfe0475caf1586070a800210320cee6))
19
+ * **TopShards:** rename to overloaded shards ([d9978bd](https://github.com/ydb-platform/ydb-embedded-ui/commit/d9978bdd84b9a883e4eefcac7f85f856da55d770))
20
+ * **UserSettings:** treat invertedDisks settings as string ([ad7742a](https://github.com/ydb-platform/ydb-embedded-ui/commit/ad7742a6bf0be59c2b9cbbf947aaa66f79d748be))
21
+
22
+ ## [3.1.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.0.1...v3.1.0) (2022-12-13)
23
+
24
+
25
+ ### Features
26
+
27
+ * **TopShards:** date range filter ([aab4396](https://github.com/ydb-platform/ydb-embedded-ui/commit/aab439600ec28d30799c4a7ef7a9c68fcacc148c))
28
+
3
29
  ## [3.0.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.0.0...v3.0.1) (2022-12-12)
4
30
 
5
31
 
package/README.md CHANGED
@@ -15,6 +15,8 @@ Local viewer for YDB clusters
15
15
  3) Open [http://localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.\
16
16
  You will also see any lint errors in the console.
17
17
 
18
+ For API reference, open Swagger UI on http://localhost:8765/viewer/api/.
19
+
18
20
  ### Making a production bundle.
19
21
 
20
22
  Base command `npm run build` builds the app for production to the `build` folder.\
@@ -0,0 +1,11 @@
1
+ .date-range {
2
+ &__input {
3
+ min-width: 190px;
4
+ padding: 5px 8px;
5
+
6
+ color: var(--yc-color-text-primary);
7
+ border: 1px solid var(--yc-color-line-generic);
8
+ border-radius: var(--yc-border-radius-m);
9
+ background: transparent;
10
+ }
11
+ }
@@ -0,0 +1,75 @@
1
+ import {ChangeEventHandler} from 'react';
2
+ import cn from 'bem-cn-lite';
3
+
4
+ import './DateRange.scss';
5
+
6
+ const b = cn('date-range');
7
+
8
+ export interface DateRangeValues {
9
+ /** ms from epoch */
10
+ from?: number;
11
+ /** ms from epoch */
12
+ to?: number;
13
+ }
14
+
15
+ interface DateRangeProps extends DateRangeValues {
16
+ className?: string;
17
+ onChange?: (value: DateRangeValues) => void;
18
+ }
19
+
20
+ const toTimezonelessISOString = (timestamp?: number) => {
21
+ if (!timestamp || isNaN(timestamp)) {
22
+ return undefined;
23
+ }
24
+
25
+ // shift by local offset to treat toISOString output as local time
26
+ const shiftedTimestamp = timestamp - new Date().getTimezoneOffset() * 60 * 1000;
27
+ return new Date(shiftedTimestamp).toISOString().substring(0, 'yyyy-MM-DDThh:mm'.length);
28
+ };
29
+
30
+ export const DateRange = ({from, to, className, onChange}: DateRangeProps) => {
31
+ const handleFromChange: ChangeEventHandler<HTMLInputElement> = ({target: {value}}) => {
32
+ let newFrom = value ? new Date(value).getTime() : undefined;
33
+
34
+ // some browsers allow selecting time after the boundary specified in `max`
35
+ if (newFrom && to && newFrom > to) {
36
+ newFrom = to;
37
+ }
38
+
39
+ onChange?.({from: newFrom, to});
40
+ };
41
+
42
+ const handleToChange: ChangeEventHandler<HTMLInputElement> = ({target: {value}}) => {
43
+ let newTo = value ? new Date(value).getTime() : undefined;
44
+
45
+ // some browsers allow selecting time before the boundary specified in `min`
46
+ if (from && newTo && from > newTo) {
47
+ newTo = from;
48
+ }
49
+
50
+ onChange?.({from, to: newTo});
51
+ };
52
+
53
+ const startISO = toTimezonelessISOString(from);
54
+ const endISO = toTimezonelessISOString(to);
55
+
56
+ return (
57
+ <div className={b(null, className)}>
58
+ <input
59
+ type="datetime-local"
60
+ value={startISO || ''}
61
+ max={endISO}
62
+ onChange={handleFromChange}
63
+ className={b('input')}
64
+ />
65
+
66
+ <input
67
+ type="datetime-local"
68
+ min={startISO}
69
+ value={endISO || ''}
70
+ onChange={handleToChange}
71
+ className={b('input')}
72
+ />
73
+ </div>
74
+ );
75
+ };
@@ -0,0 +1 @@
1
+ export * from './DateRange';
@@ -1,8 +1,8 @@
1
- import {useEffect, useState} from 'react';
1
+ import {ImgHTMLAttributes, useEffect, useState} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import {useThemeValue} from '@gravity-ui/uikit';
4
4
 
5
- export interface IllustrationProps {
5
+ export interface IllustrationProps extends ImgHTMLAttributes<HTMLImageElement> {
6
6
  name: string;
7
7
  className?: string;
8
8
  }
@@ -38,12 +38,5 @@ export const Illustration = ({name, className, ...props}: IllustrationProps) =>
38
38
  }
39
39
  }, [srcGetter]);
40
40
 
41
- return (
42
- <img
43
- alt={name}
44
- src={src}
45
- className={b(null, className)}
46
- {...props}
47
- />
48
- );
49
- }
41
+ return <img alt={name} src={src} className={b(null, className)} {...props} />;
42
+ };
@@ -54,6 +54,8 @@
54
54
  &__value {
55
55
  display: flex;
56
56
 
57
+ min-width: 120px;
58
+
57
59
  word-break: break-all;
58
60
  }
59
61
 
@@ -27,7 +27,7 @@ function DiskStateProgressBar({
27
27
  diskAllocatedPercent = -1,
28
28
  severity,
29
29
  }: DiskStateProgressBarProps) {
30
- const inverted = useSelector((state) => getSettingValue(state, INVERTED_DISKS_KEY));
30
+ const inverted = useSelector((state) => JSON.parse(getSettingValue(state, INVERTED_DISKS_KEY)));
31
31
 
32
32
  const renderAllocatedPercent = () => {
33
33
  return (
@@ -16,6 +16,8 @@ import './StorageNodes.scss';
16
16
  enum TableColumnsIds {
17
17
  NodeId = 'NodeId',
18
18
  FQDN = 'FQDN',
19
+ DataCenter = 'DataCenter',
20
+ Rack = 'Rack',
19
21
  uptime = 'uptime',
20
22
  PDisks = 'PDisks',
21
23
  Missing = 'Missing',
@@ -36,6 +38,8 @@ interface StorageNodesProps {
36
38
  const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
37
39
  NodeId: 'Node ID',
38
40
  FQDN: 'FQDN',
41
+ DataCenter: 'DC',
42
+ Rack: 'Rack',
39
43
  uptime: 'Uptime',
40
44
  PDisks: 'PDisks',
41
45
  Missing: 'Missing',
@@ -96,6 +100,18 @@ function StorageNodes({
96
100
  },
97
101
  align: DataTable.LEFT,
98
102
  },
103
+ {
104
+ name: TableColumnsIds.DataCenter,
105
+ header: tableColumnsNames[TableColumnsIds.DataCenter],
106
+ render: ({row}) => row.DataCenter || '—',
107
+ align: DataTable.LEFT,
108
+ },
109
+ {
110
+ name: TableColumnsIds.Rack,
111
+ header: tableColumnsNames[TableColumnsIds.Rack],
112
+ render: ({row}) => row.Rack || '—',
113
+ align: DataTable.LEFT,
114
+ },
99
115
  {
100
116
  name: TableColumnsIds.uptime,
101
117
  header: tableColumnsNames[TableColumnsIds.uptime],
@@ -9,11 +9,10 @@ import {Switch, Tabs} from '@gravity-ui/uikit';
9
9
 
10
10
  import {Loader} from '../../../components/Loader';
11
11
 
12
- //@ts-ignore
13
- import TopQueries from './TopQueries/TopQueries';
12
+ import {TopQueries} from './TopQueries';
14
13
  //@ts-ignore
15
14
  import DetailedOverview from './DetailedOverview/DetailedOverview';
16
- import {TopShards} from './TopShards';
15
+ import {OverloadedShards} from './OverloadedShards';
17
16
  //@ts-ignore
18
17
  import Storage from '../../Storage/Storage';
19
18
  //@ts-ignore
@@ -130,8 +129,8 @@ function Diagnostics(props: DiagnosticsProps) {
130
129
  />
131
130
  );
132
131
  }
133
- case GeneralPagesIds.topShards: {
134
- return <TopShards tenantPath={tenantNameString} type={type} />;
132
+ case GeneralPagesIds.overloadedShards: {
133
+ return <OverloadedShards tenantPath={tenantNameString} type={type} />;
135
134
  }
136
135
  case GeneralPagesIds.nodes: {
137
136
  return (
@@ -3,7 +3,7 @@ import {EPathType} from '../../../types/api/schema';
3
3
  export enum GeneralPagesIds {
4
4
  'overview' = 'Overview',
5
5
  'topQueries' = 'topQueries',
6
- 'topShards' = 'topShards',
6
+ 'overloadedShards' = 'overloadedShards',
7
7
  'nodes' = 'Nodes',
8
8
  'tablets' = 'Tablets',
9
9
  'storage' = 'Storage',
@@ -29,9 +29,9 @@ const topQueries = {
29
29
  title: 'Top queries',
30
30
  };
31
31
 
32
- const topShards = {
33
- id: GeneralPagesIds.topShards,
34
- title: 'Top shards',
32
+ const overloadedShards = {
33
+ id: GeneralPagesIds.overloadedShards,
34
+ title: 'Overloaded shards',
35
35
  };
36
36
 
37
37
  const nodes = {
@@ -75,7 +75,7 @@ const consumers = {
75
75
  export const DATABASE_PAGES = [
76
76
  overview,
77
77
  topQueries,
78
- topShards,
78
+ overloadedShards,
79
79
  nodes,
80
80
  tablets,
81
81
  storage,
@@ -83,9 +83,9 @@ export const DATABASE_PAGES = [
83
83
  describe,
84
84
  ];
85
85
 
86
- export const TABLE_PAGES = [overview, topShards, graph, tablets, hotKeys, describe];
86
+ export const TABLE_PAGES = [overview, overloadedShards, graph, tablets, hotKeys, describe];
87
87
 
88
- export const DIR_PAGES = [overview, topShards, describe];
88
+ export const DIR_PAGES = [overview, overloadedShards, describe];
89
89
 
90
90
  export const CDC_STREAM_PAGES = [overview, consumers, describe];
91
91
  export const TOPIC_PAGES = [overview, consumers, describe];
@@ -0,0 +1,27 @@
1
+ .overloaded-shards {
2
+ display: flex;
3
+ flex-direction: column;
4
+
5
+ height: 100%;
6
+
7
+ background-color: var(--yc-color-base-background);
8
+
9
+ &__loader {
10
+ display: flex;
11
+ justify-content: center;
12
+ }
13
+
14
+ &__controls {
15
+ display: flex;
16
+ flex-wrap: wrap;
17
+ align-items: baseline;
18
+ gap: 16px;
19
+
20
+ margin-bottom: 10px;
21
+ }
22
+
23
+ &__table {
24
+ overflow: auto;
25
+ flex-grow: 1;
26
+ }
27
+ }
@@ -5,27 +5,36 @@ import cn from 'bem-cn-lite';
5
5
  import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-table';
6
6
  import {Loader} from '@gravity-ui/uikit';
7
7
 
8
+ import {DateRange, DateRangeValues} from '../../../../components/DateRange';
8
9
  import InternalLink from '../../../../components/InternalLink/InternalLink';
9
10
 
10
11
  import HistoryContext from '../../../../contexts/HistoryContext';
11
12
 
12
13
  import routes, {createHref} from '../../../../routes';
13
14
 
14
- import {sendShardQuery, setShardQueryOptions} from '../../../../store/reducers/shardsWorkload';
15
+ import {
16
+ sendShardQuery,
17
+ setShardsState,
18
+ setShardsQueryFilters,
19
+ } from '../../../../store/reducers/shardsWorkload';
15
20
  import {setCurrentSchemaPath, getSchema} from '../../../../store/reducers/schema';
21
+ import type {IShardsWorkloadFilters} from '../../../../types/store/shardsWorkload';
16
22
 
17
23
  import type {EPathType} from '../../../../types/api/schema';
18
24
 
19
- import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
25
+ import {formatDateTime, formatNumber} from '../../../../utils';
26
+ import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
20
27
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
21
- import {i18n} from '../../../../utils/i18n';
22
28
  import {prepareQueryError} from '../../../../utils/query';
23
29
 
30
+ import {getDefaultNodePath} from '../../../Node/NodePages';
31
+
24
32
  import {isColumnEntityType} from '../../utils/schema';
25
33
 
26
- import './TopShards.scss';
34
+ import i18n from './i18n';
35
+ import './OverloadedShards.scss';
27
36
 
28
- const b = cn('top-shards');
37
+ const b = cn('overloaded-shards');
29
38
  const bLink = cn('yc-link');
30
39
 
31
40
  const TABLE_SETTINGS: Settings = {
@@ -41,16 +50,15 @@ const tableColumnsNames = {
41
50
  CPUCores: 'CPUCores',
42
51
  DataSize: 'DataSize',
43
52
  Path: 'Path',
53
+ NodeId: 'NodeId',
54
+ PeakTime: 'PeakTime',
55
+ InFlightTxCount: 'InFlightTxCount',
44
56
  };
45
57
 
46
58
  function prepareCPUWorkloadValue(value: string) {
47
59
  return `${(Number(value) * 100).toFixed(2)}%`;
48
60
  }
49
61
 
50
- function prepareDateSizeValue(value: number) {
51
- return new Intl.NumberFormat(i18n.lang).format(value);
52
- }
53
-
54
62
  function stringToDataTableSortOrder(value: string): SortOrder[] | undefined {
55
63
  return value
56
64
  ? value.split(',').map((columnId) => ({
@@ -74,12 +82,12 @@ function dataTableToStringSortOrder(value: SortOrder | SortOrder[] = []) {
74
82
  return sortOrders.map(({columnId}) => columnId).join(',');
75
83
  }
76
84
 
77
- interface TopShardsProps {
85
+ interface OverloadedShardsProps {
78
86
  tenantPath: string;
79
87
  type?: EPathType;
80
88
  }
81
89
 
82
- export const TopShards = ({tenantPath, type}: TopShardsProps) => {
90
+ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
83
91
  const dispatch = useDispatch();
84
92
 
85
93
  const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
@@ -87,10 +95,24 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
87
95
  const {
88
96
  loading,
89
97
  data: {result: data = undefined} = {},
98
+ filters: storeFilters,
90
99
  error,
91
100
  wasLoaded,
92
101
  } = useTypedSelector((state) => state.shardsWorkload);
93
102
 
103
+ // default date range should be the last hour, but shouldn't propagate into URL until user interacts with the control
104
+ // redux initial value can't be used, as it synchronizes with URL
105
+ const [filters, setFilters] = useState<IShardsWorkloadFilters>(() => {
106
+ if (!storeFilters?.from && !storeFilters?.to) {
107
+ return {
108
+ from: Date.now() - HOUR_IN_SECONDS * 1000,
109
+ to: Date.now(),
110
+ };
111
+ }
112
+
113
+ return storeFilters;
114
+ });
115
+
94
116
  const [sortOrder, setSortOrder] = useState(tableColumnsNames.CPUCores);
95
117
 
96
118
  useAutofetcher(
@@ -100,32 +122,38 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
100
122
  database: tenantPath,
101
123
  path: currentSchemaPath,
102
124
  sortOrder: stringToQuerySortOrder(sortOrder),
125
+ filters,
103
126
  }),
104
127
  );
105
128
  },
106
- [dispatch, currentSchemaPath, tenantPath, sortOrder],
129
+ [dispatch, tenantPath, currentSchemaPath, sortOrder, filters],
107
130
  autorefresh,
108
131
  );
109
132
 
110
133
  // don't show loader for requests triggered by table sort, only for path change
111
134
  useEffect(() => {
112
135
  dispatch(
113
- setShardQueryOptions({
136
+ setShardsState({
114
137
  wasLoaded: false,
115
138
  data: undefined,
116
139
  }),
117
140
  );
118
- }, [dispatch, currentSchemaPath, tenantPath]);
141
+ }, [dispatch, currentSchemaPath, tenantPath, filters]);
119
142
 
120
143
  const history = useContext(HistoryContext);
121
144
 
122
145
  const onSort = (newSortOrder?: SortOrder | SortOrder[]) => {
123
- // omit information about sort order to disable ASC order, only DESC makes sense for top shards
146
+ // omit information about sort order to disable ASC order, only DESC makes sense for overloaded shards
124
147
  // use a string (and not the DataTable default format) to prevent reference change,
125
148
  // which would cause an excess state change, to avoid repeating requests
126
149
  setSortOrder(dataTableToStringSortOrder(newSortOrder));
127
150
  };
128
151
 
152
+ const handleDateRangeChange = (value: DateRangeValues) => {
153
+ dispatch(setShardsQueryFilters(value));
154
+ setFilters(value);
155
+ };
156
+
129
157
  const tableColumns: Column<any>[] = useMemo(() => {
130
158
  const onSchemaClick = (schemaPath: string) => {
131
159
  return () => {
@@ -161,7 +189,7 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
161
189
  name: tableColumnsNames.DataSize,
162
190
  header: 'DataSize (B)',
163
191
  render: ({value}) => {
164
- return prepareDateSizeValue(value as number);
192
+ return formatNumber(value as number);
165
193
  },
166
194
  align: DataTable.RIGHT,
167
195
  },
@@ -176,6 +204,29 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
176
204
  },
177
205
  sortable: false,
178
206
  },
207
+ {
208
+ name: tableColumnsNames.NodeId,
209
+ render: ({value: nodeId}) => {
210
+ return (
211
+ <InternalLink to={getDefaultNodePath(nodeId as string)}>
212
+ {nodeId as string}
213
+ </InternalLink>
214
+ );
215
+ },
216
+ align: DataTable.RIGHT,
217
+ sortable: false,
218
+ },
219
+ {
220
+ name: tableColumnsNames.PeakTime,
221
+ render: ({value}) => formatDateTime(new Date(value as string).valueOf()),
222
+ sortable: false,
223
+ },
224
+ {
225
+ name: tableColumnsNames.InFlightTxCount,
226
+ render: ({value}) => formatNumber(value as number),
227
+ align: DataTable.RIGHT,
228
+ sortable: false,
229
+ },
179
230
  ];
180
231
  }, [dispatch, history, tenantPath]);
181
232
 
@@ -192,12 +243,12 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
192
243
  return renderLoader();
193
244
  }
194
245
 
195
- if (!data || data.length === 0 || isColumnEntityType(type)) {
196
- return 'No data';
246
+ if (error && !error.isCancelled) {
247
+ return <div className="error">{prepareQueryError(error)}</div>;
197
248
  }
198
249
 
199
- if (error && !error.isCancelled) {
200
- return prepareQueryError(error);
250
+ if (!data || isColumnEntityType(type)) {
251
+ return i18n('no-data');
201
252
  }
202
253
 
203
254
  return (
@@ -216,6 +267,10 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
216
267
 
217
268
  return (
218
269
  <div className={b()}>
270
+ <div className={b('controls')}>
271
+ {i18n('description')}
272
+ <DateRange from={filters.from} to={filters.to} onChange={handleDateRangeChange} />
273
+ </div>
219
274
  {renderContent()}
220
275
  </div>
221
276
  );
@@ -0,0 +1,4 @@
1
+ {
2
+ "no-data": "No data",
3
+ "description": "Shards with CPU load over 70% are listed"
4
+ }
@@ -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-overloaded-shards';
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,4 @@
1
+ {
2
+ "no-data": "Нет данных",
3
+ "description": "Отображаются шарды с загрузкой CPU выше 70%"
4
+ }
@@ -0,0 +1 @@
1
+ export * from './OverloadedShards';
@@ -1,43 +1,40 @@
1
1
  @import '../../../../styles/mixins.scss';
2
2
 
3
3
  .kv-top-queries {
4
+ display: flex;
5
+ flex-direction: column;
6
+
4
7
  height: 100%;
5
8
 
6
9
  @include query-data-table;
7
- &__message-container {
8
- padding: 15px 0;
9
- }
10
10
 
11
11
  &__loader {
12
12
  display: flex;
13
13
  justify-content: center;
14
14
  }
15
15
 
16
- &__owner-container {
17
- margin: 10px;
16
+ &__controls {
17
+ display: flex;
18
+ flex-wrap: wrap;
19
+ gap: 16px;
18
20
 
19
- .yc-staff-card {
20
- display: inline-block;
21
- }
21
+ margin-bottom: 10px;
22
22
  }
23
23
 
24
- &__text {
25
- font-size: var(--yc-text-body-1-font-size);
26
- line-height: var(--yc-text-body-1-line-height);
27
-
28
- color: var(--yc-color-text-primary);
29
-
30
- &::first-letter {
31
- color: var(--yc-color-text-danger);
32
- }
24
+ &__search {
25
+ @include search();
33
26
  }
34
27
 
35
- &__result &__table-content {
36
- & .data-table {
28
+ &__result {
29
+ overflow: auto;
30
+ flex-grow: 1;
31
+
32
+ .data-table {
37
33
  &,
38
34
  &__table {
39
35
  width: 100%;
40
36
  }
37
+
41
38
  &__td {
42
39
  vertical-align: top;
43
40
  white-space: pre;