ydb-embedded-ui 22.5.0 → 22.6.0

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 (105) hide show
  1. package/dist/components/AutoRefreshControl/StickyAutoRefreshControl.d.ts +2 -0
  2. package/dist/components/AutoRefreshControl/StickyAutoRefreshControl.js +9 -0
  3. package/dist/components/AutoRefreshControl/StickyAutoRefreshControl.js.map +1 -0
  4. package/dist/components/AutoRefreshControl/StickyAutoRefreshControl.scss +18 -0
  5. package/dist/components/HideUnavailableNodesSwitch/HideUnavailableNodesSwitch.d.ts +3 -0
  6. package/dist/components/HideUnavailableNodesSwitch/HideUnavailableNodesSwitch.js +12 -0
  7. package/dist/components/HideUnavailableNodesSwitch/HideUnavailableNodesSwitch.js.map +1 -0
  8. package/dist/components/HideUnavailableNodesSwitch/i18n/en.json +3 -0
  9. package/dist/components/HideUnavailableNodesSwitch/i18n/index.d.ts +2 -0
  10. package/dist/components/HideUnavailableNodesSwitch/i18n/index.js +6 -0
  11. package/dist/components/HideUnavailableNodesSwitch/i18n/index.js.map +1 -0
  12. package/dist/components/HideUnavailableNodesSwitch/i18n/ru.json +3 -0
  13. package/dist/components/PageMeta/PageMeta.d.ts +0 -2
  14. package/dist/components/PageMeta/PageMeta.js +1 -6
  15. package/dist/components/PageMeta/PageMeta.js.map +1 -1
  16. package/dist/containers/Node/Node.js +4 -3
  17. package/dist/containers/Node/Node.js.map +1 -1
  18. package/dist/containers/Node/Node.scss +14 -2
  19. package/dist/containers/Nodes/NodesControls/NodesControls.js +2 -1
  20. package/dist/containers/Nodes/NodesControls/NodesControls.js.map +1 -1
  21. package/dist/containers/Nodes/NodesTable.js +4 -0
  22. package/dist/containers/Nodes/NodesTable.js.map +1 -1
  23. package/dist/containers/Nodes/PaginatedNodes/GroupedNodesComponent.js +3 -0
  24. package/dist/containers/Nodes/PaginatedNodes/GroupedNodesComponent.js.map +1 -1
  25. package/dist/containers/Nodes/PaginatedNodes/NodesComponent.js +9 -1
  26. package/dist/containers/Nodes/PaginatedNodes/NodesComponent.js.map +1 -1
  27. package/dist/containers/Nodes/getNodes.js +2 -1
  28. package/dist/containers/Nodes/getNodes.js.map +1 -1
  29. package/dist/containers/PDiskPage/PDiskPage.js +5 -14
  30. package/dist/containers/PDiskPage/PDiskPage.js.map +1 -1
  31. package/dist/containers/PDiskPage/PDiskPage.scss +11 -4
  32. package/dist/containers/PDiskPage/i18n/en.json +0 -1
  33. package/dist/containers/PDiskPage/i18n/index.d.ts +1 -1
  34. package/dist/containers/Storage/PaginatedStorageNodes/GroupedStorageNodesComponent.js +3 -0
  35. package/dist/containers/Storage/PaginatedStorageNodes/GroupedStorageNodesComponent.js.map +1 -1
  36. package/dist/containers/Storage/PaginatedStorageNodes/StorageNodesComponent.js +8 -1
  37. package/dist/containers/Storage/PaginatedStorageNodes/StorageNodesComponent.js.map +1 -1
  38. package/dist/containers/Storage/PaginatedStorageNodes/StorageNodesControls.js +2 -1
  39. package/dist/containers/Storage/PaginatedStorageNodes/StorageNodesControls.js.map +1 -1
  40. package/dist/containers/Storage/PaginatedStorageNodesTable/PaginatedStorageNodesTable.js +4 -0
  41. package/dist/containers/Storage/PaginatedStorageNodesTable/PaginatedStorageNodesTable.js.map +1 -1
  42. package/dist/containers/Storage/PaginatedStorageNodesTable/getNodes.js +2 -1
  43. package/dist/containers/Storage/PaginatedStorageNodesTable/getNodes.js.map +1 -1
  44. package/dist/containers/Storage/Storage.scss +4 -0
  45. package/dist/containers/StorageGroupPage/StorageGroupPage.js +4 -15
  46. package/dist/containers/StorageGroupPage/StorageGroupPage.js.map +1 -1
  47. package/dist/containers/StorageGroupPage/StorageGroupPage.scss +14 -9
  48. package/dist/containers/StorageGroupPage/i18n/en.json +0 -1
  49. package/dist/containers/StorageGroupPage/i18n/index.d.ts +1 -1
  50. package/dist/containers/Tablet/Tablet.js +3 -2
  51. package/dist/containers/Tablet/Tablet.js.map +1 -1
  52. package/dist/containers/Tablet/Tablet.scss +15 -1
  53. package/dist/containers/VDiskPage/VDiskPage.js +5 -14
  54. package/dist/containers/VDiskPage/VDiskPage.js.map +1 -1
  55. package/dist/containers/VDiskPage/VDiskPage.scss +23 -4
  56. package/dist/containers/VDiskPage/i18n/en.json +0 -1
  57. package/dist/containers/VDiskPage/i18n/index.d.ts +1 -1
  58. package/dist/store/reducers/capabilities/hooks.d.ts +1 -0
  59. package/dist/store/reducers/capabilities/hooks.js +3 -0
  60. package/dist/store/reducers/capabilities/hooks.js.map +1 -1
  61. package/dist/store/reducers/nodes/types.d.ts +1 -0
  62. package/dist/store/reducers/storage/types.d.ts +1 -0
  63. package/dist/types/api/capabilities.d.ts +1 -1
  64. package/dist/types/api/nodes.d.ts +2 -0
  65. package/dist/utils/hooks/useHideUnavailableNodes.d.ts +5 -0
  66. package/dist/utils/hooks/useHideUnavailableNodes.js +21 -0
  67. package/dist/utils/hooks/useHideUnavailableNodes.js.map +1 -0
  68. package/package.json +1 -1
  69. package/src/components/AutoRefreshControl/StickyAutoRefreshControl.scss +18 -0
  70. package/src/components/AutoRefreshControl/StickyAutoRefreshControl.tsx +15 -0
  71. package/src/components/HideUnavailableNodesSwitch/HideUnavailableNodesSwitch.tsx +24 -0
  72. package/src/components/HideUnavailableNodesSwitch/i18n/en.json +3 -0
  73. package/src/components/HideUnavailableNodesSwitch/i18n/index.ts +7 -0
  74. package/src/components/HideUnavailableNodesSwitch/i18n/ru.json +3 -0
  75. package/src/components/PageMeta/PageMeta.tsx +0 -18
  76. package/src/containers/Node/Node.scss +14 -2
  77. package/src/containers/Node/Node.tsx +6 -6
  78. package/src/containers/Nodes/NodesControls/NodesControls.tsx +2 -0
  79. package/src/containers/Nodes/NodesTable.tsx +4 -0
  80. package/src/containers/Nodes/PaginatedNodes/GroupedNodesComponent.tsx +3 -0
  81. package/src/containers/Nodes/PaginatedNodes/NodesComponent.tsx +9 -1
  82. package/src/containers/Nodes/getNodes.ts +2 -0
  83. package/src/containers/PDiskPage/PDiskPage.scss +11 -4
  84. package/src/containers/PDiskPage/PDiskPage.tsx +4 -30
  85. package/src/containers/PDiskPage/i18n/en.json +0 -1
  86. package/src/containers/Storage/PaginatedStorageNodes/GroupedStorageNodesComponent.tsx +3 -0
  87. package/src/containers/Storage/PaginatedStorageNodes/StorageNodesComponent.tsx +8 -1
  88. package/src/containers/Storage/PaginatedStorageNodes/StorageNodesControls.tsx +2 -0
  89. package/src/containers/Storage/PaginatedStorageNodesTable/PaginatedStorageNodesTable.tsx +4 -0
  90. package/src/containers/Storage/PaginatedStorageNodesTable/getNodes.ts +2 -0
  91. package/src/containers/Storage/Storage.scss +4 -0
  92. package/src/containers/StorageGroupPage/StorageGroupPage.scss +14 -9
  93. package/src/containers/StorageGroupPage/StorageGroupPage.tsx +3 -32
  94. package/src/containers/StorageGroupPage/i18n/en.json +0 -1
  95. package/src/containers/Tablet/Tablet.scss +15 -1
  96. package/src/containers/Tablet/Tablet.tsx +21 -15
  97. package/src/containers/VDiskPage/VDiskPage.scss +23 -4
  98. package/src/containers/VDiskPage/VDiskPage.tsx +4 -31
  99. package/src/containers/VDiskPage/i18n/en.json +0 -1
  100. package/src/store/reducers/capabilities/hooks.ts +4 -0
  101. package/src/store/reducers/nodes/types.ts +1 -0
  102. package/src/store/reducers/storage/types.ts +1 -0
  103. package/src/types/api/capabilities.ts +1 -0
  104. package/src/types/api/nodes.ts +2 -0
  105. package/src/utils/hooks/useHideUnavailableNodes.ts +33 -0
