ydb-embedded-ui 1.0.4 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +38 -0
- package/dist/assets/icons/question.svg +1 -0
- package/dist/components/AsideNavigation/AsideHeader.tsx +2 -1
- package/dist/components/ClusterInfo/ClusterInfo.tsx +8 -4
- package/dist/components/FullNodeViewer/FullNodeViewer.scss +4 -9
- package/dist/components/InfoViewer/InfoViewer.scss +3 -2
- package/dist/components/InternalLink/InternalLink.js +8 -0
- package/dist/components/Loader/Loader.scss +5 -0
- package/dist/components/Loader/Loader.tsx +16 -0
- package/dist/components/PDiskViewer/PDiskViewer.js +3 -4
- package/dist/containers/App/App.scss +4 -0
- package/dist/containers/App/Content.js +0 -2
- package/dist/containers/AppIcons/AppIcons.js +4 -0
- package/dist/containers/Authentication/Authentication.tsx +2 -2
- package/dist/containers/Cluster/Cluster.tsx +1 -1
- package/dist/containers/Header/Header.tsx +6 -1
- package/dist/containers/Heatmap/Heatmap.js +0 -1
- package/dist/containers/Node/Node.scss +12 -1
- package/dist/containers/Node/Node.tsx +174 -0
- package/dist/containers/Node/NodeOverview/NodeOverview.scss +0 -0
- package/dist/containers/Node/NodeOverview/NodeOverview.tsx +23 -0
- package/dist/containers/Node/NodePages.js +16 -0
- package/dist/containers/Node/NodeStructure/NodeStructure.scss +151 -0
- package/dist/containers/Node/NodeStructure/NodeStructure.tsx +155 -0
- package/dist/containers/Node/NodeStructure/Pdisk.tsx +299 -0
- package/dist/containers/Node/NodeStructure/Vdisk.tsx +153 -0
- package/dist/containers/Pdisk/Pdisk.js +2 -5
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +10 -3
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +20 -15
- package/dist/containers/Storage/Pdisk/Pdisk.scss +1 -0
- package/dist/containers/Storage/Pdisk/Pdisk.tsx +7 -5
- package/dist/containers/Storage/Storage.js +12 -9
- package/dist/containers/Storage/StorageGroups/StorageGroups.scss +2 -1
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +2 -2
- package/dist/containers/Storage/StorageNodes/StorageNodes.scss +3 -2
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +2 -2
- package/dist/containers/Storage/Vdisk/Vdisk.js +7 -6
- package/dist/containers/Storage/Vdisk/Vdisk.scss +1 -0
- package/dist/containers/Tablet/Tablet.js +2 -7
- package/dist/containers/Tablets/Tablets.js +4 -12
- package/dist/containers/Tenant/Acl/Acl.js +0 -3
- package/dist/containers/Tenant/Diagnostics/Compute/Compute.js +1 -3
- package/dist/containers/Tenant/Diagnostics/Network/Network.js +3 -6
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +2 -2
- package/dist/containers/Tenant/QueryEditor/QueryEditor.js +24 -24
- package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +4 -0
- package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +4 -1
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +1 -0
- package/dist/containers/Vdisk/Vdisk.js +2 -4
- package/dist/containers/VdiskPdiskNode/VdiskPdiskNode.js +4 -6
- package/dist/services/api.js +0 -1
- package/dist/store/reducers/executeQuery.js +1 -1
- package/dist/store/reducers/header.ts +1 -1
- package/dist/store/reducers/node.js +98 -3
- package/dist/store/reducers/nodes.js +0 -3
- package/dist/store/reducers/storage.js +8 -2
- package/dist/store/reducers/tablets.js +0 -3
- package/dist/utils/constants.js +0 -6
- package/dist/utils/getNodesColumns.js +2 -9
- package/dist/utils/utils.js +10 -1
- package/package.json +43 -29
- package/dist/containers/Node/Node.js +0 -184
@@ -0,0 +1,151 @@
|
|
1
|
+
@import '../../../styles/mixins.scss';
|
2
|
+
|
3
|
+
.kv-node-structure {
|
4
|
+
position: relative;
|
5
|
+
|
6
|
+
display: flex;
|
7
|
+
overflow: auto;
|
8
|
+
flex-direction: column;
|
9
|
+
flex-shrink: 0;
|
10
|
+
@include flex-container();
|
11
|
+
@include body2-typography();
|
12
|
+
|
13
|
+
&__loader {
|
14
|
+
display: flex;
|
15
|
+
flex-grow: 1;
|
16
|
+
justify-content: center;
|
17
|
+
}
|
18
|
+
|
19
|
+
&__pdisk {
|
20
|
+
display: flex;
|
21
|
+
flex-direction: column;
|
22
|
+
|
23
|
+
width: 573px;
|
24
|
+
margin-bottom: 8px;
|
25
|
+
padding-left: 20px;
|
26
|
+
|
27
|
+
border: 1px solid var(--yc-color-line-generic);
|
28
|
+
border-radius: 5px;
|
29
|
+
}
|
30
|
+
|
31
|
+
&__pdisk-id {
|
32
|
+
display: flex;
|
33
|
+
align-items: flex-end;
|
34
|
+
}
|
35
|
+
|
36
|
+
&__pdisk-header {
|
37
|
+
display: flex;
|
38
|
+
justify-content: space-between;
|
39
|
+
align-items: center;
|
40
|
+
|
41
|
+
height: 48px;
|
42
|
+
}
|
43
|
+
|
44
|
+
&__pdisk-title-wrapper {
|
45
|
+
display: flex;
|
46
|
+
align-items: center;
|
47
|
+
gap: 8px;
|
48
|
+
|
49
|
+
font-weight: 600;
|
50
|
+
}
|
51
|
+
|
52
|
+
&__pdisk-details {
|
53
|
+
margin-bottom: 20px;
|
54
|
+
}
|
55
|
+
|
56
|
+
&__link {
|
57
|
+
text-decoration: none;
|
58
|
+
|
59
|
+
color: var(--yc-color-base-special);
|
60
|
+
}
|
61
|
+
|
62
|
+
&__vdisks-header {
|
63
|
+
font-weight: 600;
|
64
|
+
}
|
65
|
+
|
66
|
+
&__vdisks-container {
|
67
|
+
margin-bottom: 42px;
|
68
|
+
}
|
69
|
+
|
70
|
+
&__vdisk-details {
|
71
|
+
overflow: auto;
|
72
|
+
|
73
|
+
min-width: 200px;
|
74
|
+
max-height: 90vh;
|
75
|
+
|
76
|
+
.vdisk-pdisk-node__column {
|
77
|
+
margin-bottom: 0;
|
78
|
+
}
|
79
|
+
.vdisk-pdisk-node__section {
|
80
|
+
padding-bottom: 0;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
&__vdisk-id {
|
85
|
+
display: flex;
|
86
|
+
align-items: center;
|
87
|
+
|
88
|
+
&_selected {
|
89
|
+
color: var(--yc-color-text-info);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
&__vdisk-details-button {
|
94
|
+
&_selected {
|
95
|
+
color: var(--yc-color-text-info);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
&__external-button {
|
100
|
+
display: inline-flex;
|
101
|
+
align-items: center;
|
102
|
+
|
103
|
+
margin-left: 4px;
|
104
|
+
|
105
|
+
transform: translateY(-1px);
|
106
|
+
|
107
|
+
.yc-button__text {
|
108
|
+
margin: 0 4px;
|
109
|
+
}
|
110
|
+
|
111
|
+
&_hidden {
|
112
|
+
visibility: hidden;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
.data-table__row:hover {
|
117
|
+
.kv-node-structure__external-button_hidden {
|
118
|
+
visibility: visible;
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
&__selected-vdisk {
|
123
|
+
animation: onSelectedVdiskAnimation 4s;
|
124
|
+
}
|
125
|
+
|
126
|
+
&__row {
|
127
|
+
display: flex;
|
128
|
+
}
|
129
|
+
|
130
|
+
&__column {
|
131
|
+
display: flex;
|
132
|
+
flex-direction: column;
|
133
|
+
|
134
|
+
margin-bottom: 15px;
|
135
|
+
}
|
136
|
+
|
137
|
+
&__title {
|
138
|
+
margin-right: 16px;
|
139
|
+
|
140
|
+
font-size: var(--yc-text-body2-font-size);
|
141
|
+
font-weight: 500;
|
142
|
+
line-height: var(--yc-text-body2-line-height);
|
143
|
+
text-transform: uppercase;
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
@keyframes onSelectedVdiskAnimation {
|
148
|
+
0% {
|
149
|
+
background-color: var(--yc-color-base-info-hover);
|
150
|
+
}
|
151
|
+
}
|
@@ -0,0 +1,155 @@
|
|
1
|
+
import {useEffect, useRef, useMemo} from 'react';
|
2
|
+
import {useDispatch, useSelector} from 'react-redux';
|
3
|
+
import url from 'url';
|
4
|
+
import _ from 'lodash';
|
5
|
+
|
6
|
+
import cn from 'bem-cn-lite';
|
7
|
+
|
8
|
+
import {PDisk} from './Pdisk';
|
9
|
+
import Loader from '../.././../components/Loader/Loader';
|
10
|
+
|
11
|
+
import {getNodeStructure, selectNodeStructure} from '../../../store/reducers/node';
|
12
|
+
|
13
|
+
import {AutoFetcher} from '../../../utils/autofetcher';
|
14
|
+
|
15
|
+
import './NodeStructure.scss';
|
16
|
+
|
17
|
+
const b = cn('kv-node-structure');
|
18
|
+
|
19
|
+
export function valueIsDefined(value: any) {
|
20
|
+
return value !== null && value !== undefined;
|
21
|
+
}
|
22
|
+
|
23
|
+
function generateId({type, id}: {type: 'pdisk' | 'vdisk'; id: string}) {
|
24
|
+
return `${type}-${id}`;
|
25
|
+
}
|
26
|
+
|
27
|
+
interface NodeStructureProps {
|
28
|
+
nodeId: string;
|
29
|
+
className?: string;
|
30
|
+
additionalNodesInfo?: any;
|
31
|
+
}
|
32
|
+
|
33
|
+
const autofetcher = new AutoFetcher();
|
34
|
+
|
35
|
+
function NodeStructure(props: NodeStructureProps) {
|
36
|
+
const dispatch = useDispatch();
|
37
|
+
|
38
|
+
const nodeStructure: any = useSelector(selectNodeStructure);
|
39
|
+
|
40
|
+
const loadingStructure = useSelector((state: any) => state.node.loadingStructure);
|
41
|
+
const wasLoadedStructure = useSelector((state: any) => state.node.wasLoadedStructure);
|
42
|
+
const nodeData = useSelector((state: any) => state.node?.data?.SystemStateInfo?.[0]);
|
43
|
+
|
44
|
+
const nodeHref = useMemo(() => {
|
45
|
+
return props.additionalNodesInfo?.getNodeRef
|
46
|
+
? props.additionalNodesInfo.getNodeRef(nodeData)
|
47
|
+
: undefined;
|
48
|
+
}, [nodeData, props.additionalNodesInfo]);
|
49
|
+
|
50
|
+
const {pdiskId: pdiskIdFromUrl, vdiskId: vdiskIdFromUrl} = url.parse(
|
51
|
+
window.location.href,
|
52
|
+
true,
|
53
|
+
).query;
|
54
|
+
|
55
|
+
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
56
|
+
const scrollContainer = scrollContainerRef.current;
|
57
|
+
|
58
|
+
const isReady = useRef(false);
|
59
|
+
|
60
|
+
const scrolled = useRef(false);
|
61
|
+
|
62
|
+
useEffect(() => {
|
63
|
+
return () => {
|
64
|
+
if (scrollContainer) {
|
65
|
+
scrollContainer.scrollTo({
|
66
|
+
behavior: 'smooth',
|
67
|
+
top: 0,
|
68
|
+
});
|
69
|
+
}
|
70
|
+
};
|
71
|
+
}, []);
|
72
|
+
|
73
|
+
useEffect(() => {
|
74
|
+
dispatch(getNodeStructure(props.nodeId));
|
75
|
+
autofetcher.start();
|
76
|
+
autofetcher.fetch(() => dispatch(getNodeStructure(props.nodeId)));
|
77
|
+
|
78
|
+
return () => {
|
79
|
+
scrolled.current = false;
|
80
|
+
isReady.current = false;
|
81
|
+
autofetcher.stop();
|
82
|
+
};
|
83
|
+
}, [props.nodeId, dispatch]);
|
84
|
+
|
85
|
+
useEffect(() => {
|
86
|
+
if (!_.isEmpty(nodeStructure) && scrollContainer) {
|
87
|
+
isReady.current = true;
|
88
|
+
}
|
89
|
+
}, [nodeStructure]);
|
90
|
+
|
91
|
+
useEffect(() => {
|
92
|
+
if (isReady.current && !scrolled.current && scrollContainer) {
|
93
|
+
const element = document.getElementById(
|
94
|
+
generateId({type: 'pdisk', id: pdiskIdFromUrl as string}),
|
95
|
+
);
|
96
|
+
|
97
|
+
let scrollToVdisk = 0;
|
98
|
+
|
99
|
+
if (vdiskIdFromUrl) {
|
100
|
+
const vDisks = nodeStructure[pdiskIdFromUrl as string]?.vDisks;
|
101
|
+
const vDisk = vDisks?.find((el: any) => el.id === vdiskIdFromUrl);
|
102
|
+
const dataTable = vDisk ? document.querySelector('.data-table') : undefined;
|
103
|
+
const order = vDisk?.order;
|
104
|
+
|
105
|
+
if (dataTable) {
|
106
|
+
scrollToVdisk += (dataTable as HTMLElement).offsetTop + 40 * order;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
if (element) {
|
111
|
+
scrollContainer.scrollTo({
|
112
|
+
behavior: 'smooth',
|
113
|
+
// should subtract 20 to avoid sticking the element to tabs
|
114
|
+
top: scrollToVdisk ? scrollToVdisk : element.offsetTop,
|
115
|
+
});
|
116
|
+
scrolled.current = true;
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}, [nodeStructure, pdiskIdFromUrl, vdiskIdFromUrl]);
|
120
|
+
|
121
|
+
const renderStub = () => {
|
122
|
+
return 'There is no information about node structure.';
|
123
|
+
};
|
124
|
+
|
125
|
+
const renderStructure = () => {
|
126
|
+
const pDisksIds = Object.keys(nodeStructure);
|
127
|
+
return pDisksIds.length > 0
|
128
|
+
? pDisksIds.map((pDiskId) => (
|
129
|
+
<PDisk
|
130
|
+
data={nodeStructure[pDiskId]}
|
131
|
+
key={pDiskId}
|
132
|
+
id={generateId({type: 'pdisk', id: pDiskId})}
|
133
|
+
unfolded={pdiskIdFromUrl === pDiskId}
|
134
|
+
selectedVdiskId={vdiskIdFromUrl as string}
|
135
|
+
nodeHref={nodeHref}
|
136
|
+
/>
|
137
|
+
))
|
138
|
+
: renderStub();
|
139
|
+
};
|
140
|
+
|
141
|
+
const renderContent = () => {
|
142
|
+
if (loadingStructure && !wasLoadedStructure) {
|
143
|
+
return <Loader size="m" />;
|
144
|
+
}
|
145
|
+
return renderStructure();
|
146
|
+
};
|
147
|
+
|
148
|
+
return (
|
149
|
+
<div className={b()} ref={scrollContainerRef}>
|
150
|
+
<div className={props.className}>{renderContent()}</div>
|
151
|
+
</div>
|
152
|
+
);
|
153
|
+
}
|
154
|
+
|
155
|
+
export default NodeStructure;
|
@@ -0,0 +1,299 @@
|
|
1
|
+
import {useState} from 'react';
|
2
|
+
import cn from 'bem-cn-lite';
|
3
|
+
import _ from 'lodash';
|
4
|
+
|
5
|
+
import {ArrowToggle, Button, Tooltip} from '@yandex-cloud/uikit';
|
6
|
+
|
7
|
+
import DataTable, {Column, Settings} from '@yandex-cloud/react-data-table';
|
8
|
+
|
9
|
+
import EntityStatus from '../../../components/EntityStatus/EntityStatus';
|
10
|
+
import InfoViewer from '../../../components/InfoViewer/InfoViewer';
|
11
|
+
import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
|
12
|
+
import Icon from '../../../components/Icon/Icon';
|
13
|
+
import {Vdisk} from './Vdisk';
|
14
|
+
|
15
|
+
import {bytesToGB, pad9} from '../../../utils/utils';
|
16
|
+
import {formatStorageValuesToGb} from '../../../utils';
|
17
|
+
|
18
|
+
import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
|
19
|
+
import {valueIsDefined} from './NodeStructure';
|
20
|
+
|
21
|
+
const b = cn('kv-node-structure');
|
22
|
+
|
23
|
+
interface PDiskProps {
|
24
|
+
data: Record<string, any>;
|
25
|
+
unfolded?: boolean;
|
26
|
+
id: string;
|
27
|
+
selectedVdiskId?: string;
|
28
|
+
nodeHref?: string;
|
29
|
+
}
|
30
|
+
|
31
|
+
enum VDiskTableColumnsIds {
|
32
|
+
slotId = 'VDiskSlotId',
|
33
|
+
VDiskState = 'VDiskState',
|
34
|
+
Size = 'Size',
|
35
|
+
Info = 'Info',
|
36
|
+
}
|
37
|
+
|
38
|
+
type VDiskTableColumnsIdsKeys = keyof typeof VDiskTableColumnsIds;
|
39
|
+
type VDiskTableColumnsIdsValues = typeof VDiskTableColumnsIds[VDiskTableColumnsIdsKeys];
|
40
|
+
|
41
|
+
const vDiskTableColumnsNames: Record<VDiskTableColumnsIdsValues, string> = {
|
42
|
+
VDiskSlotId: 'Slot id',
|
43
|
+
VDiskState: 'Status',
|
44
|
+
Size: 'Size',
|
45
|
+
Info: '',
|
46
|
+
};
|
47
|
+
|
48
|
+
interface RowType {
|
49
|
+
id: string;
|
50
|
+
[VDiskTableColumnsIds.slotId]: number;
|
51
|
+
[VDiskTableColumnsIds.VDiskState]: string;
|
52
|
+
AllocatedSize: string;
|
53
|
+
AvailableSize: string;
|
54
|
+
}
|
55
|
+
|
56
|
+
function getColumns({
|
57
|
+
pDiskId,
|
58
|
+
selectedVdiskId,
|
59
|
+
nodeHref,
|
60
|
+
}: {
|
61
|
+
pDiskId: number;
|
62
|
+
selectedVdiskId?: string;
|
63
|
+
nodeHref?: string;
|
64
|
+
}) {
|
65
|
+
const columns: Column<RowType>[] = [
|
66
|
+
{
|
67
|
+
name: VDiskTableColumnsIds.slotId as string,
|
68
|
+
header: vDiskTableColumnsNames[VDiskTableColumnsIds.slotId],
|
69
|
+
width: 100,
|
70
|
+
render: ({value, row}) => {
|
71
|
+
let vdiskInternalViewerLink: string | undefined;
|
72
|
+
|
73
|
+
if (nodeHref && value !== undefined) {
|
74
|
+
vdiskInternalViewerLink +=
|
75
|
+
nodeHref + '/actors/vdisks/vdisk' + pad9(pDiskId) + '_' + pad9(value);
|
76
|
+
}
|
77
|
+
|
78
|
+
return (
|
79
|
+
<div className={b('vdisk-id', {selected: row.id === selectedVdiskId})}>
|
80
|
+
<span>{value as number}</span>
|
81
|
+
{vdiskInternalViewerLink && (
|
82
|
+
<Button
|
83
|
+
size="s"
|
84
|
+
className={b('external-button', {hidden: true})}
|
85
|
+
href={vdiskInternalViewerLink}
|
86
|
+
target="_blank"
|
87
|
+
>
|
88
|
+
<Icon name="external" />
|
89
|
+
</Button>
|
90
|
+
)}
|
91
|
+
</div>
|
92
|
+
);
|
93
|
+
},
|
94
|
+
align: DataTable.LEFT,
|
95
|
+
},
|
96
|
+
{
|
97
|
+
name: VDiskTableColumnsIds.VDiskState as string,
|
98
|
+
header: vDiskTableColumnsNames[VDiskTableColumnsIds.VDiskState],
|
99
|
+
width: 70,
|
100
|
+
render: ({value}) => {
|
101
|
+
return <EntityStatus status={value === 'OK' ? 'green' : 'red'} />;
|
102
|
+
},
|
103
|
+
sortAccessor: (row) => (row[VDiskTableColumnsIds.VDiskState] === 'OK' ? 1 : 0),
|
104
|
+
align: DataTable.CENTER,
|
105
|
+
},
|
106
|
+
{
|
107
|
+
name: VDiskTableColumnsIds.Size as string,
|
108
|
+
header: vDiskTableColumnsNames[VDiskTableColumnsIds.Size],
|
109
|
+
width: 100,
|
110
|
+
render: ({row}) => {
|
111
|
+
return (
|
112
|
+
<ProgressViewer
|
113
|
+
value={row.AllocatedSize}
|
114
|
+
capacity={Number(row.AllocatedSize) + Number(row.AvailableSize)}
|
115
|
+
formatValues={formatStorageValuesToGb}
|
116
|
+
colorizeProgress={true}
|
117
|
+
/>
|
118
|
+
);
|
119
|
+
},
|
120
|
+
sortAccessor: (row) => Number(row.AllocatedSize),
|
121
|
+
align: DataTable.CENTER,
|
122
|
+
},
|
123
|
+
{
|
124
|
+
name: VDiskTableColumnsIds.Info as string,
|
125
|
+
header: vDiskTableColumnsNames[VDiskTableColumnsIds.Info],
|
126
|
+
width: 70,
|
127
|
+
render: ({row}) => {
|
128
|
+
return (
|
129
|
+
<Tooltip
|
130
|
+
placement={['right']}
|
131
|
+
content={<Vdisk {...row} />}
|
132
|
+
contentClassName={b('vdisk-details')}
|
133
|
+
>
|
134
|
+
<Button
|
135
|
+
view="clear"
|
136
|
+
className={b('vdisk-details-button', {
|
137
|
+
selected: row.id === selectedVdiskId,
|
138
|
+
})}
|
139
|
+
>
|
140
|
+
<Icon name="information" viewBox="0 0 512 512" height={16} width={16} />
|
141
|
+
</Button>
|
142
|
+
</Tooltip>
|
143
|
+
);
|
144
|
+
},
|
145
|
+
sortable: false,
|
146
|
+
},
|
147
|
+
];
|
148
|
+
return columns;
|
149
|
+
}
|
150
|
+
|
151
|
+
export function PDisk(props: PDiskProps) {
|
152
|
+
const [unfolded, setUnfolded] = useState(props.unfolded ?? false);
|
153
|
+
|
154
|
+
const data = props.data ?? {};
|
155
|
+
|
156
|
+
const onOpenPDiskDetails = () => {
|
157
|
+
setUnfolded(true);
|
158
|
+
};
|
159
|
+
const onClosePDiskDetails = () => {
|
160
|
+
setUnfolded(false);
|
161
|
+
};
|
162
|
+
|
163
|
+
const renderVDisks = () => {
|
164
|
+
const {selectedVdiskId, data, nodeHref} = props;
|
165
|
+
const {vDisks} = data;
|
166
|
+
|
167
|
+
return (
|
168
|
+
<DataTable
|
169
|
+
theme="yandex-cloud"
|
170
|
+
data={vDisks}
|
171
|
+
columns={getColumns({nodeHref, pDiskId: data.PDiskId, selectedVdiskId})}
|
172
|
+
settings={{...DEFAULT_TABLE_SETTINGS, dynamicRender: false} as Settings}
|
173
|
+
rowClassName={(row) => {
|
174
|
+
return row.id === selectedVdiskId ? b('selected-vdisk') : '';
|
175
|
+
}}
|
176
|
+
/>
|
177
|
+
);
|
178
|
+
};
|
179
|
+
|
180
|
+
const renderPDiskDetails = () => {
|
181
|
+
if (_.isEmpty(data)) {
|
182
|
+
return <div>No information about PDisk</div>;
|
183
|
+
}
|
184
|
+
const {nodeHref} = props;
|
185
|
+
const {
|
186
|
+
TotalSize,
|
187
|
+
AvailableSize,
|
188
|
+
Device,
|
189
|
+
Guid,
|
190
|
+
PDiskId,
|
191
|
+
Path,
|
192
|
+
Realtime,
|
193
|
+
State,
|
194
|
+
Category,
|
195
|
+
SerialNumber,
|
196
|
+
} = data;
|
197
|
+
|
198
|
+
let pDiskInternalViewerLink: string | undefined;
|
199
|
+
|
200
|
+
if (nodeHref) {
|
201
|
+
pDiskInternalViewerLink += nodeHref + '/actors/pdisks/pdisk' + pad9(PDiskId);
|
202
|
+
}
|
203
|
+
|
204
|
+
const pdiskInfo: any = [
|
205
|
+
{
|
206
|
+
label: 'PDisk Id',
|
207
|
+
value: (
|
208
|
+
<div className={b('pdisk-id')}>
|
209
|
+
{PDiskId}
|
210
|
+
{pDiskInternalViewerLink && (
|
211
|
+
<Button
|
212
|
+
size="s"
|
213
|
+
className={b('external-button')}
|
214
|
+
href={pDiskInternalViewerLink}
|
215
|
+
target="_blank"
|
216
|
+
view="clear"
|
217
|
+
>
|
218
|
+
<Icon name="external" />
|
219
|
+
</Button>
|
220
|
+
)}
|
221
|
+
</div>
|
222
|
+
),
|
223
|
+
},
|
224
|
+
];
|
225
|
+
if (valueIsDefined(Path)) {
|
226
|
+
pdiskInfo.push({label: 'Path', value: Path});
|
227
|
+
}
|
228
|
+
if (valueIsDefined(Guid)) {
|
229
|
+
pdiskInfo.push({label: 'GUID', value: Guid});
|
230
|
+
}
|
231
|
+
if (valueIsDefined(Category)) {
|
232
|
+
pdiskInfo.push({label: 'Category', value: Category});
|
233
|
+
}
|
234
|
+
pdiskInfo.push({
|
235
|
+
label: 'Allocated Size',
|
236
|
+
value: bytesToGB(TotalSize - AvailableSize),
|
237
|
+
});
|
238
|
+
pdiskInfo.push({
|
239
|
+
label: 'Available Size',
|
240
|
+
value: bytesToGB(AvailableSize),
|
241
|
+
});
|
242
|
+
if (Number(TotalSize) >= 0 && Number(AvailableSize) >= 0) {
|
243
|
+
pdiskInfo.push({
|
244
|
+
label: 'Size',
|
245
|
+
value: (
|
246
|
+
<ProgressViewer
|
247
|
+
value={TotalSize - AvailableSize}
|
248
|
+
capacity={TotalSize}
|
249
|
+
formatValues={formatStorageValuesToGb}
|
250
|
+
colorizeProgress={true}
|
251
|
+
className={b('size')}
|
252
|
+
/>
|
253
|
+
),
|
254
|
+
});
|
255
|
+
}
|
256
|
+
if (valueIsDefined(State)) {
|
257
|
+
pdiskInfo.push({label: 'State', value: State});
|
258
|
+
}
|
259
|
+
if (valueIsDefined(Device)) {
|
260
|
+
pdiskInfo.push({
|
261
|
+
label: 'Device',
|
262
|
+
value: <EntityStatus status={Device} />,
|
263
|
+
});
|
264
|
+
}
|
265
|
+
if (valueIsDefined(Realtime)) {
|
266
|
+
pdiskInfo.push({
|
267
|
+
label: 'Realtime',
|
268
|
+
value: <EntityStatus status={Realtime} />,
|
269
|
+
});
|
270
|
+
}
|
271
|
+
if (valueIsDefined(SerialNumber)) {
|
272
|
+
pdiskInfo.push({label: 'SerialNumber', value: SerialNumber});
|
273
|
+
}
|
274
|
+
return (
|
275
|
+
<div>
|
276
|
+
<InfoViewer className={b('pdisk-details')} info={pdiskInfo} />
|
277
|
+
<div className={b('vdisks-container')}>
|
278
|
+
<div className={b('vdisks-header')}>VDisks</div>
|
279
|
+
{renderVDisks()}
|
280
|
+
</div>
|
281
|
+
</div>
|
282
|
+
);
|
283
|
+
};
|
284
|
+
|
285
|
+
return (
|
286
|
+
<div className={b('pdisk')} id={props.id}>
|
287
|
+
<div className={b('pdisk-header')}>
|
288
|
+
<div className={b('pdisk-title-wrapper')}>
|
289
|
+
<span>{data.Path}</span>
|
290
|
+
<EntityStatus status={data.Device} name={`${data.NodeId}-${data.PDiskId}`} />
|
291
|
+
</div>
|
292
|
+
<Button onClick={unfolded ? onClosePDiskDetails : onOpenPDiskDetails} view="clear">
|
293
|
+
<ArrowToggle direction={unfolded ? 'top' : 'bottom'} />
|
294
|
+
</Button>
|
295
|
+
</div>
|
296
|
+
{unfolded && renderPDiskDetails()}
|
297
|
+
</div>
|
298
|
+
);
|
299
|
+
}
|