ydb-embedded-ui 1.0.0 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ### [1.0.3](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v1.0.2...v1.0.3) (2022-03-21)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * query status should not be shown when query is loading ([d214eee](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/d214eee575b63341082f0be33163e3fce520df88))
9
+ * should set correct initial current index in queries history ([c3228d7](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/c3228d7a6a0c810982db1bdbec7762889ac44ffa))
10
+ * **Storage:** wording fixed [YDB-1552] ([3f487ff](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/3f487ff01117963760b676d14281e93e5f3002c0))
11
+
12
+ ### [1.0.2](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v1.0.1...v1.0.2) (2022-03-11)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **Header:** add link to internal viewer ([64af24f](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/64af24f8d78cf0d34466ac129be10c0764cce3d4))
18
+
19
+ ### [1.0.1](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v1.0.0...v1.0.1) (2022-03-05)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * **QueriesHistory:** should save history to local storage ([#8](https://www.github.com/ydb-platform/ydb-embedded-ui/issues/8)) ([57031ab](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/57031ab16900e9d1112bbf506d5c777f94f883bb))
25
+
3
26
  ## [1.0.0](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v0.2.0...v1.0.0) (2022-03-01)
4
27
 
5
28
 
@@ -25,4 +25,16 @@
25
25
  font-size: var(--yc-text-body2-font-size);
26
26
  font-weight: 500;
27
27
  }
28
+
29
+ &__cluster-name-wrapper {
30
+ display: flex;
31
+ align-items: center;
32
+
33
+ height: 100%;
34
+ gap: 5px;
35
+ }
36
+
37
+ &__divider {
38
+ height: 80%;
39
+ }
28
40
  }
@@ -1,15 +1,19 @@
1
- import {useEffect} from 'react';
1
+ import React, {useEffect} from 'react';
2
2
  import {useDispatch, useSelector} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
  import {useHistory, useLocation} from 'react-router';
5
+ import {Breadcrumbs, BreadcrumbsItem, Link} from '@yandex-cloud/uikit';
5
6
 
6
- import {clusterName as clusterNameLocation} from '../../store';
7
+ import Divider from '../../components/Divider/Divider';
8
+ //@ts-ignore
9
+ import Icon from '../../components/Icon/Icon';
10
+
11
+ import {clusterName as clusterNameLocation, backend, customBackend} from '../../store';
7
12
  import {getClusterInfo} from '../../store/reducers/cluster';
8
13
  import {getHostInfo} from '../../store/reducers/host';
14
+ import {HeaderItemType} from '../../store/reducers/header';
9
15
 
10
16
  import './Header.scss';
11
- import {Breadcrumbs, BreadcrumbsItem} from '@yandex-cloud/uikit';
12
- import {HeaderItemType} from '../../store/reducers/header';
13
17
 
14
18
  const b = cn('header');
15
19
 
@@ -49,6 +53,12 @@ function Header() {
49
53
  const renderHeader = () => {
50
54
  const clusterNameFinal = singleClusterMode ? host.ClusterName : clusterName;
51
55
 
56
+ let link = backend + '/internal';
57
+
58
+ if (singleClusterMode && !customBackend) {
59
+ link = `/internal`;
60
+ }
61
+
52
62
  const breadcrumbItems = header.reduce((acc, el) => {
53
63
  acc.push({text: el.text, action: () => history.push(el.link)});
54
64
  return acc;
@@ -63,8 +73,20 @@ function Header() {
63
73
  firstDisplayedItemsCount={1}
64
74
  />
65
75
  </div>
66
- <div>
67
- {clusterNameFinal && <ClusterName name={clusterNameFinal} />}
76
+
77
+ <div className={b('cluster-name-wrapper')}>
78
+ <Link href={link} target="_blank">
79
+ Internal viewer{' '}
80
+ <Icon name="external" viewBox={'0 0 16 16'} width={16} height={16} />
81
+ </Link>
82
+ {clusterNameFinal && (
83
+ <React.Fragment>
84
+ <div className={b('divider')}>
85
+ <Divider />
86
+ </div>
87
+ <ClusterName name={clusterNameFinal} />
88
+ </React.Fragment>
89
+ )}
68
90
  </div>
69
91
  </header>
70
92
  );
@@ -210,13 +210,16 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes}: StorageGro
210
210
  ];
211
211
 
212
212
  let columns = allColumns;
213
+ let emptyMessage = 'No such groups.';
213
214
 
214
215
  if (visibleEntities === VisibleEntities.Space) {
215
216
  columns = allColumns.filter((col) => col.name !== TableColumnsIds.Missing);
217
+ emptyMessage = 'No storage groups with space problems.';
216
218
  }
217
219
 
218
220
  if (visibleEntities === VisibleEntities.Missing) {
219
221
  columns = allColumns.filter((col) => col.name !== TableColumnsIds.UsedSpaceFlag);
222
+ emptyMessage = 'No degraded groups.';
220
223
  }
221
224
  return data ? (
222
225
  <DataTable
@@ -226,7 +229,7 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes}: StorageGro
226
229
  columns={columns}
227
230
  settings={tableSettings}
228
231
  initialSortOrder={setSortOrder(visibleEntities)}
229
- emptyDataMessage="No such groups."
232
+ emptyDataMessage={emptyMessage}
230
233
  />
231
234
  ) : null;
232
235
  }
