ydb-embedded-ui 1.0.4 → 1.1.2
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.
- 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
|
+
}
|