ydb-embedded-ui 2.0.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/assets/icons/update-arrow.svg +6 -0
  3. package/dist/components/EntityStatus/EntityStatus.js +37 -10
  4. package/dist/components/EntityStatus/EntityStatus.scss +36 -6
  5. package/dist/components/NodesViewer/NodesViewer.js +1 -0
  6. package/dist/components/ShortyString/ShortyString.tsx +21 -8
  7. package/dist/components/ShortyString/i18n/en.json +10 -0
  8. package/dist/components/ShortyString/i18n/index.ts +11 -0
  9. package/dist/components/ShortyString/i18n/ru.json +10 -0
  10. package/dist/containers/Cluster/Cluster.tsx +3 -3
  11. package/dist/containers/Nodes/Nodes.js +6 -6
  12. package/dist/containers/Storage/StorageFilter/StorageFilter.tsx +1 -0
  13. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +2 -2
  14. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +16 -9
  15. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +18 -5
  16. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +83 -0
  17. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuePreview/IssuePreview.tsx +35 -0
  18. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuePreview/index.ts +1 -0
  19. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/IssuesList.tsx +62 -0
  20. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/index.ts +1 -0
  21. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueViewer.scss +21 -15
  22. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssuesViewer.js +52 -86
  23. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx +74 -0
  24. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/index.ts +1 -0
  25. package/dist/containers/Tenant/Diagnostics/Healthcheck/i18n/en.json +7 -0
  26. package/dist/containers/Tenant/Diagnostics/Healthcheck/i18n/index.ts +11 -0
  27. package/dist/containers/Tenant/Diagnostics/Healthcheck/i18n/ru.json +7 -0
  28. package/dist/containers/Tenant/Diagnostics/Healthcheck/index.ts +1 -0
  29. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +4 -21
  30. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +18 -19
  31. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +1 -0
  32. package/dist/containers/Tenant/Tenant.tsx +2 -2
  33. package/dist/containers/Tenants/Tenants.js +7 -7
  34. package/dist/services/api.d.ts +9 -0
  35. package/dist/types/api/healthcheck.ts +91 -0
  36. package/dist/types/store/healthcheck.ts +3 -0
  37. package/dist/utils/hooks/index.ts +1 -0
  38. package/dist/utils/hooks/useAutofetcher.ts +29 -0
  39. package/package.json +4 -1
  40. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.js +0 -195
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.2.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.1.0...v2.2.0) (2022-10-14)
4
+
5
+
6
+ ### Features
7
+
8
+ * **Healthcheck:** rework issues list in modal ([e7cb0df](https://github.com/ydb-platform/ydb-embedded-ui/commit/e7cb0df58e22c8c9cd25aae83b78be4808e9ba81))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **EntityStatus:** enable component to left trim links ([fbc6c51](https://github.com/ydb-platform/ydb-embedded-ui/commit/fbc6c51f9fbea3c1a7f5f70cb542971a41f4d8b3))
14
+ * fix pre-commit prettier linting and add json linting ([#189](https://github.com/ydb-platform/ydb-embedded-ui/issues/189)) ([047415d](https://github.com/ydb-platform/ydb-embedded-ui/commit/047415d2d69ecf4a2d99f0092b9e6735bd8efbc0))
15
+ * **Healthcheck:** delete unneeded i18n translations ([0c6de90](https://github.com/ydb-platform/ydb-embedded-ui/commit/0c6de9031607e4cde1387387393a9cfc9e1e2b8f))
16
+ * **Healthcheck:** enable update button in modal to fetch data ([de0b06e](https://github.com/ydb-platform/ydb-embedded-ui/commit/de0b06e7f2d3536df1b3896cbf86a947b2e7a291))
17
+ * **Healthcheck:** fix layout shift on scrollbar appearance ([ccdde6e](https://github.com/ydb-platform/ydb-embedded-ui/commit/ccdde6e065abbdb1c22a2c3bdd17e63f706d0f77))
18
+ * **Healthcheck:** fix styles for long issues trees ([32f1a8d](https://github.com/ydb-platform/ydb-embedded-ui/commit/32f1a8db58d9f84073327b92dcd80a5b4626a526))
19
+ * **Healthcheck:** fix variable typo ([0f0e056](https://github.com/ydb-platform/ydb-embedded-ui/commit/0f0e056576b9ec18fc3ce574d3742d55e5da6e35))
20
+ * **Healthcheck:** full check status in a preview ([bc0b51e](https://github.com/ydb-platform/ydb-embedded-ui/commit/bc0b51eedd4ff3b4ae1650946832f463a6703c12))
21
+ * **Healthcheck:** make modal show only one first level issue ([cdc95a7](https://github.com/ydb-platform/ydb-embedded-ui/commit/cdc95a7412c1266d990df7e2807630a8f4c88780))
22
+ * **Healthcheck:** redesign healthcheck header ([867f57a](https://github.com/ydb-platform/ydb-embedded-ui/commit/867f57aed84b7b72c22a816c6ac02387490ff495))
23
+ * **Healthcheck:** replace update button with icon ([709a994](https://github.com/ydb-platform/ydb-embedded-ui/commit/709a994544f068db1b0fe09009ecb4d8db46fc38))
24
+ * **Healthcheck:** update styles to be closer to the design ([aa1083d](https://github.com/ydb-platform/ydb-embedded-ui/commit/aa1083d299e24590336eeb3d913a9c53fd77bad6))
25
+ * **Nodes:** case insensitive search ([11d2c98](https://github.com/ydb-platform/ydb-embedded-ui/commit/11d2c985e0c30bb74ed07e22273d8b3459b54c89))
26
+ * **QueryEditor:** smarter error message trim ([8632948](https://github.com/ydb-platform/ydb-embedded-ui/commit/863294828090dc8eb2595884283d0996156c3785))
27
+ * **Tenants:** case insensitive search ([0ad93f5](https://github.com/ydb-platform/ydb-embedded-ui/commit/0ad93f57dcbba7d9746be54a4ba7b76ab4d45108))
28
+ * **Tenants:** fix filtering by ControlPlane name ([4941c82](https://github.com/ydb-platform/ydb-embedded-ui/commit/4941c821cdbb7c5d0da26a3b0d5c00d8979401c0))
29
+ * **Tenants:** left trim db names in db list ([81bf0fa](https://github.com/ydb-platform/ydb-embedded-ui/commit/81bf0fafe901d3601dc04fdf71939e914493ff1c))
30
+
31
+ ## [2.1.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.0.0...v2.1.0) (2022-10-04)
32
+
33
+
34
+ ### Features
35
+
36
+ * autofocus all text search fields ([a38ee84](https://github.com/ydb-platform/ydb-embedded-ui/commit/a38ee84abad4202f5e9b8af897eb68d2c006233a))
37
+ * **Healthcheck:** display first level issues in overview ([10b4bf5](https://github.com/ydb-platform/ydb-embedded-ui/commit/10b4bf5d15d32f028702ff8cfecca0e06bc5616f))
38
+
39
+
40
+ ### Bug Fixes
41
+
42
+ * fix production assets paths ([8eaad0f](https://github.com/ydb-platform/ydb-embedded-ui/commit/8eaad0f1db109c4cf3cbf7d11ad32ea335a6b0c1))
43
+ * **Healthcheck:** add translations ([75f9851](https://github.com/ydb-platform/ydb-embedded-ui/commit/75f9851a35766ef692805a6f154d40340b003487))
44
+ * move eslint hooks rule extension to src config ([179b81d](https://github.com/ydb-platform/ydb-embedded-ui/commit/179b81d60adf422addc8d72f947800c72bd3e4c5))
45
+ * **QueryEditor:** disable fullscreen button for empty result ([4825b5b](https://github.com/ydb-platform/ydb-embedded-ui/commit/4825b5b8dcb89fcafd828dabbace521ddc429922))
46
+ * **QueryEditor:** fix query stats spacings ([b836d72](https://github.com/ydb-platform/ydb-embedded-ui/commit/b836d72824a791b3fde2b9e4585c6c9b42385265))
47
+ * **useAutofetcher:** private autofetcher instance for each usage ([3f34b7a](https://github.com/ydb-platform/ydb-embedded-ui/commit/3f34b7aee2042562a42e6d1a7daf03ffddd888c0))
48
+
3
49
  ## [2.0.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.14.2...v2.0.0) (2022-09-26)
4
50
 
5
51
 
@@ -0,0 +1,6 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M21 12C21 16.9706 16.9706 21 12 21C9.5 21 6.5 19 5 17" stroke='currentColor' stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M5 21L5 17L9 17" stroke='currentColor' stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
4
+ <path d="M3 12C3 7.02944 7.02944 3 12 3C14.5 3 17.5 5 19 7" stroke='currentColor' stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
5
+ <path d="M19 3L19 7L15 7" stroke='currentColor' stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
6
+ </svg>
@@ -2,10 +2,22 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cn from 'bem-cn-lite';
4
4
  import {Link} from 'react-router-dom';
5
- import {ClipboardButton, Link as ExternalLink, Button} from '@gravity-ui/uikit';
5
+ import {ClipboardButton, Link as UIKitLink, Button, Icon} from '@gravity-ui/uikit';
6
+
7
+ import circleInfoIcon from '../../assets/icons/circle-info.svg';
8
+ import circleExclamationIcon from '../../assets/icons/circle-exclamation.svg';
9
+ import triangleExclamationIcon from '../../assets/icons/triangle-exclamation.svg';
10
+ import circleTimesIcon from '../../assets/icons/circle-xmark.svg';
6
11
 
7
12
  import './EntityStatus.scss';
8
13
 
14
+ const icons = {
15
+ BLUE: circleInfoIcon,
16
+ YELLOW: circleExclamationIcon,
17
+ ORANGE: triangleExclamationIcon,
18
+ RED: circleTimesIcon,
19
+ };
20
+
9
21
  const b = cn('entity-status');
10
22
 
11
23
  class EntityStatus extends React.Component {
@@ -22,6 +34,8 @@ class EntityStatus extends React.Component {
22
34
  showStatus: PropTypes.bool,
23
35
  externalLink: PropTypes.bool,
24
36
  className: PropTypes.string,
37
+ mode: PropTypes.oneOf(['color', 'icons']),
38
+ withLeftTrim: PropTypes.bool,
25
39
  };
26
40
 
27
41
  static defaultProps = {
@@ -31,35 +45,47 @@ class EntityStatus extends React.Component {
31
45
  label: '',
32
46
  showStatus: true,
33
47
  externalLink: false,
48
+ mode: 'color',
49
+ withLeftTrim: false,
34
50
  };
35
51
  renderIcon() {
36
- const {status, size, showStatus} = this.props;
52
+ const {status, size, showStatus, mode} = this.props;
37
53
 
38
54
  if (!showStatus) {
39
55
  return null;
40
56
  }
41
57
 
42
- return <div className={b('status-icon', {state: status.toLowerCase(), size})} />;
58
+ const modifiers = {state: status.toLowerCase(), size};
59
+
60
+ if (mode === 'icons' && icons[status]) {
61
+ return <Icon className={b('status-icon', modifiers)} data={icons[status]} />;
62
+ }
63
+
64
+ return <div className={b('status-color', modifiers)} />;
43
65
  }
44
66
  renderStatusLink() {
45
67
  const {iconPath} = this.props;
46
68
 
47
69
  return (
48
- <ExternalLink target="_blank" href={iconPath}>
70
+ <UIKitLink target="_blank" href={iconPath}>
49
71
  {this.renderIcon()}
50
- </ExternalLink>
72
+ </UIKitLink>
51
73
  );
52
74
  }
53
75
  renderLink() {
54
76
  const {externalLink, name, path, onNameMouseEnter, onNameMouseLeave} = this.props;
55
77
 
56
78
  if (externalLink) {
57
- return <ExternalLink href={path}>{name}</ExternalLink>;
79
+ return (
80
+ <UIKitLink className={b('name')} href={path}>
81
+ {name}
82
+ </UIKitLink>
83
+ );
58
84
  }
59
85
 
60
86
  return path ? (
61
87
  <Link
62
- title={name}
88
+ className={b('name')}
63
89
  to={path}
64
90
  onMouseEnter={onNameMouseEnter}
65
91
  onMouseLeave={onNameMouseLeave}
@@ -70,7 +96,6 @@ class EntityStatus extends React.Component {
70
96
  name && (
71
97
  <span
72
98
  className={b('name')}
73
- title={name}
74
99
  onMouseEnter={onNameMouseEnter}
75
100
  onMouseLeave={onNameMouseLeave}
76
101
  >
@@ -83,14 +108,16 @@ class EntityStatus extends React.Component {
83
108
  const {name, label, iconPath, hasClipboardButton, className} = this.props;
84
109
 
85
110
  return (
86
- <div className={b(null, className)}>
111
+ <div className={b(null, className)} title={name}>
87
112
  {iconPath ? this.renderStatusLink() : this.renderIcon()}
88
113
  {label && (
89
114
  <span title={label} className={b('label')}>
90
115
  {label}
91
116
  </span>
92
117
  )}
93
- {this.renderLink()}
118
+ <span className={b('link', {'with-left-trim': this.props.withLeftTrim})}>
119
+ {this.renderLink()}
120
+ </span>
94
121
  {hasClipboardButton && (
95
122
  <Button
96
123
  component="span"
@@ -31,11 +31,7 @@
31
31
  }
32
32
 
33
33
  a {
34
- overflow: hidden;
35
-
36
- white-space: nowrap;
37
34
  text-decoration: none;
38
- text-overflow: ellipsis;
39
35
 
40
36
  color: var(--yc-color-text-link);
41
37
  }
@@ -53,30 +49,49 @@
53
49
  color: var(--yc-color-text-complementary);
54
50
  }
55
51
 
56
- &__name {
52
+ &__link {
53
+ overflow: hidden;
54
+
57
55
  white-space: nowrap;
56
+ text-overflow: ellipsis;
57
+ }
58
+
59
+ &__link_with-left-trim {
60
+ direction: rtl;
61
+
62
+ .entity-status__name {
63
+ unicode-bidi: plaintext;
64
+ }
58
65
  }
59
66
 
67
+ &__status-color,
60
68
  &__status-icon {
69
+ flex-shrink: 0;
70
+
61
71
  margin-right: 8px;
62
72
 
63
73
  border-radius: 3px;
64
74
  &_size_xs {
65
75
  aspect-ratio: 1;
66
76
 
77
+ width: 12px;
67
78
  height: 12px;
68
79
  }
69
80
  &_size_s {
70
81
  aspect-ratio: 1;
71
82
 
83
+ width: 16px;
72
84
  height: 16px;
73
85
  }
74
86
  &_size_m {
75
87
  aspect-ratio: 1;
76
88
 
89
+ width: 18px;
77
90
  height: 18px;
78
91
  }
92
+ }
79
93
 
94
+ &__status-color {
80
95
  &_state_running,
81
96
  &_state_green {
82
97
  background-color: var(--yc-color-infographics-positive-heavy);
@@ -96,7 +111,22 @@
96
111
  background: var(--yc-color-text-complementary);
97
112
  }
98
113
  &_state_orange {
99
- background: var(--yc-color-text-warning-heavy);
114
+ background: var(--yc-color-base-warning-orange);
115
+ }
116
+ }
117
+
118
+ &__status-icon {
119
+ &_state_blue {
120
+ color: var(--yc-color-infographics-info-heavy);
121
+ }
122
+ &_state_yellow {
123
+ color: var(--yc-color-infographics-warning-heavy);
124
+ }
125
+ &_state_orange {
126
+ color: var(--yc-color-base-warning-orange);
127
+ }
128
+ &_state_red {
129
+ color: var(--yc-color-infographics-danger-heavy);
100
130
  }
101
131
  }
102
132
 
@@ -106,6 +106,7 @@ class NodesViewer extends React.PureComponent {
106
106
  text={searchQuery}
107
107
  onUpdate={handleSearchQuery}
108
108
  hasClear
109
+ autoFocus
109
110
  />
110
111
  <ProblemFilter value={filter} onChange={this.onChangeProblemFilter} />
111
112
  <Label theme="info" size="m">{`Nodes: ${nodesToShow.length}`}</Label>
@@ -3,6 +3,7 @@ import cn from 'bem-cn-lite';
3
3
 
4
4
  import {Link} from '@gravity-ui/uikit';
5
5
 
6
+ import i18n from './i18n';
6
7
  import './ShortyString.scss';
7
8
 
8
9
  const block = cn('kv-shorty-string');
@@ -10,6 +11,8 @@ const block = cn('kv-shorty-string');
10
11
  type Props = {
11
12
  value?: string;
12
13
  limit?: number;
14
+ /** in strict mode the text always trims at the limit, otherwise it is allowed to overflow a little */
15
+ strict?: boolean;
13
16
  displayLength?: boolean;
14
17
  render?: (value: string) => React.ReactNode;
15
18
  onToggle?: () => void;
@@ -20,19 +23,29 @@ type Props = {
20
23
  export default function ShortyString({
21
24
  value = '',
22
25
  limit = 200,
26
+ strict = false,
23
27
  displayLength = true,
24
28
  render = (v: string) => v,
25
29
  onToggle,
26
- expandLabel = 'Show more',
27
- collapseLabel = 'Show less',
30
+ expandLabel = i18n('default_expand_label'),
31
+ collapseLabel = i18n('default_collapse_label'),
28
32
  }: Props) {
29
33
  const [expanded, setExpanded] = React.useState(false);
30
- const hasToggle = value.length > limit;
31
- const length =
32
- displayLength && !expanded ? `(${value.length} symbols)` : undefined;
33
34
 
34
- const text = expanded || value.length <= limit ? value : value.slice(0, limit - 4) + '\u00a0...';
35
- const label = expanded ? collapseLabel : expandLabel;
35
+ const toggleLabelAction = expanded ? collapseLabel : expandLabel;
36
+ const toggleLabelSymbolsCount = displayLength && !expanded
37
+ ? i18n('chars_count', {count: value.length})
38
+ : '';
39
+ const toggleLabel = toggleLabelAction + toggleLabelSymbolsCount;
40
+
41
+ // showing toogle button with a label that is longer than the hidden part is pointless,
42
+ // hence compare to limit + length in the not-strict mode
43
+ const hasToggle = value.length > limit + (strict ? 0 : toggleLabel.length);
44
+
45
+ const text = expanded || !hasToggle
46
+ ? value
47
+ : value.slice(0, limit - 4) + '\u00a0...';
48
+
36
49
  return (
37
50
  <div className={block()}>
38
51
  {render(text)}
@@ -45,7 +58,7 @@ export default function ShortyString({
45
58
  onToggle?.();
46
59
  }}
47
60
  >
48
- {label} {length}
61
+ {toggleLabel}
49
62
  </Link>
50
63
  ) : null}
51
64
  </div>
@@ -0,0 +1,10 @@
1
+ {
2
+ "default_collapse_label": "Show less",
3
+ "default_expand_label": "Show more",
4
+ "chars_count": [
5
+ " ({{count}} symbol)",
6
+ " ({{count}} symbols)",
7
+ " ({{count}} symbols)",
8
+ " ({{count}} symbols)"
9
+ ]
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-shorty-string';
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
+ "default_collapse_label": "Показать меньше",
3
+ "default_expand_label": "Показать ещё",
4
+ "chars_count": [
5
+ " ({{count}} символ)",
6
+ " ({{count}} символа)",
7
+ " ({{count}} символов)",
8
+ " ({{count}} символов)"
9
+ ]
10
+ }
@@ -14,9 +14,9 @@ import ClusterInfo from '../../components/ClusterInfo/ClusterInfo';
14
14
  const b = cn('cluster');
15
15
 
16
16
  interface ClusterProps {
17
- additionalClusterInfo: any;
18
- additionalTenantsInfo: any;
19
- additionalNodesInfo: any;
17
+ additionalClusterInfo?: any;
18
+ additionalTenantsInfo?: any;
19
+ additionalNodesInfo?: any;
20
20
  }
21
21
 
22
22
  function Cluster(props: ClusterProps) {
@@ -87,6 +87,7 @@ class Nodes extends React.Component {
87
87
  text={searchQuery}
88
88
  onUpdate={this.handleSearchQueryChange}
89
89
  hasClear
90
+ autoFocus
90
91
  />
91
92
  <ProblemFilter value={filter} onChange={this.handleFilterChange} />
92
93
  <Label theme="info" size="m">{`Nodes: ${nodes?.length}`}</Label>
@@ -113,11 +114,10 @@ class Nodes extends React.Component {
113
114
  });
114
115
 
115
116
  let preparedNodes = searchQuery
116
- ? nodes.filter((node) =>
117
- node.Host
118
- ? node.Host.includes(searchQuery) || String(node.NodeId).includes(searchQuery)
119
- : true,
120
- )
117
+ ? nodes.filter((node) => {
118
+ const re = new RegExp(searchQuery, 'i');
119
+ return node.Host ? re.test(node.Host) || re.test(String(node.NodeId)) : true;
120
+ })
121
121
  : nodes;
122
122
  preparedNodes = preparedNodes.map((node) => ({
123
123
  ...node,
@@ -142,7 +142,7 @@ class Nodes extends React.Component {
142
142
  columnId: 'NodeId',
143
143
  order: DataTable.ASCENDING,
144
144
  }}
145
- emptyDataMessage='No such nodes'
145
+ emptyDataMessage="No such nodes"
146
146
  />
147
147
  </div>
148
148
  </div>
@@ -47,6 +47,7 @@ export const StorageFilter = (props: StorageFilterProps) => {
47
47
  value={filterValue}
48
48
  onUpdate={changeFilter}
49
49
  hasClear
50
+ autoFocus
50
51
  />
51
52
  );
52
53
  }
@@ -19,8 +19,8 @@ $section-title-line-height: 24px;
19
19
 
20
20
  &__close-modal-button {
21
21
  position: absolute;
22
- top: 10px;
23
- right: 10px;
22
+ top: 23px;
23
+ right: 13px;
24
24
 
25
25
  & .yc-button__text {
26
26
  display: flex;
@@ -8,8 +8,7 @@ import type {EPathType} from '../../../../types/api/schema';
8
8
  //@ts-ignore
9
9
  import Icon from '../../../../components/Icon/Icon';
10
10
  import Overview from '../Overview/Overview';
11
- //@ts-ignore
12
- import Healthcheck from '../Healthcheck/Healthcheck';
11
+ import {Healthcheck} from '../Healthcheck';
13
12
  //@ts-ignore
14
13
  import TenantOverview from '../TenantOverview/TenantOverview';
15
14
 
@@ -27,11 +26,12 @@ const b = cn('kv-detailed-overview');
27
26
  function DetailedOverview(props: DetailedOverviewProps) {
28
27
  const [isModalVisible, setIsModalVisible] = useState(false);
29
28
 
30
- const {
31
- currentSchemaPath,
32
- } = useSelector((state: any) => state.schema);
29
+ const [expandedIssueId, setExpandedIssueId] = useState<string>();
30
+
31
+ const {currentSchemaPath} = useSelector((state: any) => state.schema);
33
32
 
34
- const openModalHandler = () => {
33
+ const openModalHandler = (id: string) => {
34
+ setExpandedIssueId(id);
35
35
  setIsModalVisible(true);
36
36
  };
37
37
 
@@ -42,12 +42,16 @@ function DetailedOverview(props: DetailedOverviewProps) {
42
42
  const renderModal = () => {
43
43
  return (
44
44
  <Modal open={isModalVisible} onClose={closeModalHandler} className={b('modal')}>
45
- <Healthcheck tenant={props.tenantName} />
45
+ <Healthcheck
46
+ tenant={props.tenantName}
47
+ fetchData={false}
48
+ expandedIssueId={expandedIssueId}
49
+ />
46
50
  <Button
47
51
  className={b('close-modal-button')}
48
52
  onClick={closeModalHandler}
49
53
  view="flat-secondary"
50
- title='Close'
54
+ title="Close"
51
55
  >
52
56
  <Icon name="close" viewBox={'0 0 16 16 '} height={20} width={20} />
53
57
  </Button>
@@ -63,7 +67,10 @@ function DetailedOverview(props: DetailedOverviewProps) {
63
67
  {isTenant ? (
64
68
  <>
65
69
  <div className={b('section')}>
66
- <TenantOverview tenantName={tenantName} additionalTenantInfo={additionalTenantInfo} />
70
+ <TenantOverview
71
+ tenantName={tenantName}
72
+ additionalTenantInfo={additionalTenantInfo}
73
+ />
67
74
  </div>
68
75
  <div className={b('section')}>
69
76
  <Healthcheck
@@ -1,8 +1,19 @@
1
1
  @use '../DetailedOverview/DetailedOverview.scss' as detailedOverview;
2
2
  @import '../../../../styles/mixins.scss';
3
+ @import '@gravity-ui/uikit/styles/mixins.scss';
3
4
 
4
5
  .healthcheck {
5
- padding: 25px 20px 20px;
6
+ // Since most of the inner containers have fixed width, we can set fixed width here as well
7
+ // Thus we will get rid of unneeded layout shift when scrollbar appear
8
+ min-width: 885px;
9
+
10
+ &__issues-list {
11
+ padding: 25px 20px 20px;
12
+ }
13
+
14
+ &__issue-preview {
15
+ margin-bottom: 15px;
16
+ }
6
17
 
7
18
  &__issues {
8
19
  overflow-x: hidden;
@@ -21,19 +32,21 @@
21
32
  padding: 15px 0;
22
33
  }
23
34
 
24
- &__self-check-status {
35
+ &__issues-list-header {
25
36
  display: flex;
26
37
  align-items: center;
27
38
 
28
39
  margin-bottom: 20px;
29
40
  }
30
41
 
31
- &__self-check-status-label {
42
+ &__issues-list-header-title {
32
43
  margin: 0 10px 0 0;
44
+
45
+ @include text-header-1();
33
46
  }
34
47
 
35
- &__self-check-update {
36
- margin-left: 20px;
48
+ &__issues-list-header-update {
49
+ margin-left: 10px;
37
50
  }
38
51
 
39
52
  &__status-wrapper {
@@ -0,0 +1,83 @@
1
+ import {useCallback} from 'react';
2
+ import {useDispatch, useSelector} from 'react-redux';
3
+ import cn from 'bem-cn-lite';
4
+
5
+ import {Loader} from '@gravity-ui/uikit';
6
+
7
+ import {getHealthcheckInfo} from '../../../../store/reducers/healthcheckInfo';
8
+ import {useAutofetcher} from '../../../../utils/hooks';
9
+
10
+ import {IssuesList} from './IssuesList';
11
+ import {Preview} from './Preview';
12
+
13
+ import i18n from './i18n';
14
+ import './Healthcheck.scss';
15
+
16
+ interface HealthcheckProps {
17
+ tenant: string;
18
+ preview?: boolean;
19
+ fetchData?: boolean;
20
+ expandedIssueId?: string;
21
+ showMoreHandler?: (id: string) => void;
22
+ }
23
+
24
+ const b = cn('healthcheck');
25
+
26
+ export const Healthcheck = (props: HealthcheckProps) => {
27
+ const {tenant, preview, fetchData = true, showMoreHandler, expandedIssueId} = props;
28
+
29
+ const dispatch = useDispatch();
30
+
31
+ const {data, loading, wasLoaded, error} = useSelector((state: any) => state.healthcheckInfo);
32
+ const {autorefresh} = useSelector((state: any) => state.schema);
33
+
34
+ const fetchHealthcheck = useCallback(() => {
35
+ dispatch(getHealthcheckInfo(tenant));
36
+ }, [dispatch, tenant]);
37
+
38
+ useAutofetcher(
39
+ () => {
40
+ if (fetchData) {
41
+ fetchHealthcheck();
42
+ }
43
+ },
44
+ [fetchData, fetchHealthcheck],
45
+ autorefresh,
46
+ );
47
+
48
+ const renderContent = () => {
49
+ if (error) {
50
+ return error.statusText;
51
+ }
52
+
53
+ if (loading && !wasLoaded) {
54
+ return (
55
+ <div className={b('loader')}>
56
+ <Loader size="m" />
57
+ </div>
58
+ );
59
+ }
60
+
61
+ if (data && data['self_check_result']) {
62
+ return preview ? (
63
+ <Preview
64
+ data={data}
65
+ loading={loading}
66
+ onShowMore={showMoreHandler}
67
+ onUpdate={fetchHealthcheck}
68
+ />
69
+ ) : (
70
+ <IssuesList
71
+ data={data}
72
+ expandedIssueId={expandedIssueId}
73
+ loading={loading}
74
+ onUpdate={fetchHealthcheck}
75
+ />
76
+ );
77
+ }
78
+
79
+ return <div className="error">{i18n('no-data')}</div>;
80
+ };
81
+
82
+ return <div className={b()}>{renderContent()}</div>;
83
+ };
@@ -0,0 +1,35 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import {Link, Text} from '@gravity-ui/uikit';
4
+
5
+ import EntityStatus from '../../../../../components/EntityStatus/EntityStatus';
6
+ import {IssueLog} from '../../../../../types/api/healthcheck';
7
+
8
+ import i18n from '../i18n';
9
+
10
+ const b = cn('healthcheck');
11
+
12
+ interface IssuePreviewProps {
13
+ data?: IssueLog;
14
+ onShowMore?: (id: string) => void;
15
+ }
16
+
17
+ export const IssuePreview = (props: IssuePreviewProps) => {
18
+ const {data, onShowMore} = props;
19
+
20
+ if (!data) {
21
+ return null;
22
+ }
23
+
24
+ return (
25
+ <div className={b('issue-preview')}>
26
+ <EntityStatus mode="icons" status={data.status} name={data.type} />
27
+ <Text as="div" color="secondary" variant="body-2">
28
+ {data.message}
29
+ </Text>
30
+ <Link onClick={() => onShowMore && onShowMore(data.id)}>
31
+ {i18n('label.show-details')}
32
+ </Link>
33
+ </div>
34
+ );
35
+ };
@@ -0,0 +1 @@
1
+ export * from './IssuePreview';