@@ -7,6 +7,7 @@ import {NODES_COLUMNS_TO_DATA_FIELDS} from '../../../components/nodesColumns/con
7
7
  import {storageApi} from '../../../store/reducers/storage/storage';
8
8
  import type {NodesGroupByField, NodesRequiredField} from '../../../types/api/nodes';
9
9
  import {useAutoRefreshInterval} from '../../../utils/hooks';
10
+ import {useHideUnavailableNodes} from '../../../utils/hooks/useHideUnavailableNodes';
10
11
  import {NodesUptimeFilterValues} from '../../../utils/nodes';
11
12
  import {renderPaginatedTableErrorMessage} from '../../../utils/renderPaginatedTableErrorMessage';
12
13
  import {getRequiredDataFields} from '../../../utils/tableUtils/getRequiredDataFields';
@@ -105,6 +106,7 @@ export function GroupedStorageNodesComponent({
105
106
  scrollContainerRef,
106
107
  }: PaginatedStorageProps) {
107
108
  const [autoRefreshInterval] = useAutoRefreshInterval();
109
+ const {hideUnavailable} = useHideUnavailableNodes();
108
110
 
109
111
  const {nodesSearchValue, storageNodesGroupByParam, handleShowAllNodes} =
110
112
  useStorageQueryParams();
@@ -131,6 +133,7 @@ export function GroupedStorageNodesComponent({
131
133
  const {currentData, isFetching, error} = storageApi.useGetStorageNodesInfoQuery(
132
134
  {
133
135
  database,
136
+ alive: hideUnavailable || undefined,
134
137
  with: 'all',
135
138
  filter: nodesSearchValue.trim(),
136
139
  node_id: nodeId,
@@ -1,6 +1,7 @@
1
1
  import {PaginatedTableWithLayout} from '../../../components/PaginatedTable/PaginatedTableWithLayout';
2
2
  import {TableColumnSetup} from '../../../components/TableColumnSetup/TableColumnSetup';
3
3
  import {useViewerNodesHandlerHasGrouping} from '../../../store/reducers/capabilities/hooks';
4
+ import {useHideUnavailableNodes} from '../../../utils/hooks/useHideUnavailableNodes';
4
5
  import {renderPaginatedTableErrorMessage} from '../../../utils/renderPaginatedTableErrorMessage';
5
6
  import type {PaginatedStorageProps} from '../PaginatedStorage';
6
7
  import {PaginatedStorageNodesTable} from '../PaginatedStorageNodesTable/PaginatedStorageNodesTable';
@@ -22,6 +23,7 @@ export function StorageNodesComponent({
22
23
  useStorageQueryParams();
23
24
 
24
25
  const viewerNodesHandlerHasGrouping = useViewerNodesHandlerHasGrouping();
26
+ const {hideUnavailable} = useHideUnavailableNodes();
25
27
 
26
28
  const expertMode = useIsStorageExpertMode();
27
29
  const {handleDataFetched, columnsSettings, rowHeight} = useStorageColumnsSettings({
@@ -69,7 +71,12 @@ export function StorageNodesComponent({
69
71
  }
70
72
  tableWrapperProps={{
71
73
  scrollContainerRef,
72
- scrollDependencies: [nodesSearchValue, visibleEntities, nodesUptimeFilter],
74
+ scrollDependencies: [
75
+ nodesSearchValue,
76
+ visibleEntities,
77
+ nodesUptimeFilter,
78
+ hideUnavailable,
79
+ ],
73
80
  }}
74
81
  />
75
82
  );
@@ -4,6 +4,7 @@ import {ChartTreemap} from '@gravity-ui/icons';
4
4
  import {Button, Flex, Icon, Select, Text} from '@gravity-ui/uikit';
5
5
 
6
6
  import {EntitiesCount} from '../../../components/EntitiesCount/EntitiesCount';
7
+ import {HideUnavailableNodesSwitch} from '../../../components/HideUnavailableNodesSwitch/HideUnavailableNodesSwitch';
7
8
  import {usePaginatedTableState} from '../../../components/PaginatedTable/PaginatedTableContext';
8
9
  import {Search} from '../../../components/Search/Search';
9
10
  import {UptimeFilter} from '../../../components/UptimeFIlter';
@@ -127,6 +128,7 @@ export function StorageNodesControls({
127
128
  {i18n('controls_expert-mode')}
128
129
  </Button>
129
130
  ) : null}
131
+ <HideUnavailableNodesSwitch className={b('hide-unavailable')} />
130
132
  </Flex>
131
133
  {canUseStorageExpertMode && isStorageExpertMode ? (
132
134
  <StorageNodesExpertModePanel />
@@ -14,6 +14,7 @@ import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
14
14
  import type {PreparedStorageNode, VisibleEntities} from '../../../store/reducers/storage/types';
15
15
  import type {NodesGroupByField} from '../../../types/api/nodes';
16
16
  import {cn} from '../../../utils/cn';
17
+ import {useHideUnavailableNodes} from '../../../utils/hooks/useHideUnavailableNodes';
17
18
  import {NodesUptimeFilterValues, isUnavailableNode} from '../../../utils/nodes';
18
19
  import {STORAGE_NODES_DEFAULT_ROW_HEIGHT} from '../PaginatedStorageNodes/nodeExpertModeLayout';
19
20
 
@@ -69,9 +70,11 @@ export const PaginatedStorageNodesTable = ({
69
70
  onDataFetched,
70
71
  }: PaginatedStorageNodesTableProps) => {
71
72
  const history = useHistory();
73
+ const {hideUnavailable} = useHideUnavailableNodes();
72
74
  const tableFilters = React.useMemo(() => {
73
75
  return {
74
76
  searchValue,
77
+ hideUnavailable,
75
78
  visibleEntities,
76
79
  nodesUptimeFilter,
77
80
  database,
@@ -81,6 +84,7 @@ export const PaginatedStorageNodesTable = ({
81
84
  filterGroupBy,
82
85
  };
83
86
  }, [
87
+ hideUnavailable,
84
88
  searchValue,
85
89
  visibleEntities,
86
90
  nodesUptimeFilter,
@@ -29,6 +29,7 @@ export const getStorageNodes: FetchData<
29
29
  } = params;
30
30
  const {
31
31
  searchValue,
32
+ hideUnavailable,
32
33
  nodesUptimeFilter,
33
34
  visibleEntities,
34
35
  database,
@@ -51,6 +52,7 @@ export const getStorageNodes: FetchData<
51
52
  offset,
52
53
  sort,
53
54
  filter: searchValue?.trim(),
55
+ alive: hideUnavailable || undefined,
54
56
  uptime: getUptimeParamValue(nodesUptimeFilter),
55
57
  with: visibleEntities,
56
58
  database,
@@ -1,6 +1,10 @@
1
1
  @use '../../styles/mixins.scss';
2
2
 
3
3
  .global-storage {
4
+ &__hide-unavailable {
5
+ margin-inline-start: var(--g-spacing-1);
6
+ }
7
+
4
8
  &__search {
5
9
  @include mixins.search();
6
10
  }
@@ -1,4 +1,8 @@
1
+ @use '../../styles/mixins.scss';
2
+
1
3
  .ydb-storage-group-page {
4
+ --sticky-tabs-height: 40px;
5
+ --data-table-start: var(--sticky-tabs-height);
2
6
  position: relative;
3
7
 
4
8
  overflow: auto;
@@ -15,16 +19,12 @@
15
19
  }
16
20
 
17
21
  &__summary {
18
- margin-top: var(--g-spacing-4);
19
22
  margin-bottom: var(--g-spacing-6);
20
23
  }
21
24
 
22
- &__meta {
23
- margin-bottom: var(--g-spacing-4);
24
- }
25
-
26
25
  &__title {
27
26
  margin-bottom: var(--g-spacing-4);
27
+ padding-right: 125px;
28
28
  }
29
29
 
30
30
  &__title-content {
@@ -33,11 +33,16 @@
33
33
  min-width: 0;
34
34
  }
35
35
 
36
- &__refresh-control {
37
- flex-shrink: 0;
38
- }
39
-
40
36
  &__storage-title {
37
+ z-index: 3;
38
+
39
+ display: flex;
40
+ align-items: center;
41
+
42
+ height: var(--sticky-tabs-height);
41
43
  margin: 0;
44
+ padding-right: 125px;
45
+
46
+ @include mixins.sticky-top();
42
47
  }
43
48
  }
@@ -1,13 +1,11 @@
1
1
  import React from 'react';
2
2
 
3
- import {ArrowLeft} from '@gravity-ui/icons';
4
- import {Button, Flex, Icon, Text} from '@gravity-ui/uikit';
3
+ import {Flex, Text} from '@gravity-ui/uikit';
5
4
  import {skipToken} from '@reduxjs/toolkit/query';
6
5
  import {Helmet} from 'react-helmet-async';
7
- import {useHistory} from 'react-router-dom';
8
6
  import {StringParam, useQueryParams} from 'use-query-params';
9
7
 
10
- import {AutoRefreshControl} from '../../components/AutoRefreshControl/AutoRefreshControl';
8
+ import {StickyAutoRefreshControl} from '../../components/AutoRefreshControl/StickyAutoRefreshControl';
11
9
  import {CopyableValue} from '../../components/CopyableValue/CopyableValue';
12
10
  import {ResponseError} from '../../components/Errors/ResponseError';
13
11
  import {InfoViewerSkeleton} from '../../components/InfoViewerSkeleton/InfoViewerSkeleton';
@@ -32,13 +30,10 @@ const storageGroupPageCn = cn('ydb-storage-group-page');
32
30
 
33
31
  export function StorageGroupPage() {
34
32
  const dispatch = useTypedDispatch();
35
- const history = useHistory();
36
33
  const database = useDatabaseFromQuery();
37
34
  const containerRef = React.useRef<HTMLDivElement>(null);
38
35
 
39
36
  const [{groupId}] = useQueryParams({groupId: StringParam});
40
- const showBackButton = Boolean(groupId) && history.length > 1;
41
- const showRefreshInTitle = Boolean(groupId) && !showBackButton;
42
37
 
43
38
  const [autoRefreshInterval] = useAutoRefreshInterval();
44
39
  const capabilitiesLoaded = useCapabilitiesLoaded();
@@ -75,27 +70,6 @@ export function StorageGroupPage() {
75
70
  );
76
71
  };
77
72
 
78
- const renderPageMeta = () => {
79
- if (!showBackButton) {
80
- return null;
81
- }
82
-
83
- return (
84
- <Flex
85
- className={storageGroupPageCn('meta')}
86
- alignItems="center"
87
- justifyContent="space-between"
88
- gap={2}
89
- >
90
- <Button view="outlined" onClick={() => history.goBack()}>
91
- <Icon data={ArrowLeft} />
92
- {storageGroupPageKeyset('action_back')}
93
- </Button>
94
- <AutoRefreshControl className={storageGroupPageCn('refresh-control')} />
95
- </Flex>
96
- );
97
- };
98
-
99
73
  const renderPageTitle = () => {
100
74
  return (
101
75
  <Flex
@@ -122,9 +96,6 @@ export function StorageGroupPage() {
122
96
  </CopyableValue>
123
97
  <StorageGroupStateLabel state={state} />
124
98
  </Flex>
125
- {showRefreshInTitle && (
126
- <AutoRefreshControl className={storageGroupPageCn('refresh-control')} />
127
- )}
128
99
  </Flex>
129
100
  );
130
101
  };
@@ -167,8 +138,8 @@ export function StorageGroupPage() {
167
138
  return (
168
139
  <div className={storageGroupPageCn(null)} ref={containerRef}>
169
140
  {renderHelmet()}
141
+ {groupId ? <StickyAutoRefreshControl /> : null}
170
142
  <div className={storageGroupPageCn('summary')}>
171
- {renderPageMeta()}
172
143
  {renderPageTitle()}
173
144
  {renderError()}
174
145
  {renderInfo()}
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "storage-group": "Storage Group",
3
3
  "storage": "Storage",
4
- "action_back": "Back",
5
4
  "action_copy-group-id-from-title": "Copy Storage Group ID from page title"
6
5
  }
@@ -2,10 +2,24 @@
2
2
 
3
3
  .ydb-tablet-page {
4
4
  $_: &;
5
+ position: relative;
6
+
7
+ overflow: auto;
8
+
5
9
  height: 100%;
6
- padding: 20px;
10
+ padding: 0 20px 20px;
7
11
  @include mixins.body-2-typography();
8
12
 
13
+ &__content {
14
+ // The sticky refresh wrapper already supplies the initial 20px page inset.
15
+ min-height: calc(100% - 20px);
16
+ }
17
+
18
+ &__meta {
19
+ min-height: 28px;
20
+ padding-right: 121px;
21
+ }
22
+
9
23
  &__placeholder {
10
24
  display: flex;
11
25
  flex: 1 1 auto;
@@ -7,12 +7,13 @@ import {useParams} from 'react-router-dom';
7
7
  import {useQueryParams} from 'use-query-params';
8
8
  import {z} from 'zod';
9
9
 
10
+ import {StickyAutoRefreshControl} from '../../components/AutoRefreshControl/StickyAutoRefreshControl';
10
11
  import {EmptyStateWrapper} from '../../components/EmptyState';
11
12
  import {EntityPageTitle} from '../../components/EntityPageTitle/EntityPageTitle';
12
13
  import {ResponseError} from '../../components/Errors/ResponseError';
13
14
  import {InternalLink} from '../../components/InternalLink';
14
15
  import {LoaderWrapper} from '../../components/LoaderWrapper/LoaderWrapper';
15
- import {PageMetaWithAutorefresh} from '../../components/PageMeta/PageMeta';
16
+ import {PageMeta} from '../../components/PageMeta/PageMeta';
16
17
  import {tabletPageQueryParams, useTabletPagePath} from '../../routes';
17
18
  import {useTabletDevUiSecurePath} from '../../store/reducers/capabilities/hooks';
18
19
  import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
@@ -106,25 +107,30 @@ export function Tablet() {
106
107
  }
107
108
 
108
109
  return (
109
- <Flex gap={5} direction="column" className={b()}>
110
+ <div className={b()}>
110
111
  <Helmet>
111
112
  <title>{`${id} — ${i18n('tablet.header')} — ${
112
113
  databaseFullPath || queryClusterName || CLUSTER_DEFAULT_TITLE
113
114
  }`}</title>
114
115
  </Helmet>
115
- <PageMetaWithAutorefresh items={metaItems} />
116
- <LoaderWrapper loading={loading} size="l">
117
- {error ? <ResponseError error={error} /> : null}
118
- {currentData ? (
119
- <TabletContent
120
- id={id}
121
- tablet={tablet}
122
- history={history}
123
- database={database || databaseFullPath}
124
- />
125
- ) : null}
126
- </LoaderWrapper>
127
- </Flex>
116
+ <StickyAutoRefreshControl />
117
+ <Flex gap={5} direction="column" className={b('content')}>
118
+ <Flex alignItems="center" className={b('meta')}>
119
+ <PageMeta items={metaItems} />
120
+ </Flex>
121
+ <LoaderWrapper loading={loading} size="l">
122
+ {error ? <ResponseError error={error} /> : null}
123
+ {currentData ? (
124
+ <TabletContent
125
+ id={id}
126
+ tablet={tablet}
127
+ history={history}
128
+ database={database || databaseFullPath}
129
+ />
130
+ ) : null}
131
+ </LoaderWrapper>
132
+ </Flex>
133
+ </div>
128
134
  );
129
135
  }
130
136
 
@@ -1,4 +1,8 @@
1
+ @use '../../styles/mixins.scss';
2
+
1
3
  .ydb-vdisk-page {
4
+ --sticky-tabs-height: 40px;
5
+ --data-table-start: var(--sticky-tabs-height);
2
6
  --g-definition-list-item-gap: var(--g-spacing-3);
3
7
  position: relative;
4
8
 
@@ -6,9 +10,8 @@
6
10
  overflow-anchor: none;
7
11
 
8
12
  height: 100%;
9
- padding: 20px 20px 0;
13
+ padding: 0 20px;
10
14
 
11
- &__meta,
12
15
  &__title,
13
16
  &__controls,
14
17
  &__storage-details,
@@ -36,8 +39,8 @@
36
39
  min-width: 0;
37
40
  }
38
41
 
39
- &__refresh-control {
40
- flex-shrink: 0;
42
+ &__title {
43
+ padding-right: 125px;
41
44
  }
42
45
 
43
46
  &__controls {
@@ -53,5 +56,21 @@
53
56
 
54
57
  &__tablets-content {
55
58
  margin-top: var(--g-spacing-4);
59
+
60
+ .data-table__sticky_moving {
61
+ // Override the table's inline sticky offset.
62
+ top: var(--sticky-tabs-height) !important;
63
+ }
64
+ }
65
+
66
+ &__tabs {
67
+ z-index: 3;
68
+
69
+ height: var(--sticky-tabs-height);
70
+ margin-bottom: 0;
71
+ padding-right: 125px;
72
+
73
+ @include mixins.sticky-top();
74
+ @include mixins.tabs-wrapper-styles();
56
75
  }
57
76
  }
@@ -1,13 +1,12 @@
1
1
  import React from 'react';
2
2
 
3
- import {ArrowLeft, Wrench} from '@gravity-ui/icons';
4
- import {Button, Flex, Icon, Tab, TabList, TabProvider, Text} from '@gravity-ui/uikit';
3
+ import {Wrench} from '@gravity-ui/icons';
4
+ import {Flex, Tab, TabList, TabProvider, Text} from '@gravity-ui/uikit';
5
5
  import {skipToken} from '@reduxjs/toolkit/query';
6
6
  import {isNil} from 'lodash';
7
7
  import {Helmet} from 'react-helmet-async';
8
- import {useHistory} from 'react-router-dom';
9
8
 
10
- import {AutoRefreshControl} from '../../components/AutoRefreshControl/AutoRefreshControl';
9
+ import {StickyAutoRefreshControl} from '../../components/AutoRefreshControl/StickyAutoRefreshControl';
11
10
  import {CopyableValue} from '../../components/CopyableValue/CopyableValue';
12
11
  import {DiskTypeLabel} from '../../components/DiskStatus/DiskStatus';
13
12
  import {ResponseError} from '../../components/Errors/ResponseError';
@@ -48,8 +47,6 @@ const vDiskPageCn = cn('ydb-vdisk-page');
48
47
 
49
48
  export function VDiskPage() {
50
49
  const dispatch = useTypedDispatch();
51
- const history = useHistory();
52
- const showBackButton = history.length > 1;
53
50
  const getVDiskPagePath = useVDiskPagePath();
54
51
 
55
52
  const containerRef = React.useRef<HTMLDivElement>(null);
@@ -114,27 +111,6 @@ export function VDiskPage() {
114
111
  );
115
112
  };
116
113
 
117
- const renderPageMeta = () => {
118
- if (!showBackButton) {
119
- return null;
120
- }
121
-
122
- return (
123
- <Flex
124
- className={vDiskPageCn('meta')}
125
- alignItems="center"
126
- justifyContent="space-between"
127
- gap={1}
128
- >
129
- <Button size="m" view="outlined" onClick={() => history.goBack()}>
130
- <Icon data={ArrowLeft} size={16} />
131
- {vDiskPageKeyset('action_back')}
132
- </Button>
133
- <AutoRefreshControl className={vDiskPageCn('refresh-control')} />
134
- </Flex>
135
- );
136
- };
137
-
138
114
  const renderTitleMeta = () => {
139
115
  return (
140
116
  <Flex gap={2} wrap="wrap" alignItems="center">
@@ -182,9 +158,6 @@ export function VDiskPage() {
182
158
  </CopyableValue>
183
159
  </Flex>
184
160
  </Flex>
185
- {!showBackButton && (
186
- <AutoRefreshControl className={vDiskPageCn('refresh-control')} />
187
- )}
188
161
  </Flex>
189
162
  );
190
163
  };
@@ -334,7 +307,7 @@ export function VDiskPage() {
334
307
  return (
335
308
  <div className={vDiskPageCn(null)} ref={containerRef}>
336
309
  {renderHelmet()}
337
- {renderPageMeta()}
310
+ <StickyAutoRefreshControl />
338
311
  {renderPageTitle()}
339
312
  {renderControls()}
340
313
  {renderContent()}
@@ -4,7 +4,6 @@
4
4
  "vdisk": "VDisk",
5
5
  "storage": "Storage",
6
6
  "tablets": "Tablets",
7
- "action_back": "Back",
8
7
  "action_go-to-pdisk": "Go to PDisk",
9
8
  "action_open-in-developer-ui": "Open in Developer UI",
10
9
  "tablet-id": "Tablet ID",
@@ -106,6 +106,10 @@ export const useStorageGroupsHandlerHasGrouping = () => {
106
106
  return useGetFeatureVersion('/storage/groups') > 4;
107
107
  };
108
108
 
109
+ export const useViewerNodesAliveFilterAvailable = () => {
110
+ return useGetFeatureVersion('/viewer/nodes/alive') >= 1;
111
+ };
112
+
109
113
  export const useViewerNodesHandlerHasGrouping = () => {
110
114
  return useGetFeatureVersion('/viewer/nodes') > 6;
111
115
  };
@@ -3,6 +3,7 @@ import type {NodesUptimeFilterValues} from '../../../utils/nodes';
3
3
 
4
4
  export interface NodesFilters {
5
5
  searchValue: string;
6
+ hideUnavailable?: boolean;
6
7
  withProblems: boolean;
7
8
  uptimeFilter: NodesUptimeFilterValues;
8
9
  peerRoleFilter?: NodesPeerRole;
@@ -16,6 +16,7 @@ export type StorageType = z.infer<typeof storageTypeSchema>;
16
16
 
17
17
  export interface PreparedStorageNodeFilters {
18
18
  searchValue: string;
19
+ hideUnavailable?: boolean;
19
20
  nodesUptimeFilter: NodesUptimeFilterValues;
20
21
  visibleEntities: VisibleEntities;
21
22
 
@@ -27,6 +27,7 @@ export type Capability =
27
27
  | '/viewer/cluster'
28
28
  | '/vdisk/blobindexstat'
29
29
  | '/viewer/nodes'
30
+ | '/viewer/nodes/alive'
30
31
  | '/viewer/peers'
31
32
  | '/viewer/acl'
32
33
  | '/viewer/topic_data'
@@ -363,6 +363,8 @@ export interface NodesRequestParams {
363
363
  uptime?: number;
364
364
  /** return nodes with SystemState !== EFlag.Green */
365
365
  problems_only?: boolean;
366
+ /** exclude disconnected nodes; requires /viewer/nodes/alive capability */
367
+ alive?: boolean;
366
368
  /** filter nodes by id or host */
367
369
  filter?: string;
368
370
  filter_peer_role?: NodesPeerRole; // v14
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+
3
+ import {BooleanParam, useQueryParam} from 'use-query-params';
4
+ import {z} from 'zod';
5
+
6
+ import {
7
+ useCapabilitiesLoaded,
8
+ useViewerNodesAliveFilterAvailable,
9
+ } from '../../store/reducers/capabilities/hooks';
10
+
11
+ const hideUnavailableSchema = z.boolean().catch(false);
12
+
13
+ export function useHideUnavailableNodes() {
14
+ const [value, setValue] = useQueryParam('hideUnavailable', BooleanParam);
15
+ const available = useViewerNodesAliveFilterAvailable();
16
+ const capabilitiesLoaded = useCapabilitiesLoaded();
17
+ const hideUnavailable = available && hideUnavailableSchema.parse(value);
18
+
19
+ React.useEffect(() => {
20
+ if (capabilitiesLoaded && !available && value !== undefined) {
21
+ setValue(undefined, 'replaceIn');
22
+ }
23
+ }, [available, capabilitiesLoaded, setValue, value]);
24
+
25
+ const handleHideUnavailableChange = React.useCallback(
26
+ (checked: boolean) => {
27
+ setValue(checked || undefined, 'replaceIn');
28
+ },
29
+ [setValue],
30
+ );
31
+
32
+ return {available, hideUnavailable, handleHideUnavailableChange};
33
+ }