ydb-embedded-ui 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +2 -0
  3. package/dist/components/DateRange/DateRange.scss +11 -0
  4. package/dist/{containers/Tenant/Diagnostics/TopShards → components}/DateRange/DateRange.tsx +7 -7
  5. package/dist/{containers/Tenant/Diagnostics/TopShards → components}/DateRange/index.ts +0 -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/{TopShards/TopShards.scss → OverloadedShards/OverloadedShards.scss} +1 -1
  13. package/dist/containers/Tenant/Diagnostics/{TopShards/TopShards.tsx → OverloadedShards/OverloadedShards.tsx} +10 -11
  14. package/dist/containers/Tenant/Diagnostics/{TopShards → OverloadedShards}/i18n/en.json +0 -0
  15. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/index.ts +11 -0
  16. package/dist/containers/Tenant/Diagnostics/{TopShards → OverloadedShards}/i18n/ru.json +0 -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/{TopShards → TopQueries}/i18n/index.ts +1 -1
  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 +9 -9
  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 +10 -2
  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 +3 -3
  47. package/dist/types/store/tablets.ts +42 -0
  48. package/dist/utils/getNodesColumns.js +8 -1
  49. package/package.json +3 -3
  50. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.js +0 -188
  51. package/dist/containers/Tenant/Diagnostics/TopShards/DateRange/DateRange.scss +0 -13
  52. package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +0 -1
  53. package/dist/store/reducers/executeTopQueries.js +0 -66
  54. package/dist/types/api/consumers.ts +0 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
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
+
3
22
  ## [3.1.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.0.1...v3.1.0) (2022-12-13)
4
23
 
5
24
 
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
+ }
@@ -1,9 +1,9 @@
1
- import cn from 'bem-cn-lite';
2
1
  import {ChangeEventHandler} from 'react';
2
+ import cn from 'bem-cn-lite';
3
3
 
4
4
  import './DateRange.scss';
5
5
 
6
- const b = cn('top-shards');
6
+ const b = cn('date-range');
7
7
 
