ydb-embedded-ui 4.1.0 → 4.2.1

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 (26) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/components/LabelWithPopover/LabelWithPopover.tsx +10 -4
  3. package/dist/components/TabletsStatistic/TabletsStatistic.scss +1 -1
  4. package/dist/containers/App/App.scss +7 -4
  5. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +3 -12
  6. package/dist/containers/Storage/VDisk/VDisk.tsx +4 -11
  7. package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +13 -16
  8. package/dist/containers/Storage/utils/types.ts +2 -1
  9. package/dist/containers/Tablet/Tablet.scss +4 -0
  10. package/dist/containers/Tablet/TabletTable/TabletTable.tsx +28 -6
  11. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +1 -1
  12. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +4 -3
  13. package/dist/containers/Tenant/QueryEditor/Issues/Issues.scss +1 -1
  14. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/OldQueryEditorControls.tsx +1 -1
  15. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.tsx +1 -1
  16. package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +12 -0
  17. package/dist/containers/UserSettings/UserSettings.tsx +19 -17
  18. package/dist/services/api.ts +9 -9
  19. package/dist/store/reducers/node.js +5 -1
  20. package/dist/store/reducers/nodesList.ts +2 -7
  21. package/dist/store/reducers/storage.js +12 -0
  22. package/dist/store/reducers/tablet.ts +16 -2
  23. package/dist/types/store/tablet.ts +1 -0
  24. package/dist/utils/createToast.tsx +3 -3
  25. package/dist/utils/nodes.ts +11 -0
  26. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.2.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.2.0...v4.2.1) (2023-05-18)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * export toaster ([b5d12c0](https://github.com/ydb-platform/ydb-embedded-ui/commit/b5d12c0aa39ea3877a9b74071e3124f89a309ca3))
9
+
10
+ ## [4.2.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.1.0...v4.2.0) (2023-05-16)
11
+
12
+
13
+ ### Features
14
+
15
+ * **Tablet:** display node fqdn in table ([4d8099a](https://github.com/ydb-platform/ydb-embedded-ui/commit/4d8099a454f34fc76886b26ca948895171c57ab8))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **api:** change nulls to empty objects ([0ab14e8](https://github.com/ydb-platform/ydb-embedded-ui/commit/0ab14e883a47aeac2f2bab437f2214a32ccb1c9b))
21
+ * display storage pool in VDisks popups ([5b5dd8a](https://github.com/ydb-platform/ydb-embedded-ui/commit/5b5dd8a4e6cb4bcc1ead78a7c06d2e80a81424cc))
22
+ * fix Select label and values align ([f796730](https://github.com/ydb-platform/ydb-embedded-ui/commit/f7967309fe4a042e7637de212f33b1ebfc6877fc))
23
+ * **Overview:** partitioning by size disabled for 0 SizeToSpit ([1028e7d](https://github.com/ydb-platform/ydb-embedded-ui/commit/1028e7d8d3566f5f5e6b2ebe04112ef135d7b55e))
24
+ * **Schema:** display NotNull columns ([d61eaa4](https://github.com/ydb-platform/ydb-embedded-ui/commit/d61eaa4ccff357c1e9ca6efde855ec46be24a314))
25
+
3
26
  ## [4.1.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.0.0...v4.1.0) (2023-05-10)
4
27
 
5
28
 
@@ -3,14 +3,20 @@ import type {ReactNode} from 'react';
3
3
  import {HelpPopover} from '@gravity-ui/uikit';
4
4
 
5
5
  interface LabelWithPopoverProps {
6
- text: string;
6
+ text: ReactNode;
7
7
  popoverContent: ReactNode;
8
8
  className?: string;
9
+ contentClassName?: string;
9
10
  }
10
11
 
11
- export const LabelWithPopover = ({text, popoverContent, className}: LabelWithPopoverProps) => (
12
+ export const LabelWithPopover = ({
13
+ text,
14
+ popoverContent,
15
+ className,
16
+ contentClassName,
17
+ }: LabelWithPopoverProps) => (
12
18
  <div className={className}>
13
- {text}
14
- <HelpPopover content={popoverContent} />
19
+ {text + '\u00a0'}
20
+ <HelpPopover content={popoverContent} contentClassName={contentClassName} />
15
21
  </div>
16
22
  );
@@ -24,7 +24,7 @@
24
24
  background-color: var(--yc-color-base-positive);
25
25
  }
26
26
  &_state_yellow {
27
- color: var(--yc-color-text-warning-medium);
27
+ color: var(--yc-color-text-warning);
28
28
  background-color: var(--yc-color-base-warning);
29
29
  }
30
30
  &_state_blue {
@@ -34,10 +34,6 @@ body,
34
34
  --ydb-data-table-color-hover: var(--yc-color-base-float-hover);
35
35
  }
36
36
 
37
- .yc-select__label {
38
- font-weight: 600;
39
- }
40
-
41
37
  :is(#tab, .yc-tabs-item_active .yc-tabs-item__title) {
42
38
  color: var(--yc-color-text-primary) !important;
43
39
  }
@@ -110,10 +106,17 @@ body,
110
106
  align-items: center;
111
107
  }
112
108
 
109
+ // Should be removed after https://github.com/ydb-platform/ydb-embedded-ui/issues/344
113
110
  .yc-button__text {
114
111
  display: flex;
115
112
  align-items: center;
116
113
  }
114
+
115
+ .g-select {
116
+ .yc-button__text {
117
+ align-items: baseline;
118
+ }
119
+ }
117
120
  }
118
121
 
119
122
  .error {
@@ -256,18 +256,14 @@ function StorageGroups({
256
256
  name: TableColumnsIds.VDisks,
257
257
  className: b('vdisks-column'),
258
258
  header: tableColumnsNames[TableColumnsIds.VDisks],
259
- render: ({value, row}) => (
259
+ render: ({value}) => (
260
260
  <div className={b('vdisks-wrapper')}>
261
261
  {_.map(value as TVDiskStateInfo[], (el) => {
262
262
  const donors = el.Donors;
263
263
 
264
264
  return donors && donors.length > 0 ? (
265
265
  <Stack className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
266
- <VDisk
267
- data={el}
268
- poolName={row[TableColumnsIds.PoolName]}
269
- nodes={nodes}
270
- />
266
+ <VDisk data={el} nodes={nodes} />
271
267
  {donors.map((donor) => {
272
268
  const isFullData = isFullVDiskData(donor);
273
269
 
@@ -275,7 +271,6 @@ function StorageGroups({
275
271
  <VDisk
276
272
  data={isFullData ? donor : {...donor, DonorMode: true}}
277
273
  // donor and acceptor are always in the same group
278
- poolName={row[TableColumnsIds.PoolName]}
279
274
  nodes={nodes}
280
275
  key={stringifyVdiskId(
281
276
  isFullData ? donor.VDiskId : donor,
@@ -286,11 +281,7 @@ function StorageGroups({
286
281
  </Stack>
287
282
  ) : (
288
283
  <div className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
289
- <VDisk
290
- data={el}
291
- poolName={row[TableColumnsIds.PoolName]}
292
- nodes={nodes}
293
- />
284
+ <VDisk data={el} nodes={nodes} />
294
285
  </div>
295
286
  );
296
287
  })}
@@ -16,7 +16,7 @@ import {STRUCTURE} from '../../Node/NodePages';
16
16
  import {DiskStateProgressBar, EDiskStateSeverity} from '../DiskStateProgressBar';
17
17
  import {VDiskPopup} from '../VDiskPopup';
18
18
 
19
- import type {IUnavailableDonor} from '../utils/types';
19
+ import type {UnavailableDonor} from '../utils/types';
20
20
  import {NOT_AVAILABLE_SEVERITY} from '../utils';
21
21
 
22
22
  import './VDisk.scss';
@@ -49,13 +49,12 @@ const getColorSeverity = (color?: EFlag) => {
49
49
  };
50
50
 
51
51
  interface VDiskProps {
52
- data?: TVDiskStateInfo | IUnavailableDonor;
53
- poolName?: string;
52
+ data?: TVDiskStateInfo | UnavailableDonor;
54
53
  nodes?: NodesMap;
55
54
  compact?: boolean;
56
55
  }
57
56
 
58
- export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
57
+ export const VDisk = ({data = {}, nodes, compact}: VDiskProps) => {
59
58
  const isFullData = isFullVDiskData(data);
60
59
 
61
60
  const [severity, setSeverity] = useState(
@@ -125,13 +124,7 @@ export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
125
124
 
126
125
  return (
127
126
  <React.Fragment>
128
- <VDiskPopup
129
- data={data}
130
- poolName={poolName}
131
- nodes={nodes}
132
- anchorRef={anchor}
133
- open={isPopupVisible}
134
- />
127
+ <VDiskPopup data={data} nodes={nodes} anchorRef={anchor} open={isPopupVisible} />
135
128
  <div className={b()} ref={anchor} onMouseEnter={showPopup} onMouseLeave={hidePopup}>
136
129
  {data.NodeId && isFullData ? (
137
130
  <InternalLink
@@ -13,7 +13,7 @@ import {stringifyVdiskId} from '../../../utils';
13
13
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
14
14
  import {isFullVDiskData} from '../../../utils/storage';
15
15
 
16
- import type {IUnavailableDonor} from '../utils/types';
16
+ import type {UnavailableDonor} from '../utils/types';
17
17
 
18
18
  import {preparePDiskData} from '../PDiskPopup';
19
19
 
@@ -21,13 +21,13 @@ import './VDiskPopup.scss';
21
21
 
22
22
  const b = cn('vdisk-storage-popup');
23
23
 
24
- const prepareUnavailableVDiskData = (data: IUnavailableDonor, poolName?: string) => {
25
- const {NodeId, PDiskId, VSlotId} = data;
24
+ const prepareUnavailableVDiskData = (data: UnavailableDonor) => {
25
+ const {NodeId, PDiskId, VSlotId, StoragePoolName} = data;
26
26
 
27
27
  const vdiskData: InfoViewerItem[] = [{label: 'State', value: 'not available'}];
28
28
 
29
- if (poolName) {
30
- vdiskData.push({label: 'StoragePool', value: poolName});
29
+ if (StoragePoolName) {
30
+ vdiskData.push({label: 'StoragePool', value: StoragePoolName});
31
31
  }
32
32
 
33
33
  vdiskData.push(
@@ -39,7 +39,7 @@ const prepareUnavailableVDiskData = (data: IUnavailableDonor, poolName?: string)
39
39
  return vdiskData;
40
40
  };
41
41
 
42
- const prepareVDiskData = (data: TVDiskStateInfo, poolName?: string) => {
42
+ const prepareVDiskData = (data: TVDiskStateInfo) => {
43
43
  const {
44
44
  VDiskId,
45
45
  VDiskState,
@@ -51,6 +51,7 @@ const prepareVDiskData = (data: TVDiskStateInfo, poolName?: string) => {
51
51
  AllocatedSize,
52
52
  ReadThroughput,
53
53
  WriteThroughput,
54
+ StoragePoolName,
54
55
  } = data;
55
56
 
56
57
  const vdiskData: InfoViewerItem[] = [
@@ -58,8 +59,8 @@ const prepareVDiskData = (data: TVDiskStateInfo, poolName?: string) => {
58
59
  {label: 'State', value: VDiskState ?? 'not available'},
59
60
  ];
60
61
 
61
- if (poolName) {
62
- vdiskData.push({label: 'StoragePool', value: poolName});
62
+ if (StoragePoolName) {
63
+ vdiskData.push({label: 'StoragePool', value: StoragePoolName});
63
64
  }
64
65
 
65
66
  if (SatisfactionRank && SatisfactionRank.FreshRank?.Flag !== EFlag.Green) {
@@ -128,20 +129,16 @@ const prepareVDiskData = (data: TVDiskStateInfo, poolName?: string) => {
128
129
  };
129
130
 
130
131
  interface VDiskPopupProps extends PopupProps {
131
- data: TVDiskStateInfo | IUnavailableDonor;
132
- poolName?: string;
132
+ data: TVDiskStateInfo | UnavailableDonor;
133
133
  nodes?: NodesMap;
134
134
  }
135
135
 
136
- export const VDiskPopup = ({data, poolName, nodes, ...props}: VDiskPopupProps) => {
136
+ export const VDiskPopup = ({data, nodes, ...props}: VDiskPopupProps) => {
137
137
  const isFullData = isFullVDiskData(data);
138
138
 
139
139
  const vdiskInfo = useMemo(
140
- () =>
141
- isFullData
142
- ? prepareVDiskData(data, poolName)
143
- : prepareUnavailableVDiskData(data, poolName),
144
- [data, poolName, isFullData],
140
+ () => (isFullData ? prepareVDiskData(data) : prepareUnavailableVDiskData(data)),
141
+ [data, isFullData],
145
142
  );
146
143
  const pdiskInfo = useMemo(
147
144
  () => isFullData && data.PDisk && preparePDiskData(data.PDisk, nodes),
@@ -1,5 +1,6 @@
1
1
  import {TVSlotId} from '../../../types/api/vdisk';
2
2
 
3
- export interface IUnavailableDonor extends TVSlotId {
3
+ export interface UnavailableDonor extends TVSlotId {
4
4
  DonorMode?: boolean;
5
+ StoragePoolName?: string;
5
6
  }
@@ -89,4 +89,8 @@
89
89
  line-height: var(--yc-text-body-2-line-height);
90
90
  text-transform: uppercase;
91
91
  }
92
+
93
+ &__host {
94
+ width: 300px;
95
+ }
92
96
  }
@@ -1,7 +1,13 @@
1
1
  import DataTable, {Column} from '@gravity-ui/react-data-table';
2
2
 
3
+ import EntityStatus from '../../../components/EntityStatus/EntityStatus';
4
+ import {InternalLink} from '../../../components/InternalLink/InternalLink';
5
+
3
6
  import type {ITabletPreparedHistoryItem} from '../../../types/store/tablet';
4
7
  import {calcUptime} from '../../../utils';
8
+ import {getDefaultNodePath} from '../../Node/NodePages';
9
+
10
+ import {b} from '../Tablet';
5
11
 
6
12
  const columns: Column<ITabletPreparedHistoryItem>[] = [
7
13
  {
@@ -9,12 +15,6 @@ const columns: Column<ITabletPreparedHistoryItem>[] = [
9
15
  align: DataTable.RIGHT,
10
16
  render: ({row}) => row.generation,
11
17
  },
12
- {
13
- name: 'Node ID',
14
- align: DataTable.RIGHT,
15
- sortable: false,
16
- render: ({row}) => row.nodeId,
17
- },
18
18
  {
19
19
  name: 'Change time',
20
20
  align: DataTable.RIGHT,
@@ -33,6 +33,28 @@ const columns: Column<ITabletPreparedHistoryItem>[] = [
33
33
  return row.leader ? 'leader' : row.followerId;
34
34
  },
35
35
  },
36
+ {
37
+ name: 'Node ID',
38
+ align: DataTable.RIGHT,
39
+ sortable: false,
40
+ render: ({row}) => {
41
+ return <InternalLink to={getDefaultNodePath(row.nodeId)}>{row.nodeId}</InternalLink>;
42
+ },
43
+ },
44
+ {
45
+ name: 'Node FQDN',
46
+ sortable: false,
47
+ render: ({row}) => {
48
+ if (!row.fqdn) {
49
+ return <span>—</span>;
50
+ }
51
+ return (
52
+ <div className={b('host')}>
53
+ <EntityStatus name={row.fqdn} showStatus={false} hasClipboardButton />
54
+ </div>
55
+ );
56
+ },
57
+ },
36
58
  ];
37
59
 
38
60
  const TABLE_SETTINGS = {
@@ -80,7 +80,7 @@
80
80
  }
81
81
  &_degraded,
82
82
  &_yellow {
83
- color: var(--yc-color-text-warning-medium);
83
+ color: var(--yc-color-text-warning);
84
84
  background-color: var(--yc-color-base-warning);
85
85
  }
86
86
 
@@ -86,9 +86,10 @@ const prepareTableGeneralInfo = (PartitionConfig: TPartitionConfig, TTLSettings?
86
86
 
87
87
  const generalTableInfo: InfoViewerItem[] = [];
88
88
 
89
- const partitioningBySize = PartitioningPolicy.SizeToSplit
90
- ? `Enabled, split size: ${formatBytes(PartitioningPolicy.SizeToSplit)}`
91
- : 'Disabled';
89
+ const partitioningBySize =
90
+ PartitioningPolicy.SizeToSplit && Number(PartitioningPolicy.SizeToSplit) > 0
91
+ ? `Enabled, split size: ${formatBytes(PartitioningPolicy.SizeToSplit)}`
92
+ : 'Disabled';
92
93
 
93
94
  const partitioningByLoad = PartitioningPolicy.SplitByLoadSettings?.Enabled
94
95
  ? 'Enabled'
@@ -106,7 +106,7 @@
106
106
  }
107
107
 
108
108
  &_severity_warning &__icon {
109
- color: var(--yc-color-text-warning-medium);
109
+ color: var(--yc-color-text-warning);
110
110
  }
111
111
 
112
112
  &_severity_info &__icon {
@@ -48,7 +48,7 @@ export const OldQueryEditorControls = ({
48
48
  </Button>
49
49
  <DropdownMenu
50
50
  items={runModeSelectorMenuItems}
51
- popupClassName={b('select-query-action-popup')}
51
+ popupProps={{className: b('select-query-action-popup')}}
52
52
  switcher={
53
53
  <Button
54
54
  view="action"
@@ -61,7 +61,7 @@ export const QueryEditorControls = ({
61
61
  </Button>
62
62
  <DropdownMenu
63
63
  items={querySelectorMenuItems}
64
- popupClassName={b('mode-selector__popup')}
64
+ popupProps={{className: b('mode-selector__popup')}}
65
65
  switcher={
66
66
  <Button className={b('mode-selector__button')}>
67
67
  <span className={b('mode-selector__button-content')}>
@@ -16,6 +16,7 @@ const SchemaViewerColumns = {
16
16
  name: 'Name',
17
17
  key: 'Key',
18
18
  type: 'Type',
19
+ notNull: 'NotNull',
19
20
  };
20
21
 
21
22
  class SchemaViewer extends React.Component {
@@ -59,6 +60,17 @@ class SchemaViewer extends React.Component {
59
60
  name: SchemaViewerColumns.type,
60
61
  width: 100,
61
62
  },
63
+ {
64
+ name: SchemaViewerColumns.notNull,
65
+ width: 100,
66
+ render: ({row}) => {
67
+ if (row.NotNull) {
68
+ return '\u2713';
69
+ }
70
+
71
+ return undefined;
72
+ },
73
+ },
62
74
  ];
63
75
 
64
76
  const tableData = [...keyColumns, ...restColumns];
@@ -2,12 +2,14 @@ import {ReactNode} from 'react';
2
2
  import {connect} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
- import {RadioButton, Switch, HelpPopover} from '@gravity-ui/uikit';
5
+ import {RadioButton, Switch} from '@gravity-ui/uikit';
6
6
  import {Settings} from '@gravity-ui/navigation';
7
7
 
8
8
  import favoriteFilledIcon from '../../assets/icons/star.svg';
9
9
  import flaskIcon from '../../assets/icons/flask.svg';
10
10
 
11
+ import {LabelWithPopover} from '../../components/LabelWithPopover/LabelWithPopover';
12
+
11
13
  import {
12
14
  ENABLE_QUERY_MODES_FOR_EXPLAIN,
13
15
  INVERTED_DISKS_KEY,
@@ -46,27 +48,27 @@ function UserSettings(props: any) {
46
48
 
47
49
  const renderBreakNodesSettingsItem = (title: ReactNode) => {
48
50
  return (
49
- <div className={b('item-with-popup')}>
50
- {title}
51
- <HelpPopover
52
- content="Use /viewer/json/nodes endpoint for Nodes Tab in diagnostics. It returns incorrect data on older versions"
53
- contentClassName={b('popup')}
54
- hasArrow={true}
55
- />
56
- </div>
51
+ <LabelWithPopover
52
+ className={b('item-with-popup')}
53
+ contentClassName={b('popup')}
54
+ text={title}
55
+ popoverContent={
56
+ 'Use /viewer/json/nodes endpoint for Nodes Tab in diagnostics. It returns incorrect data on older versions'
57
+ }
58
+ />
57
59
  );
58
60
  };
59
61
 
60
62
  const renderEnableExplainQueryModesItem = (title: ReactNode) => {
61
63
  return (
62
- <div className={b('item-with-popup')}>
63
- {title}
64
- <HelpPopover
65
- content="Enable script | scan query mode selector for both run and explain. May not work on some versions"
66
- contentClassName={b('popup')}
67
- hasArrow={true}
68
- />
69
- </div>
64
+ <LabelWithPopover
65
+ className={b('item-with-popup')}
66
+ contentClassName={b('popup')}
67
+ text={title}
68
+ popoverContent={
69
+ 'Enable script | scan query mode selector for both run and explain. May not work on some versions'
70
+ }
71
+ />
70
72
  );
71
73
  };
72
74
 
@@ -287,7 +287,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
287
287
  stats,
288
288
  timeout: 600000,
289
289
  },
290
- null,
290
+ {},
291
291
  {
292
292
  concurrentId,
293
293
  timeout: 9 * 60 * 1000,
@@ -307,7 +307,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
307
307
  action: action || 'explain',
308
308
  timeout: 600000,
309
309
  },
310
- null,
310
+ {},
311
311
  );
312
312
  }
313
313
  getExplainQueryAst(query: string, database: string) {
@@ -319,7 +319,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
319
319
  action: 'explain-ast',
320
320
  timeout: 600000,
321
321
  },
322
- null,
322
+ {},
323
323
  );
324
324
  }
325
325
  getHotKeys(path: string, enableSampling: boolean) {
@@ -334,18 +334,18 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
334
334
  });
335
335
  }
336
336
  killTablet(id?: string) {
337
- return this.get<string>(this.getPath(`/tablets?KillTabletID=${id}`), null);
337
+ return this.get<string>(this.getPath(`/tablets?KillTabletID=${id}`), {});
338
338
  }
339
339
  stopTablet(id?: string, hiveId?: string) {
340
340
  return this.get<string>(
341
341
  this.getPath(`/tablets/app?TabletID=${hiveId}&page=StopTablet&tablet=${id}`),
342
- null,
342
+ {},
343
343
  );
344
344
  }
345
345
  resumeTablet(id?: string, hiveId?: string) {
346
346
  return this.get<string>(
347
347
  this.getPath(`/tablets/app?TabletID=${hiveId}&page=ResumeTablet&tablet=${id}`),
348
- null,
348
+ {},
349
349
  );
350
350
  }
351
351
  getTabletDescribe(tenantId: TDomainKey) {
@@ -368,14 +368,14 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
368
368
  user,
369
369
  password,
370
370
  },
371
- null,
371
+ {},
372
372
  );
373
373
  }
374
374
  logout() {
375
- return this.post(this.getPath('/logout'), null, null);
375
+ return this.post(this.getPath('/logout'), {}, {});
376
376
  }
377
377
  whoami() {
378
- return this.get<TUserToken>(this.getPath('/viewer/json/whoami'), null);
378
+ return this.get<TUserToken>(this.getPath('/viewer/json/whoami'), {});
379
379
  }
380
380
  }
381
381
 
@@ -116,7 +116,11 @@ export const selectNodeStructure = createSelector(
116
116
  if (!structure[String(pDiskId)]) {
117
117
  structure[String(pDiskId)] = {vDisks: {}, ...vd.PDisk};
118
118
  }
119
- structure[String(pDiskId)].vDisks[vDiskId] = vd;
119
+ structure[String(pDiskId)].vDisks[vDiskId] = {
120
+ ...vd,
121
+ // VDisk doesn't have its own StoragePoolName when located inside StoragePool data
122
+ StoragePoolName: pool.Name,
123
+ };
120
124
  });
121
125
  });
122
126
  });
@@ -4,10 +4,10 @@ import type {
4
4
  NodesListState,
5
5
  NodesListAction,
6
6
  NodesListRootStateSlice,
7
- NodesMap,
8
7
  } from '../../types/store/nodesList';
9
8
  import '../../services/api';
10
9
  import {createRequestActionTypes, createApiRequest} from '../utils';
10
+ import {prepareNodesMap} from '../../utils/nodes';
11
11
 
12
12
  export const FETCH_NODES_LIST = createRequestActionTypes('nodesList', 'FETCH_NODES_LIST');
13
13
 
@@ -50,11 +50,6 @@ export function getNodesList() {
50
50
  }
51
51
 
52
52
  export const selectNodesMap = (state: NodesListRootStateSlice) =>
53
- state.nodesList.data?.reduce<NodesMap>((nodesMap, node) => {
54
- if (node.Id && node.Host) {
55
- nodesMap.set(node.Id, node.Host);
56
- }
57
- return nodesMap;
58
- }, new Map());
53
+ prepareNodesMap(state.nodesList.data);
59
54
 
60
55
  export default nodesList;
@@ -271,10 +271,22 @@ export const getFlatListStorageGroups = createSelector([getStoragePools], (stora
271
271
  ? currentType
272
272
  : 'Mixed';
273
273
  }, '');
274
+
275
+ // VDisk doesn't have its own StoragePoolName when located inside StoragePool data
276
+ const vDisks = group.VDisks?.map((vdisk) => ({
277
+ ...vdisk,
278
+ StoragePoolName: pool.Name,
279
+ Donors: vdisk.Donors?.map((donor) => ({
280
+ ...donor,
281
+ StoragePoolName: pool.Name,
282
+ })),
283
+ }));
284
+
274
285
  return [
275
286
  ...acc,
276
287
  {
277
288
  ...group,
289
+ VDisks: vDisks,
278
290
  Read: readSpeedBytesPerSec,
279
291
  Write: writeSpeedBytesPerSec,
280
292
  PoolName: pool.Name,
@@ -11,6 +11,7 @@ import type {
11
11
  import '../../services/api';
12
12
 
13
13
  import {createRequestActionTypes, createApiRequest} from '../utils';
14
+ import {prepareNodesMap} from '../../utils/nodes';
14
15
 
15
16
  export const FETCH_TABLET = createRequestActionTypes('TABLET', 'FETCH_TABLET');
16
17
  export const FETCH_TABLET_DESCRIBE = createRequestActionTypes('TABLET', 'FETCH_TABLET_DESCRIBE');
@@ -63,9 +64,19 @@ const tablet: Reducer<ITabletState, ITabletAction> = (state = initialState, acti
63
64
 
64
65
  export const getTablet = (id: string) => {
65
66
  return createApiRequest({
66
- request: Promise.all([window.api.getTablet({id}), window.api.getTabletHistory({id})]),
67
+ request: Promise.all([
68
+ window.api.getTablet({id}),
69
+ window.api.getTabletHistory({id}),
70
+ window.api.getNodesList(),
71
+ ]),
67
72
  actions: FETCH_TABLET,
68
- dataHandler: ([tabletResponseData, historyResponseData]): ITabletHandledResponse => {
73
+ dataHandler: ([
74
+ tabletResponseData,
75
+ historyResponseData,
76
+ nodesList,
77
+ ]): ITabletHandledResponse => {
78
+ const nodesMap = prepareNodesMap(nodesList);
79
+
69
80
  const historyData = Object.keys(historyResponseData).reduce<
70
81
  ITabletPreparedHistoryItem[]
71
82
  >((list, nodeId) => {
@@ -75,6 +86,8 @@ export const getTablet = (id: string) => {
75
86
 
76
87
  const {ChangeTime, Generation, State, Leader, FollowerId} = leaderTablet;
77
88
 
89
+ const fqdn = nodesMap && nodeId ? nodesMap.get(Number(nodeId)) : undefined;
90
+
78
91
  list.push({
79
92
  nodeId,
80
93
  generation: Generation,
@@ -82,6 +95,7 @@ export const getTablet = (id: string) => {
82
95
  state: State,
83
96
  leader: Leader,
84
97
  followerId: FollowerId,
98
+ fqdn,
85
99
  });
86
100
  }
87
101
  return list;
@@ -11,6 +11,7 @@ export interface ITabletPreparedHistoryItem {
11
11
  state: ETabletState | undefined;
12
12
  leader: boolean | undefined;
13
13
  followerId: number | undefined;
14
+ fqdn: string | undefined;
14
15
  }
15
16
 
16
17
  export interface ITabletState {
@@ -1,6 +1,6 @@
1
1
  import {Toaster} from '@gravity-ui/uikit';
2
2
 
3
- const toaster = new Toaster();
3
+ export const toaster = new Toaster();
4
4
 
5
5
  interface CreateToastProps {
6
6
  name?: string;
@@ -10,13 +10,13 @@ interface CreateToastProps {
10
10
  }
11
11
 
12
12
  function createToast({name, title, type, content}: CreateToastProps) {
13
- return toaster.createToast({
13
+ return toaster.add({
14
14
  name: name ?? 'Request succeeded',
15
15
  title: title ?? 'Request succeeded',
16
16
  type: type ?? 'success',
17
17
  content: content,
18
18
  isClosable: true,
19
- allowAutoHiding: type === 'success',
19
+ autoHiding: type === 'success' ? 5000 : false,
20
20
  });
21
21
  }
22
22
 
@@ -1,5 +1,7 @@
1
1
  import type {TSystemStateInfo} from '../types/api/nodes';
2
+ import type {TNodeInfo} from '../types/api/nodesList';
2
3
  import type {INodesPreparedEntity} from '../types/store/nodes';
4
+ import type {NodesMap} from '../types/store/nodesList';
3
5
  import {EFlag} from '../types/api/enums';
4
6
 
5
7
  export enum NodesUptimeFilterValues {
@@ -20,3 +22,12 @@ export type NodeAddress = Pick<TSystemStateInfo, 'Host' | 'Endpoints'>;
20
22
  export interface AdditionalNodesInfo extends Record<string, unknown> {
21
23
  getNodeRef?: (node?: NodeAddress) => string;
22
24
  }
25
+
26
+ export const prepareNodesMap = (nodesList?: TNodeInfo[]) => {
27
+ return nodesList?.reduce<NodesMap>((nodesMap, node) => {
28
+ if (node.Id && node.Host) {
29
+ nodesMap.set(Number(node.Id), node.Host);
30
+ }
31
+ return nodesMap;
32
+ }, new Map());
33
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "4.1.0",
3
+ "version": "4.2.1",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -12,7 +12,7 @@
12
12
  "@gravity-ui/axios-wrapper": "^1.3.0",
13
13
  "@gravity-ui/date-utils": "^1.1.1",
14
14
  "@gravity-ui/i18n": "^1.0.0",
15
- "@gravity-ui/navigation": "^0.3.1",
15
+ "@gravity-ui/navigation": "^0.4.0",
16
16
  "@gravity-ui/paranoid": "^1.4.0",
17
17
  "@gravity-ui/react-data-table": "^1.0.3",
18
18
  "axios": "0.19.2",
@@ -39,7 +39,7 @@
39
39
  "reselect": "4.1.6",
40
40
  "sass": "1.32.8",
41
41
  "web-vitals": "1.1.2",
42
- "ydb-ui-components": "^3.0.3"
42
+ "ydb-ui-components": "^3.1.0"
43
43
  },
44
44
  "scripts": {
45
45
  "start": "react-app-rewired start",
@@ -105,7 +105,7 @@
105
105
  "@gravity-ui/prettier-config": "^1.0.1",
106
106
  "@gravity-ui/stylelint-config": "^1.0.1",
107
107
  "@gravity-ui/tsconfig": "^1.0.0",
108
- "@gravity-ui/uikit": "^3.20.2",
108
+ "@gravity-ui/uikit": "^4.11.1",
109
109
  "@playwright/test": "^1.31.1",
110
110
  "@testing-library/jest-dom": "^5.15.0",
111
111
  "@testing-library/react": "^11.2.7",