ydb-embedded-ui 3.4.0 → 3.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +2 -3
  3. package/dist/components/ClusterInfo/ClusterInfo.tsx +14 -36
  4. package/dist/components/CopyToClipboard/CopyToClipboard.tsx +1 -2
  5. package/dist/components/CriticalActionDialog/CriticalActionDialog.js +1 -1
  6. package/dist/components/EmptyState/{EmptyState.js → EmptyState.tsx} +12 -15
  7. package/dist/components/EmptyState/index.ts +1 -0
  8. package/dist/components/EnableFullscreenButton/EnableFullscreenButton.tsx +7 -2
  9. package/dist/components/Errors/403/AccessDenied.tsx +1 -1
  10. package/dist/components/Fullscreen/Fullscreen.tsx +1 -1
  11. package/dist/components/Icon/Icon.tsx +33 -0
  12. package/dist/components/Icon/index.ts +1 -0
  13. package/dist/components/Pagination/Pagination.js +1 -1
  14. package/dist/components/QueryExecutionStatus/QueryExecutionStatus.tsx +1 -1
  15. package/dist/components/Tablet/Tablet.tsx +59 -0
  16. package/dist/components/Tablet/index.ts +1 -0
  17. package/dist/components/Tag/Tag.tsx +16 -0
  18. package/dist/components/Tag/index.ts +1 -0
  19. package/dist/components/Tags/Tags.tsx +22 -0
  20. package/dist/components/Tags/index.ts +1 -0
  21. package/dist/containers/Header/Header.tsx +2 -3
  22. package/dist/containers/Header/Host/Host.js +1 -1
  23. package/dist/containers/Node/NodeStructure/Pdisk.tsx +1 -1
  24. package/dist/containers/Nodes/getNodesColumns.tsx +1 -1
  25. package/dist/containers/Storage/EmptyFilter/EmptyFilter.tsx +8 -9
  26. package/dist/containers/Storage/PDisk/PDisk.scss +15 -0
  27. package/dist/containers/Storage/PDisk/PDisk.tsx +38 -13
  28. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +4 -2
  29. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +1 -0
  30. package/dist/containers/Tablet/Tablet.js +6 -6
  31. package/dist/containers/Tablets/Tablets.scss +0 -4
  32. package/dist/containers/Tablets/Tablets.tsx +1 -2
  33. package/dist/containers/TabletsFilters/TabletsFilters.js +1 -1
  34. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.scss +1 -1
  35. package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +27 -1
  36. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +1 -2
  37. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
  38. package/dist/containers/Tenant/Diagnostics/Network/Network.js +1 -1
  39. package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.tsx +17 -13
  40. package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsWrapper.tsx +2 -0
  41. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +1 -1
  42. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +1 -1
  43. package/dist/containers/Tenant/Preview/Preview.js +1 -1
  44. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +1 -1
  45. package/dist/containers/Tenant/QueryEditor/SavedQueries/SavedQueries.js +1 -1
  46. package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +1 -1
  47. package/dist/containers/Tenant/TenantPages.tsx +1 -2
  48. package/dist/containers/Tenant/utils/paneVisibilityToggleHelpers.tsx +2 -2
  49. package/dist/services/api.d.ts +11 -0
  50. package/dist/services/api.js +3 -3
  51. package/dist/store/reducers/consumer.ts +14 -0
  52. package/dist/store/reducers/{host.js → host.ts} +9 -5
  53. package/dist/store/reducers/shardsWorkload.ts +4 -0
  54. package/dist/store/reducers/tablet.ts +111 -0
  55. package/dist/store/reducers/topic.ts +13 -0
  56. package/dist/store/state-url-mapping.js +3 -0
  57. package/dist/types/api/cluster.ts +34 -0
  58. package/dist/types/api/systemState.ts +13 -0
  59. package/dist/types/api/tablet.ts +12 -4
  60. package/dist/types/store/consumer.ts +10 -2
  61. package/dist/types/store/host.ts +23 -0
  62. package/dist/types/store/tablet.ts +50 -0
  63. package/dist/types/store/tooltip.ts +3 -1
  64. package/dist/types/store/topic.ts +3 -2
  65. package/package.json +7 -5
  66. package/dist/components/Icon/Icon.js +0 -28
  67. package/dist/components/Tablet/Tablet.js +0 -61
  68. package/dist/components/Tag/Tag.js +0 -14
  69. package/dist/components/Tags/Tags.js +0 -36
  70. package/dist/store/reducers/tablet.js +0 -94
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.4.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.4.1...v3.4.2) (2023-03-03)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **Partitions:** add search to consumers filter ([95e4462](https://github.com/ydb-platform/ydb-embedded-ui/commit/95e446295cb2b2729daf0d0ef719e37c7c8e0d3c))
9
+ * **Partitions:** fix error on wrong consumer in query string ([44269fa](https://github.com/ydb-platform/ydb-embedded-ui/commit/44269fa9240fe31c9ef69e061c20d58b2b55fae3))
10
+ * **PDisk:** display vdisks donors ([8b39b01](https://github.com/ydb-platform/ydb-embedded-ui/commit/8b39b01e8bf62624e9e12ac0a329fda5d03cc8df))
11
+
12
+ ## [3.4.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.4.0...v3.4.1) (2023-03-01)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **Consumers:** enable navigation to Partitions tab ([fa79081](https://github.com/ydb-platform/ydb-embedded-ui/commit/fa7908124bc4392e272aa829fd4e5c1639fcf209))
18
+ * **Consumers:** update topic stats values align ([f2af851](https://github.com/ydb-platform/ydb-embedded-ui/commit/f2af851208a640ef9aa392fd7176eb579a2401db))
19
+ * **TopShards:** keep state on request cancel ([1bd4f65](https://github.com/ydb-platform/ydb-embedded-ui/commit/1bd4f65dd047b42f8edf9e4bb41c722f30220d77))
20
+
3
21
  ## [3.4.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.4...v3.4.0) (2023-02-17)
4
22
 
5
23
 
@@ -1,8 +1,8 @@
1
1
  import cn from 'bem-cn-lite';
2
2
 
3
3
  import EntityStatus from '../EntityStatus/EntityStatus';
4
- import Tags from '../Tags/Tags';
5
- import Icon from '../Icon/Icon';
4
+ import {Tags} from '../Tags';
5
+ import {Icon} from '../Icon';
6
6
 
7
7
  import './BasicNodeViewer.scss';
8
8
 
@@ -47,7 +47,6 @@ export const BasicNodeViewer = ({node, additionalNodesInfo, className}: BasicNod
47
47
  ) : (
48
48
  <div className="error">no data</div>
49
49
  )}
50
-
51
50
  </div>
52
51
  );
53
52
  };
@@ -1,6 +1,7 @@
1
1
  import React, {ReactNode} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import {connect} from 'react-redux';
4
+ import {Link, Loader} from '@gravity-ui/uikit';
4
5
 
5
6
  //@ts-ignore
6
7
  import EntityStatus from '../EntityStatus/EntityStatus';
@@ -8,10 +9,8 @@ import EntityStatus from '../EntityStatus/EntityStatus';
8
9
  import ProgressViewer from '../ProgressViewer/ProgressViewer';
9
10
  //@ts-ignore
10
11
  import InfoViewer from '../InfoViewer/InfoViewer';
11
- //@ts-ignore
12
- import Tags from '../Tags/Tags';
13
- //@ts-ignore
14
- import Tablet from '../Tablet/Tablet';
12
+ import {Tags} from '../Tags';
13
+ import {Tablet} from '../Tablet';
15
14
 
16
15
  //@ts-ignore
17
16
  import {hideTooltip, showTooltip} from '../../store/reducers/tooltip';
@@ -24,15 +23,16 @@ import {clusterName, backend, customBackend} from '../../store';
24
23
  import {formatStorageValues} from '../../utils';
25
24
  //@ts-ignore
26
25
  import {TxAllocator} from '../../utils/constants';
27
-
28
- import './ClusterInfo.scss';
29
26
  import {AutoFetcher} from '../../utils/autofetcher';
30
- import {Link, Loader} from '@gravity-ui/uikit';
31
- //@ts-ignore
32
- import Icon from '../Icon/Icon';
27
+ import {Icon} from '../Icon';
33
28
  import {setHeader} from '../../store/reducers/header';
34
29
  import routes, {CLUSTER_PAGES, createHref} from '../../routes';
35
30
 
31
+ import type {TClusterInfo} from '../../types/api/cluster';
32
+ import type {TTabletStateInfo} from '../../types/api/tablet';
33
+
34
+ import './ClusterInfo.scss';
35
+
36
36
  const b = cn('cluster-info');
37
37
 
38
38
  export interface IClusterInfoItem {
@@ -40,25 +40,11 @@ export interface IClusterInfoItem {
40
40
  value: ReactNode;
41
41
  }
42
42
 
43
- interface ICluster {
44
- StorageTotal: string;
45
- StorageUsed: string;
46
- NodesAlive: number;
47
- NodesTotal: number;
48
- LoadAverage: number;
49
- NumberOfCpus: number;
50
- Versions: string[];
51
- Name?: string;
52
- Overall: string;
53
- DataCenters?: string[];
54
- SystemTablets?: ITablet[];
55
- }
56
-
57
43
  interface ClusterInfoProps {
58
44
  className?: string;
59
- cluster?: ICluster;
45
+ cluster?: TClusterInfo;
60
46
  hideTooltip: VoidFunction;
61
- showTooltip: Function;
47
+ showTooltip: (...args: Parameters<typeof showTooltip>) => void;
62
48
  setHeader: any;
63
49
  getClusterInfo: (clusterName: string) => void;
64
50
  clusterTitle?: string;
@@ -69,12 +55,8 @@ interface ClusterInfoProps {
69
55
  error?: {statusText: string};
70
56
  }
71
57
 
72
- interface ITablet {
73
- Type: string;
74
- }
75
-
76
58
  class ClusterInfo extends React.Component<ClusterInfoProps> {
77
- static compareTablets(tablet1: ITablet, tablet2: ITablet) {
59
+ static compareTablets(tablet1: TTabletStateInfo, tablet2: TTabletStateInfo) {
78
60
  if (tablet1.Type === TxAllocator) {
79
61
  return 1;
80
62
  }
@@ -128,11 +110,7 @@ class ClusterInfo extends React.Component<ClusterInfoProps> {
128
110
  private autofetcher: any;
129
111
 
130
112
  private getInfo() {
131
- const {
132
- cluster = {} as ICluster,
133
- additionalClusterInfo = [],
134
- singleClusterMode,
135
- } = this.props;
113
+ const {cluster = {}, additionalClusterInfo = [], singleClusterMode} = this.props;
136
114
  const {StorageTotal, StorageUsed} = cluster;
137
115
 
138
116
  let link = backend + '/internal';
@@ -193,7 +171,7 @@ class ClusterInfo extends React.Component<ClusterInfoProps> {
193
171
 
194
172
  private renderContent = () => {
195
173
  const {
196
- cluster = {} as ICluster,
174
+ cluster = {},
197
175
  showTooltip,
198
176
  hideTooltip,
199
177
  clusterTitle,
@@ -1,7 +1,6 @@
1
1
  import {Button, CopyToClipboard as CopyToClipboardUiKit} from '@gravity-ui/uikit';
2
2
  import createToast from '../../utils/createToast';
3
- //@ts-ignore
4
- import Icon from '../Icon/Icon';
3
+ import {Icon} from '../Icon';
5
4
 
6
5
  interface CopyToClipboardProps {
7
6
  text: string;
@@ -2,7 +2,7 @@ import {useState} from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cn from 'bem-cn-lite';
4
4
  import {Dialog} from '@gravity-ui/uikit';
5
- import Icon from '../Icon/Icon';
5
+ import {Icon} from '../Icon';
6
6
 
7
7
  import './CriticalActionDialog.scss';
8
8
 
@@ -1,6 +1,7 @@
1
- import PropTypes from 'prop-types';
1
+ import {ReactNode} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
- import Icon from '../Icon/Icon';
3
+
4
+ import {Icon} from '../Icon';
4
5
 
5
6
  import './EmptyState.scss';
6
7
 
@@ -12,7 +13,15 @@ const sizes = {
12
13
  l: 350,
13
14
  };
14
15
 
15
- export default function EmptyState({image, title, description, actions, size}) {
16
+ interface EmptyStateProps {
17
+ title: string;
18
+ image?: ReactNode;
19
+ description?: ReactNode;
20
+ actions?: ReactNode[];
21
+ size?: keyof typeof sizes;
22
+ }
23
+
24
+ export const EmptyState = ({image, title, description, actions, size = 'm'}: EmptyStateProps) => {
16
25
  return (
17
26
  <div className={block({size})}>
18
27
  <div className={block('wrapper', {size})}>
@@ -35,16 +44,4 @@ export default function EmptyState({image, title, description, actions, size}) {
35
44
  </div>
36
45
  </div>
37
46
  );
38
- }
39
-
40
- EmptyState.propTypes = {
41
- title: PropTypes.string.isRequired,
42
- image: PropTypes.node,
43
- description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
44
- actions: PropTypes.arrayOf(PropTypes.node),
45
- size: PropTypes.string,
46
- };
47
-
48
- EmptyState.defaultProps = {
49
- size: 'm',
50
47
  };
@@ -0,0 +1 @@
1
+ export * from './EmptyState';
@@ -1,7 +1,7 @@
1
1
  import {Button} from '@gravity-ui/uikit';
2
2
  import {useDispatch} from 'react-redux';
3
3
  import {enableFullscreen} from '../../store/reducers/fullscreen';
4
- import Icon from '../Icon/Icon';
4
+ import {Icon} from '../Icon';
5
5
 
6
6
  interface EnableFullscreenButtonProps {
7
7
  disabled?: boolean;
@@ -13,7 +13,12 @@ function EnableFullscreenButton({disabled}: EnableFullscreenButtonProps) {
13
13
  dispatch(enableFullscreen());
14
14
  };
15
15
  return (
16
- <Button onClick={onEnableFullscreen} view="flat-secondary" disabled={disabled} title='Fullscreen'>
16
+ <Button
17
+ onClick={onEnableFullscreen}
18
+ view="flat-secondary"
19
+ disabled={disabled}
20
+ title="Fullscreen"
21
+ >
17
22
  <Icon name="enableFullscreen" viewBox={'0 0 16 16'} height={16} width={16} />
18
23
  </Button>
19
24
  );
@@ -1,4 +1,4 @@
1
- import EmptyState from '../../EmptyState/EmptyState';
1
+ import {EmptyState} from '../../EmptyState';
2
2
  import {Illustration} from '../../Illustration';
3
3
 
4
4
  import i18n from '../i18n';
@@ -6,7 +6,7 @@ import cn from 'bem-cn-lite';
6
6
 
7
7
  import {useDispatch} from 'react-redux';
8
8
  import {disableFullscreen} from '../../store/reducers/fullscreen';
9
- import Icon from '../Icon/Icon';
9
+ import {Icon} from '../Icon';
10
10
 
11
11
  import './Fullscreen.scss';
12
12
 
@@ -0,0 +1,33 @@
1
+ import {Icon as UiKitIcon} from '@gravity-ui/uikit';
2
+
3
+ interface IconProps {
4
+ name: string;
5
+ height?: number | string;
6
+ width?: number | string;
7
+ viewBox?: string;
8
+ className?: string;
9
+ onClick?: (event: React.MouseEvent<SVGElement, MouseEvent>) => void;
10
+ }
11
+
12
+ export const Icon = ({
13
+ name,
14
+ height = 16,
15
+ width = 16,
16
+ viewBox = '0 0 16 16',
17
+ className,
18
+ onClick,
19
+ }: IconProps) => {
20
+ return (
21
+ <UiKitIcon
22
+ // @ts-expect-error
23
+ // Both url and id strings are required in this uikit component.
24
+ // However, if no url provided, component uses id, which is what we need.
25
+ // If it is fixed in uikit it could be safely removed
26
+ data={{id: `icon.${name}`, viewBox}}
27
+ height={height}
28
+ width={width}
29
+ className={className}
30
+ onClick={onClick}
31
+ />
32
+ );
33
+ };
@@ -0,0 +1 @@
1
+ export * from './Icon';
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cn from 'bem-cn-lite';
4
4
  import {Button} from '@gravity-ui/uikit';
5
- import Icon from '../Icon/Icon';
5
+ import {Icon} from '../Icon';
6
6
  import Hotkey from '../Hotkey/Hotkey';
7
7
 
8
8
  import './Pagination.scss';
@@ -4,7 +4,7 @@ import cn from 'bem-cn-lite';
4
4
 
5
5
  import {Icon as UiKitIcon} from '@gravity-ui/uikit';
6
6
 
7
- import Icon from '../Icon/Icon';
7
+ import {Icon} from '../Icon';
8
8
 
9
9
  import questionIcon from '../../assets/icons/question.svg';
10
10
 
@@ -0,0 +1,59 @@
1
+ import {useRef} from 'react';
2
+ import cn from 'bem-cn-lite';
3
+
4
+ import type {TTabletStateInfo} from '../../types/api/tablet';
5
+ import type {ShowTooltipFunction} from '../../types/store/tooltip';
6
+ import {getTabletLabel} from '../../utils/constants';
7
+ import routes, {createHref} from '../../routes';
8
+
9
+ import {InternalLink} from '../InternalLink';
10
+
11
+ import './Tablet.scss';
12
+
13
+ const b = cn('tablet');
14
+
15
+ interface TabletProps {
16
+ tablet?: TTabletStateInfo;
17
+ onMouseEnter?: (...args: Parameters<ShowTooltipFunction>) => void;
18
+ onMouseLeave?: VoidFunction;
19
+ }
20
+
21
+ export const Tablet = ({
22
+ tablet = {},
23
+ onMouseEnter = () => {},
24
+ onMouseLeave = () => {},
25
+ }: TabletProps) => {
26
+ const ref = useRef(null);
27
+
28
+ const _onTabletMouseEnter = () => {
29
+ onMouseEnter(ref.current, tablet, 'tablet');
30
+ };
31
+
32
+ const _onTabletClick = () => {
33
+ const {TabletId: id} = tablet;
34
+
35
+ if (id) {
36
+ onMouseLeave();
37
+ }
38
+ };
39
+
40
+ const {TabletId: id} = tablet;
41
+ const status = tablet.Overall?.toLowerCase();
42
+
43
+ return (
44
+ <InternalLink
45
+ onClick={_onTabletClick}
46
+ to={id && createHref(routes.tablet, {id})}
47
+ className={b('wrapper')}
48
+ >
49
+ <div
50
+ ref={ref}
51
+ className={b({status})}
52
+ onMouseEnter={_onTabletMouseEnter}
53
+ onMouseLeave={onMouseLeave}
54
+ >
55
+ <div className={b('type')}>{[getTabletLabel(tablet.Type)]}</div>
56
+ </div>
57
+ </InternalLink>
58
+ );
59
+ };
@@ -0,0 +1 @@
1
+ export * from './Tablet';
@@ -0,0 +1,16 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import './Tag.scss';
4
+
5
+ const b = cn('tag');
6
+
7
+ export type TagType = 'blue';
8
+
9
+ interface TagProps {
10
+ text: string;
11
+ type?: TagType;
12
+ }
13
+
14
+ export const Tag = ({text, type}: TagProps) => {
15
+ return <div className={b({type})}>{text}</div>;
16
+ };
@@ -0,0 +1 @@
1
+ export * from './Tag';
@@ -0,0 +1,22 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import {Tag, TagType} from '../Tag';
4
+
5
+ import './Tags.scss';
6
+
7
+ const b = cn('tags');
8
+
9
+ interface TagsProps {
10
+ tags: string[];
11
+ tagsType?: TagType;
12
+ className?: string;
13
+ }
14
+
15
+ export const Tags = ({tags, tagsType, className = ''}: TagsProps) => {
16
+ return (
17
+ <div className={b(null, className)}>
18
+ {tags &&
19
+ tags.map((tag, tagIndex) => <Tag text={tag} key={tagIndex} type={tagsType}></Tag>)}
20
+ </div>
21
+ );
22
+ };
@@ -0,0 +1 @@
1
+ export * from './Tags';
@@ -5,8 +5,7 @@ import {useHistory, useLocation} from 'react-router';
5
5
  import {Breadcrumbs, BreadcrumbsItem, Link} from '@gravity-ui/uikit';
6
6
 
7
7
  import Divider from '../../components/Divider/Divider';
8
- //@ts-ignore
9
- import Icon from '../../components/Icon/Icon';
8
+ import {Icon} from '../../components/Icon';
10
9
 
11
10
  import {clusterName as clusterNameLocation, backend, customBackend} from '../../store';
12
11
  import {getClusterInfo} from '../../store/reducers/cluster';
@@ -27,7 +26,7 @@ function ClusterName({name}: {name: string}) {
27
26
  }
28
27
 
29
28
  interface HeaderProps {
30
- clusterName: string
29
+ clusterName: string;
31
30
  }
32
31
 
33
32
  function Header({clusterName}: HeaderProps) {
@@ -5,7 +5,7 @@ import cn from 'bem-cn-lite';
5
5
  import {Link as ExternalLink} from '@gravity-ui/uikit';
6
6
 
7
7
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
8
- import {Tag} from '../../../components/Tag/Tag';
8
+ import {Tag} from '../../../components/Tag';
9
9
 
10
10
  import {calcUptime} from '../../../utils';
11
11
  import {customBackend} from '../../../store';
@@ -9,7 +9,7 @@ import DataTable, {Column, Settings} from '@gravity-ui/react-data-table';
9
9
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
10
10
  import InfoViewer from '../../../components/InfoViewer/InfoViewer';
11
11
  import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
12
- import Icon from '../../../components/Icon/Icon';
12
+ import {Icon} from '../../../components/Icon';
13
13
  import {Vdisk} from './Vdisk';
14
14
 
15
15
  import {bytesToGB, pad9} from '../../../utils/utils';
@@ -2,7 +2,7 @@ import cn from 'bem-cn-lite';
2
2
  import DataTable, {Column} from '@gravity-ui/react-data-table';
3
3
  import {Button, Popover} from '@gravity-ui/uikit';
4
4
 
5
- import Icon from '../../components/Icon/Icon';
5
+ import {Icon} from '../../components/Icon';
6
6
  import EntityStatus from '../../components/EntityStatus/EntityStatus';
7
7
  import PoolsGraph from '../../components/PoolsGraph/PoolsGraph';
8
8
  import ProgressViewer from '../../components/ProgressViewer/ProgressViewer';
@@ -1,6 +1,6 @@
1
1
  import {Button} from '@gravity-ui/uikit';
2
2
 
3
- import EmptyState from '../../../components/EmptyState/EmptyState';
3
+ import {EmptyState} from '../../../components/EmptyState';
4
4
  import {Illustration} from '../../../components/Illustration';
5
5
 
6
6
  import i18n from './i18n';
@@ -22,13 +22,12 @@ export const EmptyFilter = ({
22
22
  image={<Illustration name="thumbsUp" />}
23
23
  title={title}
24
24
  description={message}
25
- actions={onShowAll && [
26
- <Button
27
- key="show-all"
28
- onClick={onShowAll}
29
- >
30
- {showAll}
31
- </Button>
32
- ]}
25
+ actions={
26
+ onShowAll && [
27
+ <Button key="show-all" onClick={onShowAll}>
28
+ {showAll}
29
+ </Button>,
30
+ ]
31
+ }
33
32
  />
34
33
  );
@@ -21,6 +21,21 @@
21
21
  &__vdisks-item {
22
22
  flex-basis: 5px;
23
23
  flex-shrink: 0;
24
+
25
+ .stack__layer {
26
+ background: var(--yc-color-base-background);
27
+
28
+ .data-table__row:hover & {
29
+ background: var(--ydb-data-table-color-hover);
30
+ }
31
+ }
32
+ }
33
+
34
+ &__donors-stack {
35
+ --ydb-stack-offset-x: 0px;
36
+ --ydb-stack-offset-y: -2px;
37
+ --ydb-stack-offset-x-hover: 0px;
38
+ --ydb-stack-offset-y-hover: -7px;
24
39
  }
25
40
 
26
41
  &__media-type {
@@ -2,6 +2,7 @@ import React, {useEffect, useState, useRef, useMemo} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
 
4
4
  import {InternalLink} from '../../../components/InternalLink';
5
+ import {Stack} from '../../../components/Stack/Stack';
5
6
 
6
7
  import routes, {createHref} from '../../../routes';
7
8
  import {getVDisksForPDisk} from '../../../store/reducers/storage';
@@ -10,6 +11,7 @@ import {TVDiskStateInfo} from '../../../types/api/vdisk';
10
11
  import {stringifyVdiskId} from '../../../utils';
11
12
  import {useTypedSelector} from '../../../utils/hooks';
12
13
  import {getPDiskType} from '../../../utils/pdisk';
14
+ import {isFullVDiksData} from '../../../utils/storage';
13
15
 
14
16
  import {STRUCTURE} from '../../Node/NodePages';
15
17
 
@@ -109,19 +111,42 @@ export const PDisk = ({nodeId, data: rawData = {}}: PDiskProps) => {
109
111
 
110
112
  return (
111
113
  <div className={b('vdisks')}>
112
- {vdisks.map((vdisk) => (
113
- <div
114
- key={stringifyVdiskId(vdisk.VDiskId)}
115
- className={b('vdisks-item')}
116
- style={{
117
- // 1 is small enough for empty disks to be of the minimum width
118
- // but if all of them are empty, `flex-grow: 1` would size them evenly
119
- flexGrow: Number(vdisk.AllocatedSize) || 1,
120
- }}
121
- >
122
- <VDisk data={vdisk} compact />
123
- </div>
124
- ))}
114
+ {vdisks.map((vdisk) => {
115
+ const donors = vdisk.Donors;
116
+
117
+ return (
118
+ <div
119
+ key={stringifyVdiskId(vdisk.VDiskId)}
120
+ className={b('vdisks-item')}
121
+ style={{
122
+ // 1 is small enough for empty disks to be of the minimum width
123
+ // but if all of them are empty, `flex-grow: 1` would size them evenly
124
+ flexGrow: Number(vdisk.AllocatedSize) || 1,
125
+ }}
126
+ >
127
+ {donors && donors.length ? (
128
+ <Stack className={b('donors-stack')} key={stringifyVdiskId(vdisk.VDiskId)}>
129
+ <VDisk data={vdisk} compact />
130
+ {donors.map((donor) => {
131
+ const isFullData = isFullVDiksData(donor);
132
+
133
+ return (
134
+ <VDisk
135
+ compact
136
+ data={isFullData ? donor : {...donor, DonorMode: true}}
137
+ key={stringifyVdiskId(
138
+ isFullData ? donor.VDiskId : donor,
139
+ )}
140
+ />
141
+ );
142
+ })}
143
+ </Stack>
144
+ ) : (
145
+ <VDisk data={vdisk} compact />
146
+ )}
147
+ </div>
148
+ );
149
+ })}
125
150
  </div>
126
151
  );
127
152
  };
@@ -1,8 +1,10 @@
1
1
  .global-storage-nodes {
2
+ &__pdisks-column {
3
+ overflow: visible; // to enable stacked disks overflow the row
4
+ }
5
+
2
6
  &__pdisks-wrapper {
3
7
  display: flex;
4
- overflow-x: auto;
5
- overflow-y: hidden;
6
8
  justify-content: left;
7
9
  align-items: flex-end;
8
10
 
@@ -128,6 +128,7 @@ function StorageNodes({
128
128
  },
129
129
  {
130
130
  name: TableColumnsIds.PDisks,
131
+ className: b('pdisks-column'),
131
132
  header: tableColumnsNames[TableColumnsIds.PDisks],
132
133
  render: ({value, row}) => (
133
134
  <div className={b('pdisks-wrapper')}>
@@ -12,9 +12,9 @@ import '../../services/api';
12
12
 
13
13
  import InfoViewer from '../../components/InfoViewer/InfoViewer';
14
14
  import EntityStatus from '../../components/EntityStatus/EntityStatus';
15
- import {Tag} from '../../components/Tag/Tag';
16
- import Icon from '../../components/Icon/Icon';
17
- import EmptyState from '../../components/EmptyState/EmptyState';
15
+ import {Tag} from '../../components/Tag';
16
+ import {Icon} from '../../components/Icon';
17
+ import {EmptyState} from '../../components/EmptyState';
18
18
  import {Link as ExternalLink, Button, Loader} from '@gravity-ui/uikit';
19
19
  import DataTable from '@gravity-ui/react-data-table';
20
20
  import CriticalActionDialog from '../../components/CriticalActionDialog/CriticalActionDialog';
@@ -111,9 +111,9 @@ class Tablet extends React.Component {
111
111
  const {isFirstFetchData} = this.state;
112
112
 
113
113
  if (version && this.isValidVersion()) {
114
- this.props.getTablet(id).then(({tablet}) => {
115
- if (isFirstFetchData && tablet.TenantId) {
116
- this.props.getTabletDescribe(tablet.TenantId);
114
+ this.props.getTablet(id).then(({tabletData}) => {
115
+ if (isFirstFetchData && tabletData.TenantId) {
116
+ this.props.getTabletDescribe(tabletData.TenantId);
117
117
  }
118
118
 
119
119
  this.setState({isFirstFetchData: false});