8
8
  export interface DateRangeValues {
9
9
  /** ms from epoch */
@@ -54,21 +54,21 @@ export const DateRange = ({from, to, className, onChange}: DateRangeProps) => {
54
54
  const endISO = toTimezonelessISOString(to);
55
55
 
56
56
  return (
57
- <div className={b('date-range', className)}>
57
+ <div className={b(null, className)}>
58
58
  <input
59
59
  type="datetime-local"
60
- value={startISO}
60
+ value={startISO || ''}
61
61
  max={endISO}
62
62
  onChange={handleFromChange}
63
- className={b('date-range-input')}
63
+ className={b('input')}
64
64
  />
65
65
 
66
66
  <input
67
67
  type="datetime-local"
68
68
  min={startISO}
69
- value={endISO}
69
+ value={endISO || ''}
70
70
  onChange={handleToChange}
71
- className={b('date-range-input')}
71
+ className={b('input')}
72
72
  />
73
73
  </div>
74
74
  );
@@ -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];
@@ -1,4 +1,4 @@
1
- .top-shards {
1
+ .overloaded-shards {
2
2
  display: flex;
3
3
  flex-direction: column;
4
4
 
@@ -5,6 +5,7 @@ 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';
@@ -13,8 +14,8 @@ import routes, {createHref} from '../../../../routes';
13
14
 
14
15
  import {
15
16
  sendShardQuery,
16
- setShardQueryOptions,
17
- setTopShardFilters,
17
+ setShardsState,
18
+ setShardsQueryFilters,
18
19
  } from '../../../../store/reducers/shardsWorkload';
19
20
  import {setCurrentSchemaPath, getSchema} from '../../../../store/reducers/schema';
20
21
  import type {IShardsWorkloadFilters} from '../../../../types/store/shardsWorkload';
@@ -30,12 +31,10 @@ import {getDefaultNodePath} from '../../../Node/NodePages';
30
31
 
31
32
  import {isColumnEntityType} from '../../utils/schema';
32
33
 
33
- import {DateRange, DateRangeValues} from './DateRange';
34
-
35
34
  import i18n from './i18n';
36
- import './TopShards.scss';
35
+ import './OverloadedShards.scss';
37
36
 
38
- const b = cn('top-shards');
37
+ const b = cn('overloaded-shards');
39
38
  const bLink = cn('yc-link');
40
39
 
41
40
  const TABLE_SETTINGS: Settings = {
@@ -83,12 +82,12 @@ function dataTableToStringSortOrder(value: SortOrder | SortOrder[] = []) {
83
82
  return sortOrders.map(({columnId}) => columnId).join(',');
84
83
  }
85
84
 
86
- interface TopShardsProps {
85
+ interface OverloadedShardsProps {
87
86
  tenantPath: string;
88
87
  type?: EPathType;
89
88
  }
90
89
 
91
- export const TopShards = ({tenantPath, type}: TopShardsProps) => {
90
+ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
92
91
  const dispatch = useDispatch();
93
92
 
94
93
  const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
@@ -134,7 +133,7 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
134
133
  // don't show loader for requests triggered by table sort, only for path change
135
134
  useEffect(() => {
136
135
  dispatch(
137
- setShardQueryOptions({
136
+ setShardsState({
138
137
  wasLoaded: false,
139
138
  data: undefined,
140
139
  }),
@@ -144,14 +143,14 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
144
143
  const history = useContext(HistoryContext);
145
144
 
146
145
  const onSort = (newSortOrder?: SortOrder | SortOrder[]) => {
147
- // 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
148
147
  // use a string (and not the DataTable default format) to prevent reference change,
149
148
  // which would cause an excess state change, to avoid repeating requests
150
149
  setSortOrder(dataTableToStringSortOrder(newSortOrder));
151
150
  };
152
151
 
153
152
  const handleDateRangeChange = (value: DateRangeValues) => {
154
- dispatch(setTopShardFilters(value));
153
+ dispatch(setShardsQueryFilters(value));
155
154
  setFilters(value);
156
155
  };
157
156
 
@@ -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 @@
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;
@@ -0,0 +1,202 @@
1
+ import {useCallback, useEffect, useRef, useState} from 'react';
2
+ import {useDispatch} from 'react-redux';
3
+ import cn from 'bem-cn-lite';
4
+
5
+ import DataTable, {Column, Settings} from '@yandex-cloud/react-data-table';
6
+ import {Loader} from '@gravity-ui/uikit';
7
+
8
+ import {DateRange, DateRangeValues} from '../../../../components/DateRange';
9
+ import {Search} from '../../../../components/Search';
10
+ import TruncatedQuery from '../../../../components/TruncatedQuery/TruncatedQuery';
11
+
12
+ import {changeUserInput} from '../../../../store/reducers/executeQuery';
13
+ import {
14
+ fetchTopQueries,
15
+ setTopQueriesFilters,
16
+ setTopQueriesState,
17
+ } from '../../../../store/reducers/executeTopQueries';
18
+
19
+ import type {KeyValueRow} from '../../../../types/api/query';
20
+ import type {EPathType} from '../../../../types/api/schema';
21
+ import type {ITopQueriesFilters} from '../../../../types/store/executeTopQueries';
22
+ import type {IQueryResult} from '../../../../types/store/query';
23
+
24
+ import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
25
+ import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
26
+ import {prepareQueryError} from '../../../../utils/query';
27
+
28
+ import {isColumnEntityType} from '../../utils/schema';
29
+ import {TenantGeneralTabsIds} from '../../TenantPages';
30
+
31
+ import i18n from './i18n';
32
+ import './TopQueries.scss';
33
+
34
+ const b = cn('kv-top-queries');
35
+
36
+ const TABLE_SETTINGS: Settings = {
37
+ ...DEFAULT_TABLE_SETTINGS,
38
+ dynamicRenderType: 'variable',
39
+ };
40
+
41
+ const MAX_QUERY_HEIGHT = 10;
42
+ const COLUMNS: Column<KeyValueRow>[] = [
43
+ {
44
+ name: 'CPUTimeUs',
45
+ width: 140,
46
+ sortAccessor: (row) => Number(row['CPUTimeUs']),
47
+ },
48
+ {
49
+ name: 'QueryText',
50
+ width: 500,
51
+ sortable: false,
52
+ render: ({value}) => <TruncatedQuery value={value} maxQueryHeight={MAX_QUERY_HEIGHT} />,
53
+ },
54
+ ];
55
+
56
+ interface TopQueriesProps {
57
+ path: string;
58
+ changeSchemaTab: (tab: TenantGeneralTabsIds) => void;
59
+ type?: EPathType;
60
+ }
61
+
62
+ export const TopQueries = ({path, type, changeSchemaTab}: TopQueriesProps) => {
63
+ const dispatch = useDispatch();
64
+
65
+ const {autorefresh} = useTypedSelector((state) => state.schema);
66
+
67
+ const {
68
+ loading,
69
+ wasLoaded,
70
+ error,
71
+ data: {result: data = undefined} = {},
72
+ filters: storeFilters,
73
+ } = useTypedSelector((state) => state.executeTopQueries);
74
+
75
+ const preventFetch = useRef(false);
76
+
77
+ // some filters sync between redux state and URL
78
+ // component state is for default values,
79
+ // default values are determined from the query response, and should not propagate to URL
80
+ const [filters, setFilters] = useState<ITopQueriesFilters>(storeFilters);
81
+
82
+ useEffect(() => {
83
+ dispatch(setTopQueriesFilters(filters));
84
+ }, [dispatch, filters]);
85
+
86
+ const setDefaultFiltersFromResponse = (responseData?: IQueryResult) => {
87
+ const intervalEnd = responseData?.result?.[0]?.IntervalEnd;
88
+
89
+ if (intervalEnd) {
90
+ const to = new Date(intervalEnd).getTime();
91
+ const from = new Date(to - HOUR_IN_SECONDS * 1000).getTime();
92
+
93
+ setFilters((currentFilters) => {
94
+ // request without filters returns the latest interval with data
95
+ // only in this case should update filters in ui
96
+ // also don't update if user already interacted with controls
97
+ const shouldUpdateFilters = !currentFilters.from && !currentFilters.to;
98
+
99
+ if (!shouldUpdateFilters) {
100
+ return currentFilters;
101
+ }
102
+
103
+ preventFetch.current = true;
104
+
105
+ return {...currentFilters, from, to};
106
+ });
107
+ }
108
+ };
109
+
110
+ useAutofetcher(
111
+ (isBackground) => {
112
+ if (preventFetch.current) {
113
+ preventFetch.current = false;
114
+ return;
115
+ }
116
+
117
+ if (!isBackground) {
118
+ dispatch(
119
+ setTopQueriesState({
120
+ wasLoaded: false,
121
+ data: undefined,
122
+ }),
123
+ );
124
+ }
125
+
126
+ // @ts-expect-error
127
+ // typed dispatch required, remove error expectation after adding it
128
+ dispatch(fetchTopQueries({database: path, filters})).then(
129
+ setDefaultFiltersFromResponse,
130
+ );
131
+ },
132
+ [dispatch, filters, path],
133
+ autorefresh,
134
+ );
135
+
136
+ const handleRowClick = useCallback(
137
+ (row) => {
138
+ const {QueryText: input} = row;
139
+
140
+ dispatch(changeUserInput({input}));
141
+ changeSchemaTab(TenantGeneralTabsIds.query);
142
+ },
143
+ [changeSchemaTab, dispatch],
144
+ );
145
+
146
+ const handleTextSearchUpdate = (text: string) => {
147
+ setFilters((currentFilters) => ({...currentFilters, text}));
148
+ };
149
+
150
+ const handleDateRangeChange = (value: DateRangeValues) => {
151
+ setFilters((currentFilters) => ({...currentFilters, ...value}));
152
+ };
153
+
154
+ const renderLoader = () => {
155
+ return (
156
+ <div className={b('loader')}>
157
+ <Loader size="m" />
158
+ </div>
159
+ );
160
+ };
161
+
162
+ const renderContent = () => {
163
+ if (loading && !wasLoaded) {
164
+ return renderLoader();
165
+ }
166
+
167
+ if (error && !error.isCancelled) {
168
+ return <div className="error">{prepareQueryError(error)}</div>;
169
+ }
170
+
171
+ if (!data || isColumnEntityType(type)) {
172
+ return i18n('no-data');
173
+ }
174
+
175
+ return (
176
+ <div className={b('result')}>
177
+ <DataTable
178
+ columns={COLUMNS}
179
+ data={data}
180
+ settings={TABLE_SETTINGS}
181
+ onRowClick={handleRowClick}
182
+ theme="yandex-cloud"
183
+ />
184
+ </div>
185
+ );
186
+ };
187
+
188
+ return (
189
+ <div className={b()}>
190
+ <div className={b('controls')}>
191
+ <Search
192
+ value={filters.text}
193
+ onChange={handleTextSearchUpdate}
194
+ placeholder={i18n('filter.text.placeholder')}
195
+ className={b('search')}
196
+ />
197
+ <DateRange from={filters.from} to={filters.to} onChange={handleDateRangeChange} />
198
+ </div>
199
+ {renderContent()}
200
+ </div>
201
+ );
202
+ };
@@ -0,0 +1,4 @@
1
+ {
2
+ "no-data": "No data",
3
+ "filter.text.placeholder": "Search by query text..."
4
+ }
@@ -3,7 +3,7 @@ import {i18n, Lang} from '../../../../../utils/i18n';
3
3
  import en from './en.json';
4
4
  import ru from './ru.json';
5
5
 
6
- const COMPONENT = 'ydb-diagnostics-top-shards';
6
+ const COMPONENT = 'ydb-diagnostics-top-queries';
7
7
 
8
8
  i18n.registerKeyset(Lang.En, COMPONENT, en);
9
9
  i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
@@ -0,0 +1,4 @@
1
+ {
2
+ "no-data": "Нет данных",
3
+ "filter.text.placeholder": "Искать по тексту запроса..."
4
+ }
@@ -0,0 +1 @@
1
+ export * from './TopQueries';
@@ -67,7 +67,7 @@ const mapStateToProps = (state: any) => {
67
67
 
68
68
  return {
69
69
  theme,
70
- invertedDisks,
70
+ invertedDisks: JSON.parse(invertedDisks),
71
71
  };
72
72
  };
73
73
 
@@ -48,6 +48,13 @@ interface Window {
48
48
  getTenantInfo: (params: {
49
49
  path: string;
50
50
  }) => Promise<import('../types/api/tenant').TTenantInfo>;
51
+ getTabletsInfo: (params: {
52
+ nodes?: string[];
53
+ path?: string;
54
+ }) => Promise<import('../types/api/tablet').TEvTabletStateResponse>;
55
+ getHeatmapData: (params: {
56
+ path: string;
57
+ }) => Promise<import('../types/api/schema').TEvDescribeSchemeResult>;
51
58
  [method: string]: Function;
52
59
  };
53
60
  }