ydb-embedded-ui 3.4.1 → 3.4.3
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 +18 -0
- package/README.md +31 -10
- package/dist/components/CriticalActionDialog/CriticalActionDialog.scss +1 -1
- package/dist/components/CriticalActionDialog/{CriticalActionDialog.js → CriticalActionDialog.tsx} +21 -16
- package/dist/components/CriticalActionDialog/index.ts +1 -0
- package/dist/containers/App/Content.js +1 -1
- package/dist/containers/Nodes/Nodes.scss +4 -0
- package/dist/containers/Nodes/Nodes.tsx +2 -1
- package/dist/containers/Storage/PDisk/PDisk.scss +15 -0
- package/dist/containers/Storage/PDisk/PDisk.tsx +43 -13
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +2 -2
- package/dist/containers/Storage/StorageNodes/StorageNodes.scss +8 -2
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +3 -1
- package/dist/containers/Storage/VDisk/VDisk.tsx +2 -2
- package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +2 -2
- package/dist/containers/Tablet/Tablet.tsx +121 -0
- package/dist/containers/Tablet/TabletControls/TabletControls.tsx +159 -0
- package/dist/containers/Tablet/TabletControls/index.ts +1 -0
- package/dist/containers/Tablet/TabletInfo/TabletInfo.tsx +80 -0
- package/dist/containers/Tablet/TabletInfo/index.ts +1 -0
- package/dist/containers/Tablet/TabletTable/TabletTable.tsx +59 -0
- package/dist/containers/Tablet/TabletTable/index.ts +1 -0
- package/dist/containers/Tablet/i18n/en.json +10 -0
- package/dist/containers/Tablet/i18n/index.ts +11 -0
- package/dist/containers/Tablet/i18n/ru.json +10 -0
- package/dist/containers/Tablet/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.tsx +9 -15
- package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsWrapper.tsx +2 -0
- package/dist/store/reducers/storage.js +1 -0
- package/dist/store/reducers/topic.ts +13 -0
- package/dist/types/api/nodes.ts +1 -1
- package/dist/types/store/topic.ts +3 -2
- package/dist/utils/nodes.ts +7 -0
- package/dist/utils/storage.ts +1 -1
- package/package.json +5 -2
- package/dist/containers/Tablet/Tablet.js +0 -448
@@ -0,0 +1,159 @@
|
|
1
|
+
import {useEffect, useState} from 'react';
|
2
|
+
import {Button} from '@gravity-ui/uikit';
|
3
|
+
|
4
|
+
import {ETabletState, TTabletStateInfo} from '../../../types/api/tablet';
|
5
|
+
import {CriticalActionDialog} from '../../../components/CriticalActionDialog';
|
6
|
+
|
7
|
+
import i18n from '../i18n';
|
8
|
+
import {b} from '../Tablet';
|
9
|
+
|
10
|
+
enum EVisibleDialogType {
|
11
|
+
'kill' = 'kill',
|
12
|
+
'stop' = 'kill',
|
13
|
+
'resume' = 'kill',
|
14
|
+
}
|
15
|
+
|
16
|
+
type VisibleDialogType = EVisibleDialogType | null;
|
17
|
+
|
18
|
+
interface TabletControlsProps {
|
19
|
+
tablet: TTabletStateInfo;
|
20
|
+
}
|
21
|
+
|
22
|
+
export const TabletControls = ({tablet}: TabletControlsProps) => {
|
23
|
+
const {TabletId, HiveId} = tablet;
|
24
|
+
|
25
|
+
const [isDialogVisible, setIsDialogVisible] = useState(false);
|
26
|
+
const [visibleDialogType, setVisibleDialogType] = useState<VisibleDialogType>(null);
|
27
|
+
const [isTabletActionsDisabled, setIsTabletActionsDisabled] = useState(false);
|
28
|
+
|
29
|
+
// Enable controls after data update
|
30
|
+
useEffect(() => {
|
31
|
+
setIsTabletActionsDisabled(false);
|
32
|
+
}, [tablet]);
|
33
|
+
|
34
|
+
const makeShowDialog = (type: VisibleDialogType) => () => {
|
35
|
+
setIsDialogVisible(true);
|
36
|
+
setVisibleDialogType(type);
|
37
|
+
};
|
38
|
+
|
39
|
+
const showKillDialog = makeShowDialog(EVisibleDialogType.kill);
|
40
|
+
const showStopDialog = makeShowDialog(EVisibleDialogType.stop);
|
41
|
+
const showResumeDialog = makeShowDialog(EVisibleDialogType.resume);
|
42
|
+
|
43
|
+
const hideDialog = () => {
|
44
|
+
setIsDialogVisible(false);
|
45
|
+
setVisibleDialogType(null);
|
46
|
+
};
|
47
|
+
|
48
|
+
const _onKillClick = () => {
|
49
|
+
setIsTabletActionsDisabled(true);
|
50
|
+
return window.api.killTablet(TabletId);
|
51
|
+
};
|
52
|
+
const _onStopClick = () => {
|
53
|
+
setIsTabletActionsDisabled(true);
|
54
|
+
return window.api.stopTablet(TabletId, HiveId);
|
55
|
+
};
|
56
|
+
const _onResumeClick = () => {
|
57
|
+
setIsTabletActionsDisabled(true);
|
58
|
+
return window.api.resumeTablet(TabletId, HiveId);
|
59
|
+
};
|
60
|
+
|
61
|
+
const hasHiveId = () => {
|
62
|
+
return HiveId && HiveId !== '0';
|
63
|
+
};
|
64
|
+
|
65
|
+
const isDisabledResume = () => {
|
66
|
+
if (isTabletActionsDisabled) {
|
67
|
+
return true;
|
68
|
+
}
|
69
|
+
|
70
|
+
return tablet.State !== ETabletState.Stopped && tablet.State !== ETabletState.Dead;
|
71
|
+
};
|
72
|
+
|
73
|
+
const isDisabledKill = () => {
|
74
|
+
return isTabletActionsDisabled;
|
75
|
+
};
|
76
|
+
|
77
|
+
const isDisabledStop = () => {
|
78
|
+
if (isTabletActionsDisabled) {
|
79
|
+
return true;
|
80
|
+
}
|
81
|
+
|
82
|
+
return tablet.State === ETabletState.Stopped || tablet.State === ETabletState.Deleted;
|
83
|
+
};
|
84
|
+
|
85
|
+
const renderDialog = () => {
|
86
|
+
if (!isDialogVisible) {
|
87
|
+
return null;
|
88
|
+
}
|
89
|
+
|
90
|
+
switch (visibleDialogType) {
|
91
|
+
case EVisibleDialogType.kill: {
|
92
|
+
return (
|
93
|
+
<CriticalActionDialog
|
94
|
+
visible={isDialogVisible}
|
95
|
+
text={i18n('dialog.kill')}
|
96
|
+
onClose={hideDialog}
|
97
|
+
onConfirm={_onKillClick}
|
98
|
+
/>
|
99
|
+
);
|
100
|
+
}
|
101
|
+
case EVisibleDialogType.stop: {
|
102
|
+
return (
|
103
|
+
<CriticalActionDialog
|
104
|
+
visible={isDialogVisible}
|
105
|
+
text={i18n('dialog.stop')}
|
106
|
+
onClose={hideDialog}
|
107
|
+
onConfirm={_onStopClick}
|
108
|
+
/>
|
109
|
+
);
|
110
|
+
}
|
111
|
+
case EVisibleDialogType.resume: {
|
112
|
+
return (
|
113
|
+
<CriticalActionDialog
|
114
|
+
visible={isDialogVisible}
|
115
|
+
text={i18n('dialog.resume')}
|
116
|
+
onClose={hideDialog}
|
117
|
+
onConfirm={_onResumeClick}
|
118
|
+
/>
|
119
|
+
);
|
120
|
+
}
|
121
|
+
default:
|
122
|
+
return null;
|
123
|
+
}
|
124
|
+
};
|
125
|
+
|
126
|
+
return (
|
127
|
+
<div className={b('controls')}>
|
128
|
+
<Button
|
129
|
+
onClick={showKillDialog}
|
130
|
+
view="action"
|
131
|
+
disabled={isDisabledKill()}
|
132
|
+
className={b('control')}
|
133
|
+
>
|
134
|
+
{i18n('controls.kill')}
|
135
|
+
</Button>
|
136
|
+
{hasHiveId() ? (
|
137
|
+
<>
|
138
|
+
<Button
|
139
|
+
onClick={showStopDialog}
|
140
|
+
view="action"
|
141
|
+
disabled={isDisabledStop()}
|
142
|
+
className={b('control')}
|
143
|
+
>
|
144
|
+
{i18n('controls.stop')}
|
145
|
+
</Button>
|
146
|
+
<Button
|
147
|
+
onClick={showResumeDialog}
|
148
|
+
view="action"
|
149
|
+
disabled={isDisabledResume()}
|
150
|
+
className={b('control')}
|
151
|
+
>
|
152
|
+
{i18n('controls.resume')}
|
153
|
+
</Button>
|
154
|
+
</>
|
155
|
+
) : null}
|
156
|
+
{renderDialog()}
|
157
|
+
</div>
|
158
|
+
);
|
159
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './TabletControls';
|
@@ -0,0 +1,80 @@
|
|
1
|
+
import {Link} from 'react-router-dom';
|
2
|
+
|
3
|
+
import {Link as UIKitLink} from '@gravity-ui/uikit';
|
4
|
+
|
5
|
+
import {ETabletState, TTabletStateInfo} from '../../../types/api/tablet';
|
6
|
+
import {InfoViewer, InfoViewerItem} from '../../../components/InfoViewer';
|
7
|
+
import routes, {createHref} from '../../../routes';
|
8
|
+
import {calcUptime} from '../../../utils';
|
9
|
+
import {getDefaultNodePath} from '../../Node/NodePages';
|
10
|
+
|
11
|
+
import {b} from '../Tablet';
|
12
|
+
|
13
|
+
interface TabletInfoProps {
|
14
|
+
tablet: TTabletStateInfo;
|
15
|
+
tenantPath: string;
|
16
|
+
}
|
17
|
+
|
18
|
+
export const TabletInfo = ({tablet, tenantPath}: TabletInfoProps) => {
|
19
|
+
const {
|
20
|
+
ChangeTime,
|
21
|
+
Generation,
|
22
|
+
FollowerId,
|
23
|
+
NodeId,
|
24
|
+
HiveId,
|
25
|
+
State,
|
26
|
+
Type,
|
27
|
+
TenantId: {SchemeShard} = {},
|
28
|
+
} = tablet;
|
29
|
+
|
30
|
+
const hasHiveId = HiveId && HiveId !== '0';
|
31
|
+
const hasUptime = State === ETabletState.Active;
|
32
|
+
|
33
|
+
const tabletInfo: InfoViewerItem[] = [{label: 'Database', value: tenantPath}];
|
34
|
+
|
35
|
+
if (hasHiveId) {
|
36
|
+
tabletInfo.push({
|
37
|
+
label: 'HiveId',
|
38
|
+
value: (
|
39
|
+
<UIKitLink href={createHref(routes.tablet, {id: HiveId})} target="_blank">
|
40
|
+
{HiveId}
|
41
|
+
</UIKitLink>
|
42
|
+
),
|
43
|
+
});
|
44
|
+
}
|
45
|
+
|
46
|
+
if (SchemeShard) {
|
47
|
+
tabletInfo.push({
|
48
|
+
label: 'SchemeShard',
|
49
|
+
value: (
|
50
|
+
<UIKitLink href={createHref(routes.tablet, {id: SchemeShard})} target="_blank">
|
51
|
+
{SchemeShard}
|
52
|
+
</UIKitLink>
|
53
|
+
),
|
54
|
+
});
|
55
|
+
}
|
56
|
+
|
57
|
+
tabletInfo.push({label: 'Type', value: Type}, {label: 'State', value: State});
|
58
|
+
|
59
|
+
if (hasUptime) {
|
60
|
+
tabletInfo.push({label: 'Uptime', value: calcUptime(ChangeTime)});
|
61
|
+
}
|
62
|
+
|
63
|
+
tabletInfo.push(
|
64
|
+
{label: 'Generation', value: Generation},
|
65
|
+
{
|
66
|
+
label: 'Node',
|
67
|
+
value: (
|
68
|
+
<Link className={b('link')} to={getDefaultNodePath(String(NodeId))}>
|
69
|
+
{NodeId}
|
70
|
+
</Link>
|
71
|
+
),
|
72
|
+
},
|
73
|
+
);
|
74
|
+
|
75
|
+
if (FollowerId) {
|
76
|
+
tabletInfo.push({label: 'Follower', value: FollowerId});
|
77
|
+
}
|
78
|
+
|
79
|
+
return <InfoViewer info={tabletInfo} />;
|
80
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './TabletInfo';
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import DataTable, {Column} from '@gravity-ui/react-data-table';
|
2
|
+
|
3
|
+
import type {ITabletPreparedHistoryItem} from '../../../types/store/tablet';
|
4
|
+
import {calcUptime} from '../../../utils';
|
5
|
+
|
6
|
+
const columns: Column<ITabletPreparedHistoryItem>[] = [
|
7
|
+
{
|
8
|
+
name: 'Generation',
|
9
|
+
align: DataTable.RIGHT,
|
10
|
+
render: ({row}) => row.generation,
|
11
|
+
},
|
12
|
+
{
|
13
|
+
name: 'Node ID',
|
14
|
+
align: DataTable.RIGHT,
|
15
|
+
sortable: false,
|
16
|
+
render: ({row}) => row.nodeId,
|
17
|
+
},
|
18
|
+
{
|
19
|
+
name: 'Change time',
|
20
|
+
align: DataTable.RIGHT,
|
21
|
+
sortable: false,
|
22
|
+
render: ({row}) => calcUptime(row.changeTime),
|
23
|
+
},
|
24
|
+
{
|
25
|
+
name: 'State',
|
26
|
+
sortable: false,
|
27
|
+
render: ({row}) => row.state,
|
28
|
+
},
|
29
|
+
{
|
30
|
+
name: 'Follower ID',
|
31
|
+
sortable: false,
|
32
|
+
render: ({row}) => {
|
33
|
+
return row.leader ? 'leader' : row.followerId;
|
34
|
+
},
|
35
|
+
},
|
36
|
+
];
|
37
|
+
|
38
|
+
const TABLE_SETTINGS = {
|
39
|
+
displayIndices: false,
|
40
|
+
};
|
41
|
+
|
42
|
+
interface TabletTableProps {
|
43
|
+
history: ITabletPreparedHistoryItem[];
|
44
|
+
}
|
45
|
+
|
46
|
+
export const TabletTable = ({history}: TabletTableProps) => {
|
47
|
+
return (
|
48
|
+
<DataTable
|
49
|
+
theme="yandex-cloud"
|
50
|
+
data={history}
|
51
|
+
columns={columns}
|
52
|
+
settings={TABLE_SETTINGS}
|
53
|
+
initialSortOrder={{
|
54
|
+
columnId: 'Generation',
|
55
|
+
order: DataTable.DESCENDING,
|
56
|
+
}}
|
57
|
+
/>
|
58
|
+
);
|
59
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './TabletTable';
|
@@ -0,0 +1,10 @@
|
|
1
|
+
{
|
2
|
+
"tablet.header": "Tablet",
|
3
|
+
"controls.kill": "Restart",
|
4
|
+
"controls.stop": "Stop",
|
5
|
+
"controls.resume": "Resume",
|
6
|
+
"dialog.kill": "The tablet will be restarted. Do you want to proceed?",
|
7
|
+
"dialog.stop": "The tablet will be stopped. Do you want to proceed?",
|
8
|
+
"dialog.resume": "The tablet will be resumed. Do you want to proceed?",
|
9
|
+
"emptyState": "The tablet was not found"
|
10
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import {i18n, Lang} from '../../../utils/i18n';
|
2
|
+
|
3
|
+
import en from './en.json';
|
4
|
+
import ru from './ru.json';
|
5
|
+
|
6
|
+
const COMPONENT = 'ydb-tablet-page';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
@@ -0,0 +1,10 @@
|
|
1
|
+
{
|
2
|
+
"tablet.header": "Таблетка",
|
3
|
+
"controls.kill": "Перезапустить",
|
4
|
+
"controls.stop": "Остановить",
|
5
|
+
"controls.resume": "Запустить",
|
6
|
+
"dialog.kill": "Таблетка будет перезапущена. Вы хотите продолжить?",
|
7
|
+
"dialog.stop": "Таблетка будет остановлена. Вы хотите продолжить?",
|
8
|
+
"dialog.resume": "Таблетка будет запущена. Вы хотите продолжить?",
|
9
|
+
"emptyState": "Таблетка не найдена"
|
10
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './Tablet';
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import block from 'bem-cn-lite';
|
2
|
-
import {useCallback, useEffect, useMemo,
|
2
|
+
import {useCallback, useEffect, useMemo, useState} from 'react';
|
3
3
|
import {useDispatch} from 'react-redux';
|
4
4
|
import {escapeRegExp} from 'lodash/fp';
|
5
5
|
|
@@ -53,8 +53,6 @@ export const Partitions = ({path, type, nodes, consumers}: PartitionsProps) => {
|
|
53
53
|
|
54
54
|
const dispatch = useDispatch();
|
55
55
|
|
56
|
-
const isFirstRenderRef = useRef(true);
|
57
|
-
|
58
56
|
const [generalSearchValue, setGeneralSearchValue] = useState('');
|
59
57
|
const [partitionIdSearchValue, setPartitionIdSearchValue] = useState('');
|
60
58
|
|
@@ -74,14 +72,6 @@ export const Partitions = ({path, type, nodes, consumers}: PartitionsProps) => {
|
|
74
72
|
useEffect(() => {
|
75
73
|
// Manual path control to ensure it updates with other values so no request with wrong params will be sent
|
76
74
|
setComponentCurrentPath(path);
|
77
|
-
|
78
|
-
// Do not reset selected consumer on first effect call
|
79
|
-
// To enable navigating to specific consumer
|
80
|
-
if (isFirstRenderRef.current) {
|
81
|
-
isFirstRenderRef.current = false;
|
82
|
-
} else {
|
83
|
-
dispatch(setSelectedConsumer(undefined));
|
84
|
-
}
|
85
75
|
}, [dispatch, path]);
|
86
76
|
|
87
77
|
const fetchConsumerData = useCallback(
|
@@ -90,11 +80,11 @@ export const Partitions = ({path, type, nodes, consumers}: PartitionsProps) => {
|
|
90
80
|
dispatch(setDataWasNotLoaded());
|
91
81
|
}
|
92
82
|
|
93
|
-
if (selectedConsumer) {
|
83
|
+
if (selectedConsumer && consumers && consumers.includes(selectedConsumer)) {
|
94
84
|
dispatch(getConsumer(componentCurrentPath, selectedConsumer));
|
95
85
|
}
|
96
86
|
},
|
97
|
-
[dispatch, selectedConsumer, componentCurrentPath],
|
87
|
+
[dispatch, selectedConsumer, componentCurrentPath, consumers],
|
98
88
|
);
|
99
89
|
|
100
90
|
useAutofetcher(fetchConsumerData, [fetchConsumerData], autorefresh);
|
@@ -111,10 +101,13 @@ export const Partitions = ({path, type, nodes, consumers}: PartitionsProps) => {
|
|
111
101
|
);
|
112
102
|
|
113
103
|
useEffect(() => {
|
114
|
-
|
104
|
+
const shouldUpdateSelectedConsumer =
|
105
|
+
!selectedConsumer || (consumers && !consumers.includes(selectedConsumer));
|
106
|
+
|
107
|
+
if (consumersToSelect && consumersToSelect.length && shouldUpdateSelectedConsumer) {
|
115
108
|
dispatch(setSelectedConsumer(consumersToSelect[0].value));
|
116
109
|
}
|
117
|
-
}, [dispatch, consumersToSelect, selectedConsumer]);
|
110
|
+
}, [dispatch, consumersToSelect, selectedConsumer, consumers]);
|
118
111
|
|
119
112
|
const selectedColumns: string[] = useMemo(
|
120
113
|
() =>
|
@@ -222,6 +215,7 @@ export const Partitions = ({path, type, nodes, consumers}: PartitionsProps) => {
|
|
222
215
|
options={consumersToSelect}
|
223
216
|
value={[selectedConsumer || '']}
|
224
217
|
onUpdate={handleConsumerSelectChange}
|
218
|
+
filterable={consumers && consumers.length > 5}
|
225
219
|
/>
|
226
220
|
<Search
|
227
221
|
onChange={handlePartitionIdSearchChange}
|
@@ -6,6 +6,7 @@ import type {EPathType} from '../../../../types/api/schema';
|
|
6
6
|
import {useTypedSelector} from '../../../../utils/hooks';
|
7
7
|
|
8
8
|
import {
|
9
|
+
cleanTopicData,
|
9
10
|
getTopic,
|
10
11
|
setDataWasNotLoaded as setTopicDataWasNotLoaded,
|
11
12
|
} from '../../../../store/reducers/topic';
|
@@ -61,6 +62,7 @@ export const PartitionsWrapper = ({path, type}: PartitionsWrapperProps) => {
|
|
61
62
|
|
62
63
|
useEffect(() => {
|
63
64
|
dispatch(setTopicDataWasNotLoaded());
|
65
|
+
dispatch(cleanTopicData());
|
64
66
|
dispatch(setNodesDataWasNotLoaded());
|
65
67
|
|
66
68
|
dispatch(getTopic(path));
|
@@ -313,6 +313,7 @@ export const getFlatListStorageNodes = createSelector([getStorageNodes], (storag
|
|
313
313
|
}).length;
|
314
314
|
return {
|
315
315
|
NodeId: node.NodeId,
|
316
|
+
SystemState: systemState.SystemState,
|
316
317
|
FQDN: systemState.Host,
|
317
318
|
DataCenter: systemState.DataCenter,
|
318
319
|
Rack: systemState.Rack,
|
@@ -18,6 +18,7 @@ import {convertBytesObjectToSpeed} from '../../utils/bytesParsers';
|
|
18
18
|
export const FETCH_TOPIC = createRequestActionTypes('topic', 'FETCH_TOPIC');
|
19
19
|
|
20
20
|
const SET_DATA_WAS_NOT_LOADED = 'topic/SET_DATA_WAS_NOT_LOADED';
|
21
|
+
const CLEAN_TOPIC_DATA = 'topic/CLEAN_TOPIC_DATA';
|
21
22
|
|
22
23
|
const initialState = {
|
23
24
|
loading: true,
|
@@ -64,6 +65,12 @@ const topic: Reducer<ITopicState, ITopicAction> = (state = initialState, action)
|
|
64
65
|
wasLoaded: false,
|
65
66
|
};
|
66
67
|
}
|
68
|
+
case CLEAN_TOPIC_DATA: {
|
69
|
+
return {
|
70
|
+
...state,
|
71
|
+
data: undefined,
|
72
|
+
};
|
73
|
+
}
|
67
74
|
default:
|
68
75
|
return state;
|
69
76
|
}
|
@@ -75,6 +82,12 @@ export const setDataWasNotLoaded = () => {
|
|
75
82
|
} as const;
|
76
83
|
};
|
77
84
|
|
85
|
+
export const cleanTopicData = () => {
|
86
|
+
return {
|
87
|
+
type: CLEAN_TOPIC_DATA,
|
88
|
+
} as const;
|
89
|
+
};
|
90
|
+
|
78
91
|
export function getTopic(path?: string) {
|
79
92
|
return createApiRequest({
|
80
93
|
request: window.api.getTopic({path}),
|
package/dist/types/api/nodes.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import {FETCH_TOPIC, setDataWasNotLoaded} from '../../store/reducers/topic';
|
1
|
+
import {FETCH_TOPIC, cleanTopicData, setDataWasNotLoaded} from '../../store/reducers/topic';
|
2
2
|
import type {ApiRequestAction} from '../../store/utils';
|
3
3
|
import type {IProcessSpeedStats} from '../../utils/bytesParsers';
|
4
4
|
import type {IResponseError} from '../api/error';
|
@@ -31,7 +31,8 @@ export interface ITopicState {
|
|
31
31
|
|
32
32
|
export type ITopicAction =
|
33
33
|
| ApiRequestAction<typeof FETCH_TOPIC, DescribeTopicResult, IResponseError>
|
34
|
-
| ReturnType<typeof setDataWasNotLoaded
|
34
|
+
| ReturnType<typeof setDataWasNotLoaded>
|
35
|
+
| ReturnType<typeof cleanTopicData>;
|
35
36
|
|
36
37
|
export interface ITopicRootStateSlice {
|
37
38
|
topic: ITopicState;
|
package/dist/utils/nodes.ts
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
import type {TSystemStateInfo} from '../types/api/nodes';
|
2
|
+
import type {INodesPreparedEntity} from '../types/store/nodes';
|
3
|
+
import {EFlag} from '../types/api/enums';
|
4
|
+
|
1
5
|
export enum NodesUptimeFilterValues {
|
2
6
|
'All' = 'All',
|
3
7
|
'SmallUptime' = 'SmallUptime',
|
@@ -7,3 +11,6 @@ export const NodesUptimeFilterTitles = {
|
|
7
11
|
[NodesUptimeFilterValues.All]: 'All',
|
8
12
|
[NodesUptimeFilterValues.SmallUptime]: 'Uptime < 1h',
|
9
13
|
};
|
14
|
+
|
15
|
+
export const isUnavailableNode = (node: INodesPreparedEntity | TSystemStateInfo) =>
|
16
|
+
!node.SystemState || node.SystemState === EFlag.Grey;
|
package/dist/utils/storage.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import type {TVSlotId, TVDiskStateInfo} from '../types/api/vdisk';
|
2
2
|
import type {IStoragePoolGroup} from '../types/store/storage';
|
3
3
|
|
4
|
-
export const
|
4
|
+
export const isFullVDiskData = (disk: TVDiskStateInfo | TVSlotId): disk is TVDiskStateInfo =>
|
5
5
|
'VDiskId' in disk;
|
6
6
|
|
7
7
|
export const getUsage = (data: IStoragePoolGroup, step = 1) => {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ydb-embedded-ui",
|
3
|
-
"version": "3.4.
|
3
|
+
"version": "3.4.3",
|
4
4
|
"files": [
|
5
5
|
"dist"
|
6
6
|
],
|
@@ -53,7 +53,9 @@
|
|
53
53
|
"eject": "react-scripts eject",
|
54
54
|
"prepublishOnly": "npm run package",
|
55
55
|
"typecheck": "tsc --noEmit",
|
56
|
-
"prepare": "husky install"
|
56
|
+
"prepare": "husky install",
|
57
|
+
"test:e2e:install": "npx playwright install --with-deps chromium",
|
58
|
+
"test:e2e": "npx playwright test --config=playwright.config.ts"
|
57
59
|
},
|
58
60
|
"lint-staged": {
|
59
61
|
"*.{scss}": [
|
@@ -104,6 +106,7 @@
|
|
104
106
|
"@gravity-ui/stylelint-config": "^1.0.1",
|
105
107
|
"@gravity-ui/tsconfig": "^1.0.0",
|
106
108
|
"@gravity-ui/uikit": "^3.20.2",
|
109
|
+
"@playwright/test": "^1.31.1",
|
107
110
|
"@testing-library/jest-dom": "^5.15.0",
|
108
111
|
"@testing-library/react": "^11.2.7",
|
109
112
|
"@testing-library/user-event": "^12.8.3",
|