@@ -114,9 +114,14 @@ function StorageNodes({data, tableSettings, visibleEntities}: StorageGroupsProps
114
114
  ];
115
115
 
116
116
  let columns = allColumns;
117
+ let emptyMessage = 'No such nodes.';
117
118
 
118
119
  if (visibleEntities === VisibleEntities.Space) {
119
120
  columns = allColumns.filter((col) => col.name !== TableColumnsIds.Missing);
121
+ emptyMessage = 'No nodes with space problems.';
122
+ }
123
+ if (visibleEntities === VisibleEntities.Missing) {
124
+ emptyMessage = 'No degraded nodes.';
120
125
  }
121
126
 
122
127
  return data ? (
@@ -127,7 +132,7 @@ function StorageNodes({data, tableSettings, visibleEntities}: StorageGroupsProps
127
132
  columns={columns}
128
133
  settings={tableSettings}
129
134
  initialSortOrder={setSortOrder(visibleEntities)}
130
- emptyDataMessage="No such nodes."
135
+ emptyDataMessage={emptyMessage}
131
136
  />
132
137
  ) : null;
133
138
  }
@@ -1,6 +1,10 @@
1
1
  @import '../../../styles/mixins.scss';
2
2
 
3
3
  .kv-acl {
4
+ display: flex;
5
+ overflow: auto;
6
+ flex-grow: 1;
7
+
4
8
  padding: 0 12px 16px;
5
9
  @include query-data-table;
6
10
  &__message-container {
@@ -38,8 +42,4 @@
38
42
  color: var(--yc-color-text-danger);
39
43
  }
40
44
  }
41
-
42
- &__result {
43
- overflow: auto;
44
- }
45
45
  }
@@ -13,7 +13,9 @@
13
13
  max-height: 100%;
14
14
 
15
15
  &__overview-wrapper {
16
+ display: flex;
16
17
  overflow: auto;
18
+ flex-grow: 1;
17
19
 
18
20
  padding: 0 12px 16px;
19
21
  }
@@ -91,7 +93,15 @@
91
93
  }
92
94
 
93
95
  &__info {
96
+ display: flex;
94
97
  overflow: hidden;
98
+ flex-direction: column;
99
+ }
100
+
101
+ &__schema {
102
+ display: flex;
103
+ overflow: auto;
104
+ flex-grow: 1;
95
105
  }
96
106
 
