ydb-embedded-ui 4.5.0 → 4.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/components/CriticalActionDialog/CriticalActionDialog.tsx +3 -0
  3. package/dist/containers/Tablet/Tablet.scss +4 -0
  4. package/dist/containers/Tablet/Tablet.tsx +2 -1
  5. package/dist/containers/Tablet/TabletControls/TabletControls.tsx +19 -27
  6. package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +2 -2
  7. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +37 -38
  8. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +15 -28
  9. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +2 -2
  10. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +5 -3
  11. package/dist/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +2 -3
  12. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +3 -3
  13. package/dist/containers/Tenant/Tenant.tsx +14 -15
  14. package/dist/containers/Tenant/TenantPages.tsx +3 -6
  15. package/dist/containers/Tenant/utils/schemaActions.ts +4 -4
  16. package/dist/routes.ts +1 -1
  17. package/dist/store/reducers/index.ts +1 -1
  18. package/dist/store/reducers/tenant/constants.ts +19 -0
  19. package/dist/store/reducers/{tenant.js → tenant/tenant.ts} +27 -36
  20. package/dist/store/reducers/tenant/types.ts +25 -0
  21. package/dist/utils/index.js +2 -1
  22. package/package.json +2 -1
  23. package/dist/components/Breadcrumbs/Breadcrumbs.js +0 -25
  24. package/dist/components/Breadcrumbs/Breadcrumbs.scss +0 -5
  25. package/dist/components/Collapse/Collapse.js +0 -84
  26. package/dist/components/Collapse/Collapse.scss +0 -70
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.5.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.5.0...v4.5.1) (2023-06-02)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **Tablet:** fetch data on action finish ([#405](https://github.com/ydb-platform/ydb-embedded-ui/issues/405)) ([f1d71c5](https://github.com/ydb-platform/ydb-embedded-ui/commit/f1d71c5af330a0a13246f8d87433e6bba1d3509a))
9
+
3
10
  ## [4.5.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.4.2...v4.5.0) (2023-06-01)
4
11
 
5
12
 
@@ -13,6 +13,7 @@ interface CriticalActionDialogProps {
13
13
  text: string;
14
14
  onClose: VoidFunction;
15
15
  onConfirm: () => Promise<unknown>;
16
+ onConfirmActionFinish: VoidFunction;
16
17
  }
17
18
 
18
19
  export const CriticalActionDialog = ({
@@ -20,6 +21,7 @@ export const CriticalActionDialog = ({
20
21
  text,
21
22
  onClose,
22
23
  onConfirm,
24
+ onConfirmActionFinish,
23
25
  }: CriticalActionDialogProps) => {
24
26
  const [isLoading, setIsLoading] = useState(false);
25
27
 
@@ -28,6 +30,7 @@ export const CriticalActionDialog = ({
28
30
  setIsLoading(true);
29
31
 
30
32
  return onConfirm().then(() => {
33
+ onConfirmActionFinish();
31
34
  setIsLoading(false);
32
35
  onClose();
33
36
  });
@@ -52,6 +52,10 @@
52
52
  text-transform: uppercase;
53
53
  }
54
54
 
55
+ &__loader {
56
+ width: 25px;
57
+ }
58
+
55
59
  .info-viewer__items {
56
60
  grid-template-columns: auto;
57
61
  }
@@ -108,9 +108,10 @@ export const Tablet = () => {
108
108
  <Icon name="external" />
109
109
  </a>
110
110
  {Leader && <Tag text="Leader" type="blue" />}
111
+ <span className={b('loader')}>{loading && <Loader size="s" />}</span>
111
112
  </div>
112
113
  <TabletInfo tablet={tablet} tenantPath={tenantPath} />
113
- <TabletControls tablet={tablet} />
114
+ <TabletControls tablet={tablet} fetchData={fetchData} />
114
115
  </div>
115
116
  <div className={b('rigth-pane')}>
116
117
  <TabletTable history={history} />
@@ -17,18 +17,19 @@ type VisibleDialogType = EVisibleDialogType | null;
17
17
 
18
18
  interface TabletControlsProps {
19
19
  tablet: TTabletStateInfo;
20
+ fetchData: VoidFunction;
20
21
  }
21
22
 
22
- export const TabletControls = ({tablet}: TabletControlsProps) => {
23
+ export const TabletControls = ({tablet, fetchData}: TabletControlsProps) => {
23
24
  const {TabletId, HiveId} = tablet;
24
25
 
25
26
  const [isDialogVisible, setIsDialogVisible] = useState(false);
26
27
  const [visibleDialogType, setVisibleDialogType] = useState<VisibleDialogType>(null);
27
- const [isTabletActionsDisabled, setIsTabletActionsDisabled] = useState(false);
28
+ const [isTabletActionLoading, setIsTabletActionLoading] = useState(false);
28
29
 
29
30
  // Enable controls after data update
30
31
  useEffect(() => {
31
- setIsTabletActionsDisabled(false);
32
+ setIsTabletActionLoading(false);
32
33
  }, [tablet]);
33
34
 
34
35
  const makeShowDialog = (type: VisibleDialogType) => () => {
@@ -46,15 +47,15 @@ export const TabletControls = ({tablet}: TabletControlsProps) => {
46
47
  };
47
48
 
48
49
  const _onKillClick = () => {
49
- setIsTabletActionsDisabled(true);
50
+ setIsTabletActionLoading(true);
50
51
  return window.api.killTablet(TabletId);
51
52
  };
52
53
  const _onStopClick = () => {
53
- setIsTabletActionsDisabled(true);
54
+ setIsTabletActionLoading(true);
54
55
  return window.api.stopTablet(TabletId, HiveId);
55
56
  };
56
57
  const _onResumeClick = () => {
57
- setIsTabletActionsDisabled(true);
58
+ setIsTabletActionLoading(true);
58
59
  return window.api.resumeTablet(TabletId, HiveId);
59
60
  };
60
61
 
@@ -62,25 +63,11 @@ export const TabletControls = ({tablet}: TabletControlsProps) => {
62
63
  return HiveId && HiveId !== '0';
63
64
  };
64
65
 
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
- }
66
+ const isDisabledResume =
67
+ tablet.State !== ETabletState.Stopped && tablet.State !== ETabletState.Dead;
81
68
 
82
- return tablet.State === ETabletState.Stopped || tablet.State === ETabletState.Deleted;
83
- };
69
+ const isDisabledStop =
70
+ tablet.State === ETabletState.Stopped || tablet.State === ETabletState.Deleted;
84
71
 
85
72
  const renderDialog = () => {
86
73
  if (!isDialogVisible) {
@@ -95,6 +82,7 @@ export const TabletControls = ({tablet}: TabletControlsProps) => {
95
82
  text={i18n('dialog.kill')}
96
83
  onClose={hideDialog}
97
84
  onConfirm={_onKillClick}
85
+ onConfirmActionFinish={fetchData}
98
86
  />
99
87
  );
100
88
  }
@@ -105,6 +93,7 @@ export const TabletControls = ({tablet}: TabletControlsProps) => {
105
93
  text={i18n('dialog.stop')}
106
94
  onClose={hideDialog}
107
95
  onConfirm={_onStopClick}
96
+ onConfirmActionFinish={fetchData}
108
97
  />
109
98
  );
110
99
  }
@@ -115,6 +104,7 @@ export const TabletControls = ({tablet}: TabletControlsProps) => {
115
104
  text={i18n('dialog.resume')}
116
105
  onClose={hideDialog}
117
106
  onConfirm={_onResumeClick}
107
+ onConfirmActionFinish={fetchData}
118
108
  />
119
109
  );
120
110
  }
@@ -128,7 +118,7 @@ export const TabletControls = ({tablet}: TabletControlsProps) => {
128
118
  <Button
129
119
  onClick={showKillDialog}
130
120
  view="action"
131
- disabled={isDisabledKill()}
121
+ loading={isTabletActionLoading}
132
122
  className={b('control')}
133
123
  >
134
124
  {i18n('controls.kill')}
@@ -138,7 +128,8 @@ export const TabletControls = ({tablet}: TabletControlsProps) => {
138
128
  <Button
139
129
  onClick={showStopDialog}
140
130
  view="action"
141
- disabled={isDisabledStop()}
131
+ disabled={isDisabledStop}
132
+ loading={!isDisabledStop && isTabletActionLoading}
142
133
  className={b('control')}
143
134
  >
144
135
  {i18n('controls.stop')}
@@ -146,7 +137,8 @@ export const TabletControls = ({tablet}: TabletControlsProps) => {
146
137
  <Button
147
138
  onClick={showResumeDialog}
148
139
  view="action"
149
- disabled={isDisabledResume()}
140
+ disabled={isDisabledResume}
141
+ loading={!isDisabledResume && isTabletActionLoading}
150
142
  className={b('control')}
151
143
  >
152
144
  {i18n('controls.resume')}
@@ -3,13 +3,13 @@ import block from 'bem-cn-lite';
3
3
  import qs from 'qs';
4
4
 
5
5
  import type {IPreparedConsumerData} from '../../../../../types/store/topic';
6
+ import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
6
7
  import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
7
8
  import {InternalLink} from '../../../../../components/InternalLink';
8
9
  import {formatMsToUptime} from '../../../../../utils';
9
10
  import routes, {createHref} from '../../../../../routes';
10
11
 
11
12
  import {TenantTabsGroups} from '../../../TenantPages';
12
- import {GeneralPagesIds} from '../../DiagnosticsPages';
13
13
 
14
14
  import {
15
15
  CONSUMERS_COLUMNS_IDS,
@@ -42,7 +42,7 @@ export const columns: Column<IPreparedConsumerData>[] = [
42
42
  <InternalLink
43
43
  to={createHref(routes.tenant, undefined, {
44
44
  ...queryParams,
45
- [TenantTabsGroups.generalTab]: GeneralPagesIds.partitions,
45
+ [TenantTabsGroups.generalTab]: TENANT_DIAGNOSTICS_TABS_IDS.partitions,
46
46
  selectedConsumer: row.name,
47
47
  })}
48
48
  >
@@ -7,37 +7,36 @@ import {useLocation} from 'react-router';
7
7
 
8
8
  import {Switch, Tabs} from '@gravity-ui/uikit';
9
9
 
10
+ import type {EPathType} from '../../../types/api/schema';
11
+
12
+ import {useTypedSelector} from '../../../utils/hooks';
13
+ import routes, {createHref} from '../../../routes';
14
+ import type {TenantDiagnosticsTab, TenantGeneralTab} from '../../../store/reducers/tenant/types';
15
+ import {enableAutorefresh, disableAutorefresh} from '../../../store/reducers/schema';
16
+ import {setTopLevelTab, setDiagnosticsTab} from '../../../store/reducers/tenant/tenant';
17
+ import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../store/reducers/tenant/constants';
18
+
10
19
  import {Loader} from '../../../components/Loader';
11
20
 
12
- import {TopQueries} from './TopQueries';
13
- //@ts-ignore
14
- import DetailedOverview from './DetailedOverview/DetailedOverview';
15
- import {TopShards} from './TopShards';
16
- //@ts-ignore
17
- import Storage from '../../Storage/Storage';
18
- //@ts-ignore
19
- import Network from './Network/Network';
20
- //@ts-ignore
21
- import Describe from './Describe/Describe';
22
- //@ts-ignore
23
- import HotKeys from './HotKeys/HotKeys';
24
- //@ts-ignore
25
21
  import {Heatmap} from '../../Heatmap';
26
22
  import {Nodes} from '../../Nodes';
27
- //@ts-ignore
23
+ import Storage from '../../Storage/Storage';
28
24
  import {Tablets} from '../../Tablets';
29
- import {Consumers} from './Consumers';
25
+
26
+ import Describe from './Describe/Describe';
27
+ import HotKeys from './HotKeys/HotKeys';
28
+ import Network from './Network/Network';
30
29
  import {Partitions} from './Partitions/Partitions';
30
+ import {Consumers} from './Consumers';
31
+ import {TopQueries} from './TopQueries';
32
+ import {TopShards} from './TopShards';
33
+ import DetailedOverview from './DetailedOverview/DetailedOverview';
31
34
 
32
- import routes, {createHref} from '../../../routes';
33
- import type {EPathType} from '../../../types/api/schema';
34
- import {TenantGeneralTabsIds, TenantTabsGroups} from '../TenantPages';
35
- import {GeneralPagesIds, DATABASE_PAGES, getPagesByType} from './DiagnosticsPages';
36
- //@ts-ignore
37
- import {enableAutorefresh, disableAutorefresh} from '../../../store/reducers/schema';
38
- import {setTopLevelTab, setDiagnosticsTab} from '../../../store/reducers/tenant';
39
35
  import {isDatabaseEntityType} from '../utils/schema';
40
36
 
37
+ import {TenantTabsGroups} from '../TenantPages';
38
+ import {DATABASE_PAGES, getPagesByType} from './DiagnosticsPages';
39
+
41
40
  import './Diagnostics.scss';
42
41
 
43
42
  interface DiagnosticsProps {
@@ -51,8 +50,8 @@ const b = cn('kv-tenant-diagnostics');
51
50
  function Diagnostics(props: DiagnosticsProps) {
52
51
  const dispatch = useDispatch();
53
52
  const {currentSchemaPath, autorefresh} = useSelector((state: any) => state.schema);
54
- const {diagnosticsTab = GeneralPagesIds.overview, wasLoaded} = useSelector(
55
- (state: any) => state.tenant,
53
+ const {diagnosticsTab = TENANT_DIAGNOSTICS_TABS_IDS.overview, wasLoaded} = useTypedSelector(
54
+ (state) => state.tenant,
56
55
  );
57
56
 
58
57
  const location = useLocation();
@@ -73,7 +72,7 @@ function Diagnostics(props: DiagnosticsProps) {
73
72
  return getPagesByType(props.type);
74
73
  }, [props.type, isDatabase]);
75
74
 
76
- const forwardToDiagnosticTab = (tab: GeneralPagesIds) => {
75
+ const forwardToDiagnosticTab = (tab: TenantDiagnosticsTab) => {
77
76
  dispatch(setDiagnosticsTab(tab));
78
77
  };
79
78
  const activeTab = useMemo(() => {
@@ -97,7 +96,7 @@ function Diagnostics(props: DiagnosticsProps) {
97
96
  }
98
97
  };
99
98
 
100
- const forwardToGeneralTab = (tab: TenantGeneralTabsIds) => {
99
+ const forwardToGeneralTab = (tab: TenantGeneralTab) => {
101
100
  dispatch(setTopLevelTab(tab));
102
101
  };
103
102
 
@@ -107,7 +106,7 @@ function Diagnostics(props: DiagnosticsProps) {
107
106
  const tenantNameString = tenantName as string;
108
107
 
109
108
  switch (diagnosticsTab) {
110
- case GeneralPagesIds.overview: {
109
+ case TENANT_DIAGNOSTICS_TABS_IDS.overview: {
111
110
  return (
112
111
  <DetailedOverview
113
112
  type={type}
@@ -116,7 +115,7 @@ function Diagnostics(props: DiagnosticsProps) {
116
115
  />
117
116
  );
118
117
  }
119
- case GeneralPagesIds.topQueries: {
118
+ case TENANT_DIAGNOSTICS_TABS_IDS.topQueries: {
120
119
  return (
121
120
  <TopQueries
122
121
  path={tenantNameString}
@@ -125,10 +124,10 @@ function Diagnostics(props: DiagnosticsProps) {
125
124
  />
126
125
  );
127
126
  }
128
- case GeneralPagesIds.topShards: {
127
+ case TENANT_DIAGNOSTICS_TABS_IDS.topShards: {
129
128
  return <TopShards tenantPath={tenantNameString} type={type} />;
130
129
  }
131
- case GeneralPagesIds.nodes: {
130
+ case TENANT_DIAGNOSTICS_TABS_IDS.nodes: {
132
131
  return (
133
132
  <Nodes
134
133
  path={currentSchemaPath}
@@ -137,28 +136,28 @@ function Diagnostics(props: DiagnosticsProps) {
137
136
  />
138
137
  );
139
138
  }
140
- case GeneralPagesIds.tablets: {
139
+ case TENANT_DIAGNOSTICS_TABS_IDS.tablets: {
141
140
  return <Tablets path={currentSchemaPath} />;
142
141
  }
143
- case GeneralPagesIds.storage: {
142
+ case TENANT_DIAGNOSTICS_TABS_IDS.storage: {
144
143
  return <Storage tenant={tenantNameString} database={true} />;
145
144
  }
146
- case GeneralPagesIds.network: {
145
+ case TENANT_DIAGNOSTICS_TABS_IDS.network: {
147
146
  return <Network path={tenantNameString} />;
148
147
  }
149
- case GeneralPagesIds.describe: {
148
+ case TENANT_DIAGNOSTICS_TABS_IDS.describe: {
150
149
  return <Describe tenant={tenantNameString} type={type} />;
151
150
  }
152
- case GeneralPagesIds.hotKeys: {
151
+ case TENANT_DIAGNOSTICS_TABS_IDS.hotKeys: {
153
152
  return <HotKeys type={type} />;
154
153
  }
155
- case GeneralPagesIds.graph: {
154
+ case TENANT_DIAGNOSTICS_TABS_IDS.graph: {
156
155
  return <Heatmap path={currentSchemaPath} />;
157
156
  }
158
- case GeneralPagesIds.consumers: {
157
+ case TENANT_DIAGNOSTICS_TABS_IDS.consumers: {
159
158
  return <Consumers path={currentSchemaPath} type={type} />;
160
159
  }
161
- case GeneralPagesIds.partitions: {
160
+ case TENANT_DIAGNOSTICS_TABS_IDS.partitions: {
162
161
  return <Partitions path={currentSchemaPath} />;
163
162
  }
164
163
  default: {
@@ -1,80 +1,67 @@
1
+ import type {TenantDiagnosticsTab} from '../../../store/reducers/tenant/types';
2
+ import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../store/reducers/tenant/constants';
1
3
  import {EPathType} from '../../../types/api/schema';
2
4
 
3
- export enum GeneralPagesIds {
4
- 'overview' = 'Overview',
5
- 'topQueries' = 'topQueries',
6
- 'topShards' = 'topShards',
7
- 'nodes' = 'Nodes',
8
- 'tablets' = 'Tablets',
9
- 'storage' = 'Storage',
10
- 'network' = 'Network',
11
- 'describe' = 'Describe',
12
- 'hotKeys' = 'hotKeys',
13
- 'graph' = 'graph',
14
- 'consumers' = 'consumers',
15
- 'partitions' = 'partitions',
16
- }
17
-
18
5
  type Page = {
19
- id: GeneralPagesIds;
6
+ id: TenantDiagnosticsTab;
20
7
  title: string;
21
8
  };
22
9
 
23
10
  const overview = {
24
- id: GeneralPagesIds.overview,
11
+ id: TENANT_DIAGNOSTICS_TABS_IDS.overview,
25
12
  title: 'Info',
26
13
  };
27
14
 
28
15
  const topQueries = {
29
- id: GeneralPagesIds.topQueries,
16
+ id: TENANT_DIAGNOSTICS_TABS_IDS.topQueries,
30
17
  title: 'Top queries',
31
18
  };
32
19
 
33
20
  const topShards = {
34
- id: GeneralPagesIds.topShards,
21
+ id: TENANT_DIAGNOSTICS_TABS_IDS.topShards,
35
22
  title: 'Top shards',
36
23
  };
37
24
 
38
25
  const nodes = {
39
- id: GeneralPagesIds.nodes,
26
+ id: TENANT_DIAGNOSTICS_TABS_IDS.nodes,
40
27
  title: 'Nodes',
41
28
  };
42
29
 
43
30
  const tablets = {
44
- id: GeneralPagesIds.tablets,
31
+ id: TENANT_DIAGNOSTICS_TABS_IDS.tablets,
45
32
  title: 'Tablets',
46
33
  };
47
34
  const storage = {
48
- id: GeneralPagesIds.storage,
35
+ id: TENANT_DIAGNOSTICS_TABS_IDS.storage,
49
36
  title: 'Storage',
50
37
  };
51
38
  const network = {
52
- id: GeneralPagesIds.network,
39
+ id: TENANT_DIAGNOSTICS_TABS_IDS.network,
53
40
  title: 'Network',
54
41
  };
55
42
 
56
43
  const describe = {
57
- id: GeneralPagesIds.describe,
44
+ id: TENANT_DIAGNOSTICS_TABS_IDS.describe,
58
45
  title: 'Describe',
59
46
  };
60
47
 
61
48
  const hotKeys = {
62
- id: GeneralPagesIds.hotKeys,
49
+ id: TENANT_DIAGNOSTICS_TABS_IDS.hotKeys,
63
50
  title: 'Hot keys',
64
51
  };
65
52
 
66
53
  const graph = {
67
- id: GeneralPagesIds.graph,
54
+ id: TENANT_DIAGNOSTICS_TABS_IDS.graph,
68
55
  title: 'Graph',
69
56
  };
70
57
 
71
58
  const consumers = {
72
- id: GeneralPagesIds.consumers,
59
+ id: TENANT_DIAGNOSTICS_TABS_IDS.consumers,
73
60
  title: 'Consumers',
74
61
  };
75
62
 
76
63
  const partitions = {
77
- id: GeneralPagesIds.partitions,
64
+ id: TENANT_DIAGNOSTICS_TABS_IDS.partitions,
78
65
  title: 'Partitions',
79
66
  };
80
67
 
@@ -9,7 +9,7 @@ import InfoViewer from '../../../../components/InfoViewer/InfoViewer';
9
9
  import PoolUsage from '../../../../components/PoolUsage/PoolUsage';
10
10
  import {Tablet} from '../../../../components/Tablet';
11
11
 
12
- import {getTenantInfo} from '../../../../store/reducers/tenant';
12
+ import {getTenantInfo} from '../../../../store/reducers/tenant/tenant';
13
13
 
14
14
  import {formatCPU} from '../../../../utils';
15
15
  import {bytesToGB} from '../../../../utils/utils';
@@ -191,7 +191,7 @@ class TenantOverview extends React.Component {
191
191
  }
192
192
 
193
193
  function mapStateToProps(state) {
194
- const {tenant = {}, loading, data: {status} = {}} = state.tenant;
194
+ const {tenant = {}, loading, error: {status} = {}} = state.tenant;
195
195
  const {autorefresh} = state.schema;
196
196
 
197
197
  return {
@@ -22,7 +22,9 @@ import type {KeyValueRow} from '../../../../types/api/query';
22
22
  import type {EPathType} from '../../../../types/api/schema';
23
23
  import type {ITopQueriesFilters} from '../../../../types/store/executeTopQueries';
24
24
  import type {IQueryResult} from '../../../../types/store/query';
25
+ import type {TenantGeneralTab} from '../../../../store/reducers/tenant/types';
25
26
 
27
+ import {TENANT_GENERAL_TABS_IDS} from '../../../../store/reducers/tenant/constants';
26
28
  import {formatDateTime, formatNumber} from '../../../../utils';
27
29
  import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
28
30
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
@@ -30,7 +32,7 @@ import {prepareQueryError} from '../../../../utils/query';
30
32
  import routes, {createHref} from '../../../../routes';
31
33
 
32
34
  import {isColumnEntityType} from '../../utils/schema';
33
- import {TenantGeneralTabsIds, TenantTabsGroups} from '../../TenantPages';
35
+ import {TenantTabsGroups} from '../../TenantPages';
34
36
 
35
37
  import i18n from './i18n';
36
38
  import './TopQueries.scss';
@@ -86,7 +88,7 @@ const COLUMNS: Column<KeyValueRow>[] = [
86
88
 
87
89
  interface TopQueriesProps {
88
90
  path: string;
89
- changeSchemaTab: (tab: TenantGeneralTabsIds) => void;
91
+ changeSchemaTab: (tab: TenantGeneralTab) => void;
90
92
  type?: EPathType;
91
93
  }
92
94
 
@@ -178,7 +180,7 @@ export const TopQueries = ({path, type}: TopQueriesProps) => {
178
180
 
179
181
  const queryPath = createHref(routes.tenant, undefined, {
180
182
  ...queryParams,
181
- [TenantTabsGroups.general]: TenantGeneralTabsIds.query,
183
+ [TenantTabsGroups.general]: TENANT_GENERAL_TABS_IDS.query,
182
184
  });
183
185
 
184
186
  history.push(queryPath);
@@ -5,12 +5,11 @@ import cn from 'bem-cn-lite';
5
5
  import {useThemeValue} from '@gravity-ui/uikit';
6
6
 
7
7
  import type {EPathType} from '../../../types/api/schema';
8
+ import {TENANT_GENERAL_TABS_IDS} from '../../../store/reducers/tenant/constants';
8
9
 
9
10
  import QueryEditor from '../QueryEditor/QueryEditor';
10
11
  import Diagnostics from '../Diagnostics/Diagnostics';
11
12
 
12
- import {TenantGeneralTabsIds} from '../TenantPages';
13
-
14
13
  import './ObjectGeneral.scss';
15
14
 
16
15
  const b = cn('object-general');
@@ -35,7 +34,7 @@ function ObjectGeneral(props: ObjectGeneralProps) {
35
34
  const renderTabContent = () => {
36
35
  const {type, additionalTenantInfo, additionalNodesInfo} = props;
37
36
  switch (generalTab) {
38
- case TenantGeneralTabsIds.query: {
37
+ case TENANT_GENERAL_TABS_IDS.query: {
39
38
  return <QueryEditor path={tenantName as string} theme={theme} type={type} />;
40
39
  }
41
40
  default: {
@@ -35,7 +35,6 @@ import {
35
35
  DEFAULT_SIZE_TENANT_SUMMARY_KEY,
36
36
  } from '../../../utils/constants';
37
37
  import {
38
- TenantGeneralTabsIds,
39
38
  TenantInfoTabsIds,
40
39
  TenantTabsGroups,
41
40
  TENANT_INFO_TABS,
@@ -48,7 +47,8 @@ import {
48
47
  PaneVisibilityToggleButtons,
49
48
  } from '../utils/paneVisibilityToggleHelpers';
50
49
  import {setShowPreview} from '../../../store/reducers/schema';
51
- import {setTopLevelTab} from '../../../store/reducers/tenant';
50
+ import {setTopLevelTab} from '../../../store/reducers/tenant/tenant';
51
+ import {TENANT_GENERAL_TABS_IDS} from '../../../store/reducers/tenant/constants';
52
52
 
53
53
  import './ObjectSummary.scss';
54
54
 
@@ -275,7 +275,7 @@ function ObjectSummary(props: ObjectSummaryProps) {
275
275
 
276
276
  const onOpenPreview = () => {
277
277
  dispatch(setShowPreview(true));
278
- dispatch(setTopLevelTab(TenantGeneralTabsIds.query));
278
+ dispatch(setTopLevelTab(TENANT_GENERAL_TABS_IDS.query));
279
279
  };
280
280
 
281
281
  const renderCommonInfoControls = () => {
@@ -4,28 +4,27 @@ import cn from 'bem-cn-lite';
4
4
  import {useLocation} from 'react-router';
5
5
  import qs from 'qs';
6
6
 
7
- import {AccessDenied} from '../../components/Errors/403';
7
+ import type {TEvDescribeSchemeResult} from '../../types/api/schema';
8
8
 
9
+ import {DEFAULT_IS_TENANT_SUMMARY_COLLAPSED, DEFAULT_SIZE_TENANT_KEY} from '../../utils/constants';
10
+ import {useTypedSelector} from '../../utils/hooks';
11
+ import routes, {CLUSTER_PAGES, createHref} from '../../routes';
9
12
  import {setHeader} from '../../store/reducers/header';
13
+ import {disableAutorefresh, getSchema, resetLoadingState} from '../../store/reducers/schema';
14
+ import {getSchemaAcl} from '../../store/reducers/schemaAcl';
15
+ import {getTenantInfo, clearTenant} from '../../store/reducers/tenant/tenant';
16
+
17
+ import SplitPane from '../../components/SplitPane';
18
+ import {AccessDenied} from '../../components/Errors/403';
19
+
10
20
  import ObjectGeneralTabs from './ObjectGeneralTabs/ObjectGeneralTabs';
11
21
  import ObjectSummary from './ObjectSummary/ObjectSummary';
12
22
  import ObjectGeneral from './ObjectGeneral/ObjectGeneral';
13
- //@ts-ignore
14
- import SplitPane from '../../components/SplitPane';
15
- //@ts-ignore
16
- import {DEFAULT_IS_TENANT_SUMMARY_COLLAPSED, DEFAULT_SIZE_TENANT_KEY} from '../../utils/constants';
17
- //@ts-ignore
18
- import {disableAutorefresh, getSchema, resetLoadingState} from '../../store/reducers/schema';
19
- //@ts-ignore
20
- import {getSchemaAcl} from '../../store/reducers/schemaAcl';
23
+
21
24
  import {
22
25
  PaneVisibilityActionTypes,
23
26
  paneVisibilityToggleReducerCreator,
24
27
  } from './utils/paneVisibilityToggleHelpers';
25
- //@ts-ignore
26
- import {getTenantInfo, clearTenant} from '../../store/reducers/tenant';
27
- import routes, {CLUSTER_PAGES, createHref} from '../../routes';
28
- import type {TEvDescribeSchemeResult} from '../../types/api/schema';
29
28
 
30
29
  import './Tenant.scss';
31
30
 
@@ -63,8 +62,8 @@ function Tenant(props: TenantProps) {
63
62
  const {PathType: currentPathType, PathSubType: currentPathSubType} =
64
63
  (currentItem as TEvDescribeSchemeResult).PathDescription?.Self || {};
65
64
 
66
- const {data: {status: tenantStatus = 200} = {}} = useSelector((state: any) => state.tenant);
67
- const {error: {status: schemaStatus = 200} = {}} = useSelector((state: any) => state.schema);
65
+ const {error: {status: tenantStatus = 200} = {}} = useTypedSelector((state) => state.tenant);
66
+ const {error: {status: schemaStatus = 200} = {}} = useTypedSelector((state) => state.schema);
68
67
 
69
68
  const dispatch = useDispatch();
70
69
 
@@ -1,9 +1,6 @@
1
1
  import {Icon} from '../../components/Icon';
2
+ import {TENANT_GENERAL_TABS_IDS} from '../../store/reducers/tenant/constants';
2
3
 
3
- export enum TenantGeneralTabsIds {
4
- query = 'query',
5
- diagnostics = 'diagnostics',
6
- }
7
4
  export enum TenantInfoTabsIds {
8
5
  overview = 'overview',
9
6
  acl = 'acl',
@@ -18,12 +15,12 @@ export enum TenantTabsGroups {
18
15
 
19
16
  export const TENANT_GENERAL_TABS = [
20
17
  {
21
- id: TenantGeneralTabsIds.query,
18
+ id: TENANT_GENERAL_TABS_IDS.query,
22
19
  title: 'Query',
23
20
  icon: <Icon name="query" viewBox="0 0 16 16" />,
24
21
  },
25
22
  {
26
- id: TenantGeneralTabsIds.diagnostics,
23
+ id: TENANT_GENERAL_TABS_IDS.diagnostics,
27
24
  title: 'Diagnostics',
28
25
  icon: <Icon name="diagnostics" viewBox="0 0 17 16" />,
29
26
  },
@@ -3,9 +3,9 @@ import type {NavigationTreeNodeType, NavigationTreeProps} from 'ydb-ui-component
3
3
 
4
4
  import {changeUserInput} from '../../../store/reducers/executeQuery';
5
5
  import {setShowPreview} from '../../../store/reducers/schema';
6
- import {setTopLevelTab} from '../../../store/reducers/tenant';
6
+ import {setTopLevelTab} from '../../../store/reducers/tenant/tenant';
7
+ import {TENANT_GENERAL_TABS_IDS} from '../../../store/reducers/tenant/constants';
7
8
  import createToast from '../../../utils/createToast';
8
- import {TenantGeneralTabsIds} from '../TenantPages';
9
9
 
10
10
  const createTableTemplate = (path: string) => {
11
11
  return `CREATE TABLE \`${path}/my_table\`
@@ -37,7 +37,7 @@ const bindActions = (
37
37
  ) => {
38
38
  const inputQuery = (tmpl: (path: string) => string) => () => {
39
39
  dispatch(changeUserInput({input: tmpl(path)}));
40
- dispatch(setTopLevelTab(TenantGeneralTabsIds.query));
40
+ dispatch(setTopLevelTab(TENANT_GENERAL_TABS_IDS.query));
41
41
  setActivePath(path);
42
42
  };
43
43
 
@@ -66,7 +66,7 @@ const bindActions = (
66
66
  },
67
67
  openPreview: () => {
68
68
  dispatch(setShowPreview(true));
69
- dispatch(setTopLevelTab(TenantGeneralTabsIds.query));
69
+ dispatch(setTopLevelTab(TENANT_GENERAL_TABS_IDS.query));
70
70
  setActivePath(path);
71
71
  },
72
72
  };
package/dist/routes.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import qs from 'qs';
2
2
  import {compile} from 'path-to-regexp';
3
3
  import isEmpty from 'lodash/isEmpty';
4
- //@ts-ignore
4
+
5
5
  import {backend, clusterName, webVersion} from './store';
6
6
 
7
7
  const routes = {
@@ -3,7 +3,7 @@ import {combineReducers} from 'redux';
3
3
  import nodes from './nodes';
4
4
  import cluster from './cluster/cluster';
5
5
  import clusterNodes from './clusterNodes/clusterNodes';
6
- import tenant from './tenant';
6
+ import tenant from './tenant/tenant';
7
7
  import storage from './storage';
8
8
  import node from './node';
9
9
  import tooltip from './tooltip';
@@ -0,0 +1,19 @@
1
+ export const TENANT_GENERAL_TABS_IDS = {
2
+ query: 'query',
3
+ diagnostics: 'diagnostics',
4
+ } as const;
5
+
6
+ export const TENANT_DIAGNOSTICS_TABS_IDS = {
7
+ overview: 'overview',
8
+ topQueries: 'topQueries',
9
+ topShards: 'topShards',
10
+ nodes: 'nodes',
11
+ tablets: 'tablets',
12
+ storage: 'storage',
13
+ network: 'network',
14
+ describe: 'describe',
15
+ hotKeys: 'hotKeys',
16
+ graph: 'graph',
17
+ consumers: 'consumers',
18
+ partitions: 'partitions',
19
+ } as const;
@@ -1,13 +1,20 @@
1
- import {createRequestActionTypes, createApiRequest} from '../utils';
2
- import '../../services/api';
3
- import _ from 'lodash';
1
+ import type {Reducer} from 'redux';
2
+
3
+ import type {TTenant} from '../../../types/api/tenant';
4
+ import type {TenantAction, TenantDiagnosticsTab, TenantGeneralTab, TenantState} from './types';
5
+
6
+ import '../../../services/api';
7
+ import {createRequestActionTypes, createApiRequest} from '../../utils';
8
+
9
+ export const FETCH_TENANT = createRequestActionTypes('tenant', 'FETCH_TENANT');
4
10
 
5
- const FETCH_TENANT = createRequestActionTypes('tenant', 'FETCH_TENANT');
6
11
  const SET_TOP_LEVEL_TAB = 'tenant/SET_TOP_LEVEL_TAB';
7
12
  const SET_DIAGNOSTICS_TAB = 'tenant/SET_DIAGNOSTICS_TAB';
8
13
  const CLEAR_TENANT = 'tenant/CLEAR_TENANT';
9
14
 
10
- const tenantReducer = (state = {loading: false, wasLoaded: false, tenant: {}}, action) => {
15
+ const initialState = {loading: false, wasLoaded: false};
16
+
17
+ const tenantReducer: Reducer<TenantState, TenantAction> = (state = initialState, action) => {
11
18
  switch (action.type) {
12
19
  case FETCH_TENANT.REQUEST: {
13
20
  return {
@@ -17,12 +24,9 @@ const tenantReducer = (state = {loading: false, wasLoaded: false, tenant: {}}, a
17
24
  }
18
25
 
19
26
  case FETCH_TENANT.SUCCESS: {
20
- const {tenant, tenantNodes} = action.data;
21
-
22
27
  return {
23
28
  ...state,
24
- tenant,
25
- tenantNodes,
29
+ tenant: action.data,
26
30
  loading: false,
27
31
  wasLoaded: true,
28
32
  error: undefined,
@@ -32,7 +36,7 @@ const tenantReducer = (state = {loading: false, wasLoaded: false, tenant: {}}, a
32
36
  case FETCH_TENANT.FAILURE: {
33
37
  return {
34
38
  ...state,
35
- data: action.error,
39
+ error: action.error,
36
40
  loading: false,
37
41
  wasLoaded: true,
38
42
  };
@@ -41,7 +45,7 @@ const tenantReducer = (state = {loading: false, wasLoaded: false, tenant: {}}, a
41
45
  case CLEAR_TENANT: {
42
46
  return {
43
47
  ...state,
44
- tenant: {},
48
+ tenant: undefined,
45
49
  loading: true,
46
50
  };
47
51
  }
@@ -65,45 +69,32 @@ const tenantReducer = (state = {loading: false, wasLoaded: false, tenant: {}}, a
65
69
  }
66
70
  };
67
71
 
68
- export const clearTenant = () => {
69
- return {type: CLEAR_TENANT};
70
- };
71
-
72
- export const getTenantInfo = ({path}) => {
72
+ export const getTenantInfo = ({path}: {path: string}) => {
73
73
  return createApiRequest({
74
- request: Promise.all([window.api.getTenantInfo({path}), window.api.getCompute(path)]),
74
+ request: window.api.getTenantInfo({path}),
75
75
  actions: FETCH_TENANT,
76
- dataHandler: ([tenantData, nodesData]) => {
77
- const tenant = tenantData.TenantInfo[0];
78
- const tenantNodes = _.map(nodesData?.Tenants[0]?.Nodes, (item) => {
79
- if (item.Host && item.Endpoints) {
80
- const address = _.find(item.Endpoints, {Name: 'http-mon'})?.Address;
81
- return {
82
- id: item.NodeId,
83
- backend: `${item.Host}${address ? address : ''}`,
84
- };
85
- }
86
-
87
- return undefined;
88
- }).filter(Boolean);
89
-
90
- return {tenant, tenantNodes};
76
+ dataHandler: (tenantData): TTenant | undefined => {
77
+ return tenantData.TenantInfo?.[0];
91
78
  },
92
79
  });
93
80
  };
94
81
 
95
- export function setTopLevelTab(tab) {
82
+ export const clearTenant = () => {
83
+ return {type: CLEAR_TENANT} as const;
84
+ };
85
+
86
+ export function setTopLevelTab(tab: TenantGeneralTab) {
96
87
  return {
97
88
  type: SET_TOP_LEVEL_TAB,
98
89
  data: tab,
99
- };
90
+ } as const;
100
91
  }
101
92
 
102
- export function setDiagnosticsTab(tab) {
93
+ export function setDiagnosticsTab(tab: TenantDiagnosticsTab) {
103
94
  return {
104
95
  type: SET_DIAGNOSTICS_TAB,
105
96
  data: tab,
106
- };
97
+ } as const;
107
98
  }
108
99
 
109
100
  export default tenantReducer;
@@ -0,0 +1,25 @@
1
+ import type {IResponseError} from '../../../types/api/error';
2
+ import type {TTenant} from '../../../types/api/tenant';
3
+ import type {ValueOf} from '../../../types/common';
4
+ import type {ApiRequestAction} from '../../utils';
5
+
6
+ import {TENANT_DIAGNOSTICS_TABS_IDS, TENANT_GENERAL_TABS_IDS} from './constants';
7
+ import {FETCH_TENANT, clearTenant, setDiagnosticsTab, setTopLevelTab} from './tenant';
8
+
9
+ export type TenantGeneralTab = ValueOf<typeof TENANT_GENERAL_TABS_IDS>;
10
+ export type TenantDiagnosticsTab = ValueOf<typeof TENANT_DIAGNOSTICS_TABS_IDS>;
11
+
12
+ export interface TenantState {
13
+ loading: boolean;
14
+ wasLoaded: boolean;
15
+ topLevelTab?: TenantGeneralTab;
16
+ diagnosticsTab?: TenantDiagnosticsTab;
17
+ tenant?: TTenant;
18
+ error?: IResponseError;
19
+ }
20
+
21
+ export type TenantAction =
22
+ | ApiRequestAction<typeof FETCH_TENANT, TTenant | undefined, IResponseError>
23
+ | ReturnType<typeof clearTenant>
24
+ | ReturnType<typeof setTopLevelTab>
25
+ | ReturnType<typeof setDiagnosticsTab>;
@@ -97,7 +97,8 @@ export const formatDateTime = (value) => {
97
97
 
98
98
  export const calcUptimeInSeconds = (milliseconds) => {
99
99
  const currentDate = new Date();
100
- return (currentDate - Number(milliseconds)) / 1000;
100
+ const diff = currentDate - Number(milliseconds);
101
+ return diff <= 0 ? 0 : diff / 1000;
101
102
  };
102
103
 
103
104
  export const calcUptime = (milliseconds) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "4.5.0",
3
+ "version": "4.5.1",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -48,6 +48,7 @@
48
48
  "//build:embedded": "echo 'PUBLIC_URL is a setting for create-react-app. Embedded version is built and hosted as is on ydb servers, with no way of knowing the final URL pattern. PUBLIC_URL=. keeps paths to all static relative, allowing servers to handle them as needed'",
49
49
  "build:embedded": "rm -rf build && PUBLIC_URL=. REACT_APP_BACKEND=http://localhost:8765 npm run build",
50
50
  "lint:styles": "stylelint 'src/**/*.scss'",
51
+ "unimported": "npx unimported --no-cache",
51
52
  "package": "rm -rf dist && copyfiles -u 1 'src/**/*' dist",
52
53
  "test": "react-app-rewired test",
53
54
  "eject": "react-scripts eject",
@@ -1,25 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import cn from 'bem-cn-lite';
4
- import {Breadcrumbs as BreadcrumbsUiKit} from '@gravity-ui/uikit';
5
-
6
- import './Breadcrumbs.scss';
7
-
8
- const b = cn('kv-breadcrumbs');
9
-
10
- class Breadcrumbs extends React.Component {
11
- static propTypes = {
12
- items: PropTypes.array,
13
- };
14
-
15
- static defaultProps = {
16
- items: [],
17
- };
18
-
19
- render() {
20
- const {items} = this.props;
21
- return <BreadcrumbsUiKit items={items} firstDisplayedItemsCount={1} className={b()} />;
22
- }
23
- }
24
-
25
- export default Breadcrumbs;
@@ -1,5 +0,0 @@
1
- .kv-breadcrumbs {
2
- padding: 20px 0;
3
-
4
- font-size: var(--yc-text-body-2-font-size);
5
- }
@@ -1,84 +0,0 @@
1
- import {useState, useEffect, useRef} from 'react';
2
- import block from 'bem-cn-lite';
3
- import {ArrowToggle, Button} from '@gravity-ui/uikit';
4
-
5
- import './Collapse.scss';
6
-
7
- const b = block('yc-collapse');
8
-
9
- function useEffectSkipFirst(fn, arr) {
10
- const isFirst = useRef(true);
11
-
12
- useEffect(() => {
13
- if (isFirst.current) {
14
- isFirst.current = false;
15
- return;
16
- }
17
-
18
- return fn();
19
- }, arr);
20
- }
21
-
22
- export const Collapse = ({
23
- title,
24
- children,
25
- arrowView = 'icon',
26
- emptyState = 'No data',
27
- titleSize = 'l',
28
- contentMarginTop = 12,
29
- defaultIsExpand,
30
- onChange,
31
- }) => {
32
- const [isExpand, setIsExpand] = useState(defaultIsExpand);
33
-
34
- const arrowDirection = isExpand ? 'top' : 'bottom';
35
-
36
- const arrowComponent =
37
- arrowView === 'button' ? (
38
- <Button view="flat" className={b('arrow-button')}>
39
- <ArrowToggle className={b('arrow')} direction={arrowDirection} size={20} />
40
- </Button>
41
- ) : (
42
- <ArrowToggle className={b('arrow')} direction={arrowDirection} size={20} />
43
- );
44
-
45
- useEffectSkipFirst(onChange, [isExpand]);
46
-
47
- return (
48
- <div className={b()}>
49
- <div
50
- className={b('panel', {
51
- 'no-data': !children,
52
- })}
53
- onClick={() => {
54
- setIsExpand(!isExpand);
55
- }}
56
- >
57
- {typeof title === 'string' ? (
58
- <h2
59
- className={b('title', {
60
- size: titleSize,
61
- })}
62
- >
63
- {title}
64
- </h2>
65
- ) : (
66
- title
67
- )}
68
-
69
- {children && <div className={b('arrow-wrapper')}>{arrowComponent}</div>}
70
- </div>
71
-
72
- {!children && <h4 className={b('empty-state-title')}>{emptyState}</h4>}
73
-
74
- {children && (
75
- <div
76
- className={b('content', {visible: isExpand})}
77
- style={{marginTop: contentMarginTop}}
78
- >
79
- {children}
80
- </div>
81
- )}
82
- </div>
83
- );
84
- };
@@ -1,70 +0,0 @@
1
- .yc-collapse {
2
- &__panel {
3
- display: inline-flex;
4
- align-items: center;
5
-
6
- cursor: pointer;
7
-
8
- &_no-data {
9
- cursor: default;
10
- }
11
- }
12
-
13
- &__title {
14
- margin: 0;
15
- }
16
-
17
- &__title_secondary {
18
- color: var(--yc-color-text-secondary);
19
- }
20
-
21
- &__title_size {
22
- &_s {
23
- font-size: var(--yc-text-body-1-font-size);
24
- line-height: var(--yc-text-body-1-line-height);
25
- }
26
-
27
- &_m {
28
- font-size: var(--yc-text-body-2-font-size);
29
- line-height: var(--yc-text-body-2-line-height);
30
- }
31
-
32
- &_l {
33
- font-size: var(--yc-text-body-3-font-size);
34
- line-height: var(--yc-text-body-3-line-height);
35
- }
36
- }
37
-
38
- &__arrow-wrapper {
39
- margin-left: 8px;
40
- }
41
-
42
- &__arrow-wrapper_secondary {
43
- color: var(--yc-color-text-secondary);
44
- .button2__text {
45
- color: var(--yc-color-text-secondary);
46
- }
47
- }
48
-
49
- &__content {
50
- display: none;
51
-
52
- &_visible {
53
- display: block;
54
- }
55
- }
56
-
57
- &__empty-state-title {
58
- font-size: var(--yc-text-body-1-font-size);
59
- font-weight: normal;
60
- line-height: var(--yc-text-body-1-line-height);
61
-
62
- opacity: 0.5;
63
- }
64
-
65
- &__arrow-button {
66
- .button2__text {
67
- margin: 0 4px;
68
- }
69
- }
70
- }