97
107
  &__info-controls {
@@ -78,7 +78,7 @@ interface ObjectSummaryProps {
78
78
  onCollapseSummary: VoidFunction;
79
79
  onExpandSummary: VoidFunction;
80
80
  isCollapsed: boolean;
81
- additionalTenantInfo?: any
81
+ additionalTenantInfo?: any;
82
82
  }
83
83
 
84
84
  function ObjectSummary(props: ObjectSummaryProps) {
@@ -168,10 +168,16 @@ function ObjectSummary(props: ObjectSummaryProps) {
168
168
  const renderTabContent = () => {
169
169
  switch (infoTab) {
170
170
  case TenantInfoTabsIds.acl: {
171
- return <Acl additionalTenantInfo={props.additionalTenantInfo}/>;
171
+ return <Acl additionalTenantInfo={props.additionalTenantInfo} />;
172
172
  }
173
173
  case TenantInfoTabsIds.schema: {
174
- return loadingSchema ? renderLoader() : <SchemaViewer data={schema} />;
174
+ return loadingSchema ? (
175
+ renderLoader()
176
+ ) : (
177
+ <div className={b('schema')}>
178
+ <SchemaViewer data={schema} />
179
+ </div>
180
+ );
175
181
  }
176
182
  default: {
177
183
  return renderObjectOverview();
@@ -194,7 +200,9 @@ function ObjectSummary(props: ObjectSummaryProps) {
194
200
  <div className={b('tree-title')}>Navigation</div>
195
201
  </div>
196
202
  <div className={b('tree')}>
197
- {tenantData && <SchemaNode fullPath={tenantName as string} data={tenantData} isRoot />}
203
+ {tenantData && (
204
+ <SchemaNode fullPath={tenantName as string} data={tenantData} isRoot />
205
+ )}
198
206
  </div>
199
207
  </div>
200
208
  );
@@ -38,6 +38,7 @@ function QueriesHistory(props: QueriesHistoryProps) {
38
38
  };
39
39
 
40
40
  const renderSavedQueries = () => {
41
+ const reversedHistory = ([] as string[]).concat(history).reverse();
41
42
  return (
42
43
  <Popup
43
44
  className={b('popup-wrapper')}
@@ -47,7 +48,7 @@ function QueriesHistory(props: QueriesHistoryProps) {
47
48
  onClose={onCloseHistory}
48
49
  >
49
50
  <div className={b()}>
50
- {history.length === 0 ? (
51
+ {reversedHistory.length === 0 ? (
51
52
  <div className={b('empty')}>History is empty</div>
52
53
  ) : (
53
54
  <React.Fragment>
@@ -57,7 +58,7 @@ function QueriesHistory(props: QueriesHistoryProps) {
57
58
  </div>
58
59
  </div>
59
60
  <div>
60
- {history?.reverse().map((query, index) => {
61
+ {reversedHistory.map((query, index) => {
61
62
  return (
62
63
  <div
63
64
  className={b('saved-queries-row')}
@@ -154,7 +154,7 @@ function QueryEditor(props) {
154
154
  useEffect(() => {
155
155
  const {monacoHotKey, setMonacoHotKey} = props;
156
156
  if (monacoHotKey === null) {
157
- return
157
+ return;
158
158
  }
159
159
  setMonacoHotKey(null);
160
160
  switch (monacoHotKey) {
@@ -464,19 +464,12 @@ function QueryEditor(props) {
464
464
  };
465
465
 
466
466
  const getExecuteResult = () => {
467
- const {
468
- data = [],
469
- error,
470
- loading,
471
- history: {queries},
472
- } = props.executeQuery;
467
+ const {data = [], error} = props.executeQuery;
473
468
 
474
469
  if (error) {
475
470
  return error.data || error;
476
471
  } else if (data.length > 0) {
477
472
  return data;
478
- } else if (!loading && queries.length) {
479
- return 'The request was successful';
480
473
  } else {
481
474
  return '';
482
475
  }
@@ -252,28 +252,32 @@ function QueryExplain(props) {
252
252
  return (
253
253
  <React.Fragment>
254
254
  <div className={b('controls')}>
255
- <div className={b('controls-right')}>
256
- <QueryExecutionStatus hasError={Boolean(props.error)} />
257
- {!props.error && (
258
- <React.Fragment>
259
- <Divider />
260
- <RadioButton
261
- options={explainOptions}
262
- value={activeOption}
263
- onUpdate={onSelectOption}
255
+ {!props.loading && (
256
+ <React.Fragment>
257
+ <div className={b('controls-right')}>
258
+ <QueryExecutionStatus hasError={Boolean(props.error)} />
259
+ {!props.error && (
260
+ <React.Fragment>
261
+ <Divider />
262
+ <RadioButton
263
+ options={explainOptions}
264
+ value={activeOption}
265
+ onUpdate={onSelectOption}
266
+ />
267
+ </React.Fragment>
268
+ )}
269
+ </div>
270
+ <div className={b('controls-left')}>
271
+ <EnableFullscreenButton disabled={Boolean(props.error)} />
272
+ <PaneVisibilityToggleButtons
273
+ onCollapse={props.onCollapseResults}
274
+ onExpand={props.onExpandResults}
275
+ isCollapsed={props.isResultsCollapsed}
276
+ initialDirection="bottom"
264
277
  />
265
- </React.Fragment>
266
- )}
267
- </div>
268
- <div className={b('controls-left')}>
269
- <EnableFullscreenButton disabled={Boolean(props.error)} />
270
- <PaneVisibilityToggleButtons
271
- onCollapse={props.onCollapseResults}
272
- onExpand={props.onExpandResults}
273
- isCollapsed={props.isResultsCollapsed}
274
- initialDirection="bottom"
275
- />
276
- </div>
278
+ </div>
279
+ </React.Fragment>
280
+ )}
277
281
  </div>
278
282
  <div className={b('result')}>{renderContent()}</div>
279
283
  </React.Fragment>
@@ -19,6 +19,7 @@
19
19
  justify-content: space-between;
20
20
  align-items: center;
21
21
 
22
+ height: 53px;
22
23
  padding: 12px 20px;
23
24
 
24
25
  border-bottom: 1px solid var(--yc-color-line-generic);
@@ -28,6 +28,7 @@
28
28
  justify-content: space-between;
29
29
  align-items: center;
30
30
 
31
+ height: 53px;
31
32
  padding: 12px 20px;
32
33
 
33
34
  border-bottom: 1px solid var(--yc-color-line-generic);
@@ -67,7 +67,7 @@ class SchemaViewer extends React.Component {
67
67
  theme="yandex-cloud"
68
68
  data={tableData}
69
69
  columns={columns}
70
- settings={{...DEFAULT_TABLE_SETTINGS, stickyTop: 107}}
70
+ settings={DEFAULT_TABLE_SETTINGS}
71
71
  dynamicRender={true}
72
72
  initialSortOrder={{columnId: SchemaViewerColumns.key, order: DataTable.DESCENDING}}
73
73
  />
@@ -1,5 +1,9 @@
1
1
  import {createRequestActionTypes, createApiRequest} from '../utils';
2
2
  import '../../services/api';
3
+ import {getValueFromLS, parseJson} from '../../utils/utils';
4
+ import {QUERIES_HISTORY_KEY} from '../../utils/constants';
5
+
6
+ const MAXIMUM_QUERIES_IN_HISTORY = 20;
3
7
 
4
8
  const SEND_QUERY = createRequestActionTypes('query', 'SEND_QUERY');
5
9
  const CHANGE_USER_INPUT = 'query/CHANGE_USER_INPUT';
@@ -9,6 +13,10 @@ const GO_TO_NEXT_QUERY = 'query/GO_TO_NEXT_QUERY';
9
13
  const SELECT_RUN_ACTION = 'query/SELECT_RUN_ACTION';
10
14
  const MONACO_HOT_KEY = 'query/MONACO_HOT_KEY';
11
15
 
16
+ const queriesHistoryInitial = parseJson(getValueFromLS(QUERIES_HISTORY_KEY, []));
17
+
18
+ const sliceLimit = queriesHistoryInitial.length - MAXIMUM_QUERIES_IN_HISTORY;
19
+
12
20
  export const RUN_ACTIONS_VALUES = {
13
21
  script: 'execute-script',
14
22
  scan: 'execute-scan',
@@ -25,8 +33,11 @@ const initialState = {
25
33
  loading: false,
26
34
  input: '',
27
35
  history: {
28
- queries: [],
29
- currentIndex: -1,
36
+ queries: queriesHistoryInitial.slice(sliceLimit < 0 ? 0 : sliceLimit),
37
+ currentIndex:
38
+ queriesHistoryInitial.length > MAXIMUM_QUERIES_IN_HISTORY
39
+ ? MAXIMUM_QUERIES_IN_HISTORY - 1
40
+ : queriesHistoryInitial.length - 1,
30
41
  },
31
42
  runAction: RUN_ACTIONS_VALUES.script,
32
43
  monacoHotKey: null,
@@ -76,7 +87,10 @@ const executeQuery = (state = initialState, action) => {
76
87
 
77
88
  case SAVE_QUERY_TO_HISTORY: {
78
89
  const query = action.data;
79
- const newQueries = [...state.history.queries, query];
90
+ const newQueries = [...state.history.queries, query].slice(
91
+ state.history.queries.length >= MAXIMUM_QUERIES_IN_HISTORY ? 1 : 0,
92
+ );
93
+ window.localStorage.setItem(QUERIES_HISTORY_KEY, JSON.stringify(newQueries));
80
94
  const currentIndex = newQueries.length - 1;
81
95
 
82
96
  return {
@@ -6,8 +6,8 @@ import {calcUptime} from '../../utils';
6
6
 
7
7
  export const VisibleEntities = {
8
8
  All: 'All',
9
- Missing: 'Missing',
10
- Space: 'Space',
9
+ Missing: 'Degraded',
10
+ Space: 'Out of space',
11
11
  };
12
12
 
13
13
  export const StorageTypes = {
@@ -120,6 +120,7 @@ export const PROBLEMS = 'With problems';
120
120
 
121
121
  export const THEME_KEY = 'theme';
122
122
  export const SAVED_QUERIES_KEY = 'saved_queries';
123
+ export const QUERIES_HISTORY_KEY = 'queries_history';
123
124
  export const DATA_QA_TUNE_COLUMNS_POPUP = 'tune-columns-popup';
124
125
 
125
126
  export const defaultUserSettings = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "files": [
5
5
  "dist"
6
6
  ],