ydb-embedded-ui 4.17.0 → 4.18.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 (60) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/components/FullNodeViewer/FullNodeViewer.tsx +2 -2
  3. package/dist/components/InfoViewer/formatters/common.ts +1 -1
  4. package/dist/components/InfoViewer/formatters/pqGroup.ts +1 -1
  5. package/dist/components/InfoViewer/formatters/table.ts +6 -1
  6. package/dist/components/ProgressViewer/ProgressViewer.tsx +99 -0
  7. package/dist/components/TooltipsContent/TabletTooltipContent/TabletTooltipContent.tsx +1 -1
  8. package/dist/containers/AsideNavigation/AsideNavigation.tsx +3 -1
  9. package/dist/containers/Authentication/Authentication.tsx +14 -4
  10. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +2 -2
  11. package/dist/containers/Heatmap/Heatmap.tsx +1 -1
  12. package/dist/containers/Heatmap/Histogram/Histogram.js +1 -1
  13. package/dist/containers/Node/NodeStructure/Pdisk.tsx +2 -2
  14. package/dist/containers/Node/NodeStructure/Vdisk.tsx +5 -2
  15. package/dist/containers/Nodes/getNodesColumns.tsx +2 -2
  16. package/dist/containers/Storage/PDisk/PDisk.tsx +1 -1
  17. package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +1 -1
  18. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +1 -1
  19. package/dist/containers/Storage/VDisk/VDisk.tsx +1 -1
  20. package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +1 -1
  21. package/dist/containers/Tablet/TabletInfo/TabletInfo.tsx +1 -1
  22. package/dist/containers/Tablet/TabletTable/TabletTable.tsx +1 -1
  23. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx +1 -1
  24. package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +1 -1
  25. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +3 -3
  26. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +1 -1
  27. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +1 -1
  28. package/dist/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx +1 -1
  29. package/dist/containers/Tenant/Diagnostics/TenantOverview/OldTenantOverview.tsx +1 -1
  30. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +1 -1
  31. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +1 -1
  32. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +1 -1
  33. package/dist/containers/Tenants/Tenants.tsx +5 -1
  34. package/dist/containers/UserSettings/i18n/en.json +1 -1
  35. package/dist/containers/UserSettings/i18n/ru.json +1 -1
  36. package/dist/containers/UserSettings/settings.ts +2 -2
  37. package/dist/containers/Versions/NodesTable/NodesTable.tsx +2 -2
  38. package/dist/services/api.ts +1 -1
  39. package/dist/store/reducers/clusterNodes/clusterNodes.tsx +1 -1
  40. package/dist/store/reducers/node/selectors.ts +1 -1
  41. package/dist/store/reducers/nodes/selectors.ts +1 -1
  42. package/dist/store/reducers/nodes/types.ts +1 -1
  43. package/dist/store/reducers/nodes/utils.ts +1 -1
  44. package/dist/store/reducers/settings/settings.ts +4 -4
  45. package/dist/store/reducers/storage/types.ts +1 -1
  46. package/dist/store/reducers/storage/utils.ts +1 -1
  47. package/dist/store/reducers/tenants/utils.ts +2 -2
  48. package/dist/types/api/vdisk.ts +1 -1
  49. package/dist/utils/bytesParsers/formatBytes.ts +1 -1
  50. package/dist/utils/constants.ts +2 -1
  51. package/dist/utils/dataFormatters/dataFormatters.ts +128 -0
  52. package/dist/utils/dataFormatters/i18n/en.json +3 -0
  53. package/dist/utils/dataFormatters/i18n/ru.json +3 -0
  54. package/dist/utils/index.js +0 -102
  55. package/package.json +1 -1
  56. package/dist/components/ProgressViewer/ProgressViewer.js +0 -92
  57. package/dist/utils/formatCPU/formatCPU.ts +0 -20
  58. package/dist/utils/formatCPU/i18n/en.json +0 -3
  59. package/dist/utils/formatCPU/i18n/ru.json +0 -3
  60. /package/dist/utils/{formatCPU → dataFormatters}/i18n/index.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.18.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.17.0...v4.18.0) (2023-09-25)
4
+
5
+
6
+ ### Features
7
+
8
+ * **ProgressViewer:** add custom threasholds to ProgressViewer ([#540](https://github.com/ydb-platform/ydb-embedded-ui/issues/540)) ([3553065](https://github.com/ydb-platform/ydb-embedded-ui/commit/35530655581357f4a79c277a5bf9846b3befb784))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **Authentication:** enable page redirect ([#539](https://github.com/ydb-platform/ydb-embedded-ui/issues/539)) ([721883c](https://github.com/ydb-platform/ydb-embedded-ui/commit/721883cc7f4ca60e64d4a5f77b939dbb8e960855))
14
+ * **Healthcheck:** add merge_records request param ([#538](https://github.com/ydb-platform/ydb-embedded-ui/issues/538)) ([6a47481](https://github.com/ydb-platform/ydb-embedded-ui/commit/6a474814f71c3318715a8ce638fd522a770d8038))
15
+ * **Nodes:** use nodes endpoint by default ([#535](https://github.com/ydb-platform/ydb-embedded-ui/issues/535)) ([12d4fef](https://github.com/ydb-platform/ydb-embedded-ui/commit/12d4fefde7a6663bb1a11f46b4e94fb24b23e966))
16
+ * rename flag for display metrics cards for database diagnostics ([#536](https://github.com/ydb-platform/ydb-embedded-ui/issues/536)) ([957e1fa](https://github.com/ydb-platform/ydb-embedded-ui/commit/957e1fafbbc43928498ae9e8d0bc119bcda5288d))
17
+
3
18
  ## [4.17.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.16.2...v4.17.0) (2023-09-18)
4
19
 
5
20
 
@@ -3,10 +3,10 @@ import cn from 'bem-cn-lite';
3
3
  import type {TSystemStateInfo} from '../../types/api/nodes';
4
4
 
5
5
  import {LOAD_AVERAGE_TIME_INTERVALS} from '../../utils/constants';
6
- import {calcUptime} from '../../utils';
6
+ import {calcUptime} from '../../utils/dataFormatters/dataFormatters';
7
7
 
8
8
  import InfoViewer from '../InfoViewer/InfoViewer';
9
- import ProgressViewer from '../ProgressViewer/ProgressViewer';
9
+ import {ProgressViewer} from '../ProgressViewer/ProgressViewer';
10
10
  import {PoolUsage} from '../PoolUsage/PoolUsage';
11
11
 
12
12
  import './FullNodeViewer.scss';
@@ -1,5 +1,5 @@
1
1
  import type {TDirEntry} from '../../../types/api/schema';
2
- import {formatDateTime} from '../../../utils';
2
+ import {formatDateTime} from '../../../utils/dataFormatters/dataFormatters';
3
3
 
4
4
  import {createInfoFormatter} from '../utils';
5
5
 
@@ -4,7 +4,7 @@ import {
4
4
  TPQPartitionConfig,
5
5
  TPQTabletConfig,
6
6
  } from '../../../types/api/schema';
7
- import {formatBps, formatBytes, formatNumber} from '../../../utils';
7
+ import {formatBps, formatBytes, formatNumber} from '../../../utils/dataFormatters/dataFormatters';
8
8
  import {HOUR_IN_SECONDS} from '../../../utils/constants';
9
9
 
10
10
  import {createInfoFormatter} from '../utils';
@@ -1,6 +1,11 @@
1
1
  import type {TFollowerGroup, TPartitionConfig, TTableStats} from '../../../types/api/schema';
2
2
  import type {TMetrics} from '../../../types/api/tenant';
3
- import {formatCPU, formatNumber, formatBps, formatDateTime} from '../../../utils';
3
+ import {
4
+ formatBps,
5
+ formatCPU,
6
+ formatDateTime,
7
+ formatNumber,
8
+ } from '../../../utils/dataFormatters/dataFormatters';
4
9
  import {toFormattedSize} from '../../FormattedBytes/utils';
5
10
 
6
11
  import {createInfoFormatter} from '../utils';
@@ -0,0 +1,99 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import type {ValueOf} from '../../types/common';
4
+
5
+ import './ProgressViewer.scss';
6
+
7
+ const b = cn('progress-viewer');
8
+
9
+ export const PROGRESS_VIEWER_SIZE_IDS = {
10
+ xs: 'xs',
11
+ s: 's',
12
+ ns: 'ns',
13
+ m: 'm',
14
+ n: 'n',
15
+ l: 'l',
16
+ head: 'head',
17
+ } as const;
18
+
19
+ type ProgressViewerSize = ValueOf<typeof PROGRESS_VIEWER_SIZE_IDS>;
20
+
21
+ /*
22
+
23
+ Props description:
24
+ 1) value - the amount of progress
25
+ 2) capacity - maximum possible progress value
26
+ 3) formatValues - function for formatting the value and capacity
27
+ 4) percents - display progress in percents
28
+ 5) colorizeProgress - change the color of the progress bar depending on its value
29
+ 6) inverseColorize - invert the colors of the progress bar
30
+ 7) warningThreshold - the percentage of fullness at which the color of the progress bar changes to yellow
31
+ 8) dangerThreshold - the percentage of fullness at which the color of the progress bar changes to red
32
+ */
33
+
34
+ interface ProgressViewerProps {
35
+ value?: number | string;
36
+ capacity?: number | string;
37
+ formatValues?: (value?: number, capacity?: number) => (string | undefined)[];
38
+ percents?: boolean;
39
+ className?: string;
40
+ size?: ProgressViewerSize;
41
+ colorizeProgress?: boolean;
42
+ inverseColorize?: boolean;
43
+ warningThreshold?: number;
44
+ dangerThreshold?: number;
45
+ }
46
+
47
+ export function ProgressViewer({
48
+ value,
49
+ capacity = 100,
50
+ formatValues,
51
+ percents,
52
+ className,
53
+ size = PROGRESS_VIEWER_SIZE_IDS.xs,
54
+ colorizeProgress,
55
+ inverseColorize,
56
+ warningThreshold = 60,
57
+ dangerThreshold = 80,
58
+ }: ProgressViewerProps) {
59
+ let fillWidth = Math.round((parseFloat(String(value)) / parseFloat(String(capacity))) * 100);
60
+ fillWidth = fillWidth > 100 ? 100 : fillWidth;
61
+ let valueText: number | string | undefined = Math.round(Number(value)),
62
+ capacityText: number | string | undefined = capacity,
63
+ divider = '/';
64
+ if (formatValues) {
65
+ [valueText, capacityText] = formatValues(Number(value), Number(capacity));
66
+ } else if (percents) {
67
+ valueText = fillWidth + '%';
68
+ capacityText = '';
69
+ divider = '';
70
+ }
71
+
72
+ let bg = inverseColorize ? 'scarlet' : 'apple';
73
+ if (colorizeProgress) {
74
+ if (fillWidth > warningThreshold && fillWidth <= dangerThreshold) {
75
+ bg = 'saffron';
76
+ } else if (fillWidth > dangerThreshold) {
77
+ bg = inverseColorize ? 'apple' : 'scarlet';
78
+ }
79
+ }
80
+
81
+ const lineStyle = {
82
+ width: fillWidth + '%',
83
+ };
84
+
85
+ const text = fillWidth > 60 ? 'contrast0' : 'contrast70';
86
+
87
+ if (!isNaN(fillWidth)) {
88
+ return (
89
+ <div className={b({size}, className)}>
90
+ <div className={b('line', {bg})} style={lineStyle}></div>
91
+ <span
92
+ className={b('text', {text})}
93
+ >{`${valueText} ${divider} ${capacityText}`}</span>
94
+ </div>
95
+ );
96
+ }
97
+
98
+ return <div className={`${b({size})} ${className} error`}>no data</div>;
99
+ }
@@ -1,6 +1,6 @@
1
1
  import type {TTabletStateInfo} from '../../../types/api/tablet';
2
2
 
3
- import {calcUptime} from '../../../utils';
3
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
4
4
  import {InfoViewer, createInfoFormatter, formatObject} from '../../InfoViewer';
5
5
 
6
6
  const formatTablet = createInfoFormatter<TTabletStateInfo>({
@@ -50,7 +50,9 @@ function YbdInternalUser({ydbUser, logout}: YbdInternalUserProps) {
50
50
  const history = useHistory();
51
51
 
52
52
  const handleLoginClick = () => {
53
- history.push(createHref(routes.auth, undefined, {returnUrl: encodeURI(location.href)}));
53
+ history.push(
54
+ createHref(routes.auth, undefined, {returnUrl: encodeURIComponent(location.href)}),
55
+ );
54
56
  };
55
57
 
56
58
  return (
@@ -1,12 +1,13 @@
1
1
  import {KeyboardEvent, useEffect, useState} from 'react';
2
2
  import {useDispatch} from 'react-redux';
3
- import {useHistory} from 'react-router';
3
+ import {useHistory, useLocation} from 'react-router';
4
4
  import cn from 'bem-cn-lite';
5
5
 
6
6
  import {Button, TextInput, Icon, Link as ExternalLink} from '@gravity-ui/uikit';
7
7
 
8
8
  import {authenticate} from '../../store/reducers/authentication/authentication';
9
9
  import {useTypedSelector} from '../../utils/hooks';
10
+ import {parseQuery} from '../../routes';
10
11
 
11
12
  import ydbLogoIcon from '../../assets/icons/ydb.svg';
12
13
  import showIcon from '../../assets/icons/show.svg';
@@ -18,13 +19,15 @@ import './Authentication.scss';
18
19
  const b = cn('authentication');
19
20
 
20
21
  interface AuthenticationProps {
21
- returnUrl?: string;
22
22
  closable?: boolean;
23
23
  }
24
24
 
25
- function Authentication({returnUrl, closable = false}: AuthenticationProps) {
25
+ function Authentication({closable = false}: AuthenticationProps) {
26
26
  const dispatch = useDispatch();
27
27
  const history = useHistory();
28
+ const location = useLocation();
29
+
30
+ const {returnUrl} = parseQuery(location);
28
31
 
29
32
  const {error} = useTypedSelector((state) => state.authentication);
30
33
 
@@ -58,7 +61,14 @@ function Authentication({returnUrl, closable = false}: AuthenticationProps) {
58
61
  // typed dispatch required, remove error expectation after adding it
59
62
  dispatch(authenticate(login, pass)).then(() => {
60
63
  if (returnUrl) {
61
- history.replace(decodeURI(returnUrl));
64
+ const decodedUrl = decodeURIComponent(returnUrl.toString());
65
+
66
+ // to prevent page reload we use router history
67
+ // history navigates relative to origin
68
+ // so we remove origin to make it work properly
69
+ const url = new URL(decodedUrl);
70
+ const path = url.pathname + url.search;
71
+ history.replace(path);
62
72
  }
63
73
  });
64
74
  };
@@ -3,7 +3,7 @@ import block from 'bem-cn-lite';
3
3
  import {Skeleton} from '@gravity-ui/uikit';
4
4
 
5
5
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
6
- import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
6
+ import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
7
7
  import InfoViewer, {InfoViewerItem} from '../../../components/InfoViewer/InfoViewer';
8
8
  import {Tags} from '../../../components/Tags';
9
9
  import {Tablet} from '../../../components/Tablet';
@@ -16,7 +16,7 @@ import type {AdditionalClusterProps, ClusterLink} from '../../../types/additiona
16
16
  import type {VersionValue} from '../../../types/versions';
17
17
  import type {TClusterInfo} from '../../../types/api/cluster';
18
18
  import {backend, customBackend} from '../../../store';
19
- import {formatStorageValues} from '../../../utils';
19
+ import {formatStorageValues} from '../../../utils/dataFormatters/dataFormatters';
20
20
  import {useSetting, useTypedSelector} from '../../../utils/hooks';
21
21
  import {
22
22
  CLUSTER_DEFAULT_TITLE,
@@ -7,7 +7,7 @@ import {Checkbox, Select} from '@gravity-ui/uikit';
7
7
  import type {IHeatmapMetricValue} from '../../types/store/heatmap';
8
8
  import {getTabletsInfo, setHeatmapOptions} from '../../store/reducers/heatmap';
9
9
  import {showTooltip, hideTooltip} from '../../store/reducers/tooltip';
10
- import {formatNumber} from '../../utils';
10
+ import {formatNumber} from '../../utils/dataFormatters/dataFormatters';
11
11
  import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
12
12
 
13
13
  import {Loader} from '../../components/Loader';
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
5
  import {getColorRange, getCurrentMetricLimits} from '../util';
6
- import {formatNumber} from '../../../utils';
6
+ import {formatNumber} from '../../../utils/dataFormatters/dataFormatters';
7
7
 
8
8
  import './Histogram.scss';
9
9
 
@@ -8,12 +8,12 @@ import DataTable, {Column, Settings} from '@gravity-ui/react-data-table';
8
8
 
9
9
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
10
10
  import InfoViewer from '../../../components/InfoViewer/InfoViewer';
11
- import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
11
+ import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
12
12
  import {Icon} from '../../../components/Icon';
13
13
  import {Vdisk} from './Vdisk';
14
14
 
15
15
  import {bytesToGB, pad9} from '../../../utils/utils';
16
- import {formatStorageValuesToGb} from '../../../utils';
16
+ import {formatStorageValuesToGb} from '../../../utils/dataFormatters/dataFormatters';
17
17
  import {getPDiskType} from '../../../utils/pdisk';
18
18
 
19
19
  import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
@@ -1,8 +1,11 @@
1
1
  import React from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
 
4
- import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
5
- import {formatStorageValuesToGb, stringifyVdiskId} from '../../../utils';
4
+ import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
5
+ import {
6
+ formatStorageValuesToGb,
7
+ stringifyVdiskId,
8
+ } from '../../../utils/dataFormatters/dataFormatters';
6
9
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
7
10
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
8
11
  import {valueIsDefined} from './NodeStructure';
@@ -2,12 +2,12 @@ import DataTable, {Column} from '@gravity-ui/react-data-table';
2
2
  import {Popover} from '@gravity-ui/uikit';
3
3
 
4
4
  import {PoolsGraph} from '../../components/PoolsGraph/PoolsGraph';
5
- import ProgressViewer from '../../components/ProgressViewer/ProgressViewer';
5
+ import {ProgressViewer} from '../../components/ProgressViewer/ProgressViewer';
6
6
  import {TabletsStatistic} from '../../components/TabletsStatistic';
7
7
  import {NodeHostWrapper} from '../../components/NodeHostWrapper/NodeHostWrapper';
8
8
 
9
9
  import {isSortableNodesProperty} from '../../utils/nodes';
10
- import {formatBytesToGigabyte} from '../../utils/index';
10
+ import {formatBytesToGigabyte} from '../../utils/dataFormatters/dataFormatters';
11
11
 
12
12
  import type {NodesPreparedEntity} from '../../store/reducers/nodes/types';
13
13
 
@@ -7,7 +7,7 @@ import {Stack} from '../../../components/Stack/Stack';
7
7
  import routes, {createHref} from '../../../routes';
8
8
  import {selectVDisksForPDisk} from '../../../store/reducers/storage/selectors';
9
9
  import {TPDiskStateInfo, TPDiskState} from '../../../types/api/pdisk';
10
- import {stringifyVdiskId} from '../../../utils';
10
+ import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
11
11
  import {useTypedSelector} from '../../../utils/hooks';
12
12
  import {getPDiskType} from '../../../utils/pdisk';
13
13
  import {isFullVDiskData} from '../../../utils/storage';
@@ -9,7 +9,7 @@ import {InfoViewer, InfoViewerItem} from '../../../components/InfoViewer';
9
9
 
10
10
  import {EFlag} from '../../../types/api/enums';
11
11
  import {TPDiskStateInfo} from '../../../types/api/pdisk';
12
- import {getPDiskId} from '../../../utils';
12
+ import {getPDiskId} from '../../../utils/dataFormatters/dataFormatters';
13
13
  import {getPDiskType} from '../../../utils/pdisk';
14
14
  import {bytesToGB} from '../../../utils/utils';
15
15
 
@@ -10,7 +10,7 @@ import type {HandleSort} from '../../../utils/hooks/useTableSort';
10
10
 
11
11
  import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
12
12
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
13
- import {stringifyVdiskId} from '../../../utils';
13
+ import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
14
14
  import {getUsage, isFullVDiskData, isSortableStorageProperty} from '../../../utils/storage';
15
15
 
16
16
  import shieldIcon from '../../../assets/icons/shield.svg';
@@ -8,7 +8,7 @@ import {InternalLink} from '../../../components/InternalLink';
8
8
  import routes, {createHref} from '../../../routes';
9
9
  import {EFlag} from '../../../types/api/enums';
10
10
  import {EVDiskState, TVDiskStateInfo} from '../../../types/api/vdisk';
11
- import {stringifyVdiskId} from '../../../utils';
11
+ import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
12
12
  import {isFullVDiskData} from '../../../utils/storage';
13
13
 
14
14
  import {STRUCTURE} from '../../Node/NodePages';
@@ -9,7 +9,7 @@ import {InfoViewer, InfoViewerItem} from '../../../components/InfoViewer';
9
9
 
10
10
  import {EFlag} from '../../../types/api/enums';
11
11
  import type {TVDiskStateInfo} from '../../../types/api/vdisk';
12
- import {stringifyVdiskId} from '../../../utils';
12
+ import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
13
13
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
14
14
  import {isFullVDiskData} from '../../../utils/storage';
15
15
 
@@ -5,7 +5,7 @@ import {Link as UIKitLink} from '@gravity-ui/uikit';
5
5
  import {ETabletState, TTabletStateInfo} from '../../../types/api/tablet';
6
6
  import {InfoViewer, InfoViewerItem} from '../../../components/InfoViewer';
7
7
  import routes, {createHref} from '../../../routes';
8
- import {calcUptime} from '../../../utils';
8
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
9
9
  import {getDefaultNodePath} from '../../Node/NodePages';
10
10
 
11
11
  import {b} from '../Tablet';
@@ -4,7 +4,7 @@ import EntityStatus from '../../../components/EntityStatus/EntityStatus';
4
4
  import {InternalLink} from '../../../components/InternalLink/InternalLink';
5
5
 
6
6
  import type {ITabletPreparedHistoryItem} from '../../../types/store/tablet';
7
- import {calcUptime} from '../../../utils';
7
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
8
8
  import {getDefaultNodePath} from '../../Node/NodePages';
9
9
 
10
10
  import {b} from '../Tablet';
@@ -1,7 +1,7 @@
1
1
  import block from 'bem-cn-lite';
2
2
 
3
3
  import type {IPreparedTopicStats} from '../../../../../types/store/topic';
4
- import {formatMsToUptime} from '../../../../../utils';
4
+ import {formatMsToUptime} from '../../../../../utils/dataFormatters/dataFormatters';
5
5
  import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
6
6
 
7
7
  import './ConsumersTopicStats.scss';
@@ -6,7 +6,7 @@ import type {IPreparedConsumerData} from '../../../../../types/store/topic';
6
6
  import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
7
7
  import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
8
8
  import {InternalLink} from '../../../../../components/InternalLink';
9
- import {formatMsToUptime} from '../../../../../utils';
9
+ import {formatMsToUptime} from '../../../../../utils/dataFormatters/dataFormatters';
10
10
  import routes, {createHref} from '../../../../../routes';
11
11
 
12
12
  import {TenantTabsGroups} from '../../../TenantPages';
@@ -8,7 +8,7 @@ import type {EPathType} from '../../../../types/api/schema';
8
8
  import type {AdditionalTenantsProps} from '../../../../types/additionalProps';
9
9
  import {Icon} from '../../../../components/Icon';
10
10
  import {useSetting} from '../../../../utils/hooks';
11
- import {ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN} from '../../../../utils/constants';
11
+ import {DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS} from '../../../../utils/constants';
12
12
  import Overview from '../Overview/Overview';
13
13
  import {Healthcheck} from '../OldHealthcheck';
14
14
  import {TenantOverview} from '../TenantOverview/TenantOverview';
@@ -32,7 +32,7 @@ function DetailedOverview(props: DetailedOverviewProps) {
32
32
 
33
33
  const {currentSchemaPath} = useSelector((state: any) => state.schema);
34
34
 
35
- const [newTenantDiagnostics] = useSetting(ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN);
35
+ const [displayMetricsCards] = useSetting(DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS);
36
36
 
37
37
  const openModalHandler = () => {
38
38
  setIsModalVisible(true);
@@ -59,7 +59,7 @@ function DetailedOverview(props: DetailedOverviewProps) {
59
59
  };
60
60
 
61
61
  const renderTenantOverview = () => {
62
- if (newTenantDiagnostics) {
62
+ if (displayMetricsCards) {
63
63
  return (
64
64
  <div className={b('section')}>
65
65
  <TenantOverview
@@ -8,7 +8,7 @@ import type {
8
8
  import type {KeyValueRow} from '../../../../../types/api/query';
9
9
  import {EPathType} from '../../../../../types/api/schema';
10
10
  import {isNumeric} from '../../../../../utils/utils';
11
- import {formatBytes, formatNumber} from '../../../../../utils';
11
+ import {formatBytes, formatNumber} from '../../../../../utils/dataFormatters/dataFormatters';
12
12
  import {formatDurationToShortTimeFormat} from '../../../../../utils/timeParsers';
13
13
  import {formatObject, InfoViewerItem} from '../../../../../components/InfoViewer';
14
14
  import {
@@ -11,7 +11,7 @@ import {ResponseError} from '../../../../../components/Errors/ResponseError';
11
11
 
12
12
  import {useTypedSelector} from '../../../../../utils/hooks';
13
13
  import {formatDurationToShortTimeFormat} from '../../../../../utils/timeParsers';
14
- import {formatBps, formatBytes} from '../../../../../utils';
14
+ import {formatBps, formatBytes} from '../../../../../utils/dataFormatters/dataFormatters';
15
15
 
16
16
  import {selectPreparedTopicStats} from '../../../../../store/reducers/topic';
17
17
 
@@ -4,7 +4,7 @@ import block from 'bem-cn-lite';
4
4
  import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
5
5
  import EntityStatus from '../../../../../components/EntityStatus/EntityStatus';
6
6
  import {getDefaultNodePath} from '../../../../Node/NodePages';
7
- import {formatBytes, formatMsToUptime} from '../../../../../utils';
7
+ import {formatBytes, formatMsToUptime} from '../../../../../utils/dataFormatters/dataFormatters';
8
8
  import {isNumeric} from '../../../../../utils/utils';
9
9
 
10
10
  import {
@@ -8,7 +8,7 @@ import {InfoViewer} from '../../../../components/InfoViewer';
8
8
  import {PoolUsage} from '../../../../components/PoolUsage/PoolUsage';
9
9
  import {Tablet} from '../../../../components/Tablet';
10
10
  import EntityStatus from '../../../../components/EntityStatus/EntityStatus';
11
- import {formatCPU} from '../../../../utils';
11
+ import {formatCPU} from '../../../../utils/dataFormatters/dataFormatters';
12
12
  import {TABLET_STATES, TENANT_DEFAULT_TITLE} from '../../../../utils/constants';
13
13
  import {bytesToGB} from '../../../../utils/utils';
14
14
  import {mapDatabaseTypeToDBName} from '../../utils/schema';
@@ -28,7 +28,7 @@ import {
28
28
  TENANT_PAGES_IDS,
29
29
  TENANT_QUERY_TABS_ID,
30
30
  } from '../../../../store/reducers/tenant/constants';
31
- import {formatDateTime, formatNumber} from '../../../../utils';
31
+ import {formatDateTime, formatNumber} from '../../../../utils/dataFormatters/dataFormatters';
32
32
  import {HOUR_IN_SECONDS} from '../../../../utils/constants';
33
33
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
34
34
  import {prepareQueryError} from '../../../../utils/query';
@@ -22,7 +22,7 @@ import {EShardsWorkloadMode, IShardsWorkloadFilters} from '../../../../types/sto
22
22
  import type {EPathType} from '../../../../types/api/schema';
23
23
  import type {CellValue, KeyValueRow} from '../../../../types/api/query';
24
24
 
25
- import {formatDateTime, formatNumber} from '../../../../utils';
25
+ import {formatDateTime, formatNumber} from '../../../../utils/dataFormatters/dataFormatters';
26
26
  import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
27
27
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
28
28
  import {prepareQueryError} from '../../../../utils/query';
@@ -24,7 +24,7 @@ import {
24
24
  TColumnTableDescription,
25
25
  } from '../../../types/api/schema';
26
26
  import routes, {createHref} from '../../../routes';
27
- import {formatDateTime} from '../../../utils';
27
+ import {formatDateTime} from '../../../utils/dataFormatters/dataFormatters';
28
28
  import {useTypedSelector} from '../../../utils/hooks';
29
29
  import {
30
30
  DEFAULT_IS_TENANT_COMMON_INFO_COLLAPSED,
@@ -26,7 +26,11 @@ import {
26
26
  ProblemFilterValues,
27
27
  selectProblemFilter,
28
28
  } from '../../store/reducers/settings/settings';
29
- import {formatCPU, formatBytesToGigabyte, formatNumber} from '../../utils';
29
+ import {
30
+ formatBytesToGigabyte,
31
+ formatCPU,
32
+ formatNumber,
33
+ } from '../../utils/dataFormatters/dataFormatters';
30
34
  import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
31
35
  import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
32
36
  import {clusterName} from '../../store';
@@ -25,6 +25,6 @@
25
25
  "settings.enableAdditionalQueryModes.title": "Enable additional query modes",
26
26
  "settings.enableAdditionalQueryModes.popover": "Adds 'Data', 'YQL - QueryService' and 'PostgreSQL' modes. May not work on some versions",
27
27
 
28
- "settings.tenantDiagnostics.title": "Enable new database diagnostics design",
28
+ "settings.tenantDiagnostics.title": "Display metrics cards for database diagnostics",
29
29
  "settings.tenantDiagnostics.popover": "Adds indicators of database resources usage. Incomplete data may be displayed for some databases"
30
30
  }
@@ -25,6 +25,6 @@
25
25
  "settings.enableAdditionalQueryModes.title": "Включить дополнительные режимы выполнения запросов",
26
26
  "settings.enableAdditionalQueryModes.popover": "Добавляет режимы 'Data', 'YQL - QueryService' и 'PostgreSQL'. Может работать некорректно на некоторых версиях",
27
27
 
28
- "settings.tenantDiagnostics.title": "Включить новый дизайн для диагностики баз данных",
28
+ "settings.tenantDiagnostics.title": "Показывать карточки с метриками в диагностике базы данных",
29
29
  "settings.tenantDiagnostics.popover": "Добавляет индикаторы использования ресурсов базы данных. Для некоторых баз могут отображаться неполные данные"
30
30
  }
@@ -5,7 +5,7 @@ import flaskIcon from '../../assets/icons/flask.svg';
5
5
 
6
6
  import {
7
7
  ENABLE_ADDITIONAL_QUERY_MODES,
8
- ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN,
8
+ DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
9
9
  INVERTED_DISKS_KEY,
10
10
  LANGUAGE_KEY,
11
11
  THEME_KEY,
@@ -96,7 +96,7 @@ export const enableQueryModesForExplainSetting: SettingProps = {
96
96
  helpPopoverContent: i18n('settings.enableAdditionalQueryModes.popover'),
97
97
  };
98
98
  export const enableNewTenantDiagnosticsDesign: SettingProps = {
99
- settingKey: ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN,
99
+ settingKey: DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
100
100
  title: i18n('settings.tenantDiagnostics.title'),
101
101
  helpPopoverContent: i18n('settings.tenantDiagnostics.popover'),
102
102
  };
@@ -3,10 +3,10 @@ import DataTable, {Column} from '@gravity-ui/react-data-table';
3
3
  import type {PreparedClusterNode} from '../../../store/reducers/clusterNodes/types';
4
4
  import {isUnavailableNode} from '../../../utils/nodes';
5
5
  import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
6
- import {formatBytes} from '../../../utils';
6
+ import {formatBytes} from '../../../utils/dataFormatters/dataFormatters';
7
7
  import {getDefaultNodePath} from '../../Node/NodePages';
8
8
 
9
- import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
9
+ import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
10
10
  import {PoolsGraph} from '../../../components/PoolsGraph/PoolsGraph';
11
11
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
12
12
 
@@ -352,7 +352,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
352
352
  }
353
353
  getHealthcheckInfo(database: string, {concurrentId}: AxiosOptions = {}) {
354
354
  return this.get<HealthCheckAPIResponse>(
355
- this.getPath('/viewer/json/healthcheck'),
355
+ this.getPath('/viewer/json/healthcheck?merge_records=true'),
356
356
  {tenant: database},
357
357
  {concurrentId},
358
358
  );
@@ -5,7 +5,7 @@ import {createRequestActionTypes, createApiRequest} from '../../utils';
5
5
  import type {ClusterNodesAction, ClusterNodesState, PreparedClusterNode} from './types';
6
6
 
7
7
  import '../../../services/api';
8
- import {calcUptime} from '../../../utils';
8
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
9
9
 
10
10
  export const FETCH_CLUSTER_NODES = createRequestActionTypes('cluster', 'FETCH_CLUSTER_NODES');
11
11
 
@@ -1,7 +1,7 @@
1
1
  import type {Selector} from 'reselect';
2
2
  import {createSelector} from 'reselect';
3
3
 
4
- import {stringifyVdiskId} from '../../../utils';
4
+ import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
5
5
 
6
6
  import type {
7
7
  NodeStateSlice,
@@ -1,7 +1,7 @@
1
1
  import {Selector, createSelector} from 'reselect';
2
2
 
3
3
  import {EFlag} from '../../../types/api/enums';
4
- import {calcUptimeInSeconds} from '../../../utils';
4
+ import {calcUptimeInSeconds} from '../../../utils/dataFormatters/dataFormatters';
5
5
  import {HOUR_IN_SECONDS} from '../../../utils/constants';
6
6
  import {NodesUptimeFilterValues} from '../../../utils/nodes';
7
7
  import {prepareSearchValue} from '../../../utils/filters';
@@ -64,7 +64,7 @@ export interface NodesGeneralRequestParams extends NodesSortParams {
64
64
  uptime?: number; // return nodes with less uptime in seconds
65
65
  problems_only?: boolean; // return nodes with SystemState !== EFlag.Green
66
66
 
67
- offser?: number;
67
+ offset?: number;
68
68
  limit?: number;
69
69
  }
70
70
 
@@ -1,6 +1,6 @@
1
1
  import type {TComputeInfo, TComputeNodeInfo} from '../../../types/api/compute';
2
2
  import type {TNodesInfo} from '../../../types/api/nodes';
3
- import {calcUptime} from '../../../utils';
3
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
4
4
 
5
5
  import type {NodesHandledResponse, NodesPreparedEntity} from './types';
6
6
 
@@ -15,7 +15,7 @@ import {
15
15
  LAST_USED_QUERY_ACTION_KEY,
16
16
  USE_BACKEND_PARAMS_FOR_TABLES_KEY,
17
17
  LANGUAGE_KEY,
18
- ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN,
18
+ DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
19
19
  } from '../../../utils/constants';
20
20
  import '../../../services/api';
21
21
  import {parseJson} from '../../../utils/utils';
@@ -50,14 +50,14 @@ export const initialState = {
50
50
  [INVERTED_DISKS_KEY]: readSavedSettingsValue(INVERTED_DISKS_KEY, 'false'),
51
51
  [USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY]: readSavedSettingsValue(
52
52
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
53
- 'false',
53
+ 'true',
54
54
  ),
55
55
  [ENABLE_ADDITIONAL_QUERY_MODES]: readSavedSettingsValue(
56
56
  ENABLE_ADDITIONAL_QUERY_MODES,
57
57
  'false',
58
58
  ),
59
- [ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN]: readSavedSettingsValue(
60
- ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN,
59
+ [DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS]: readSavedSettingsValue(
60
+ DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
61
61
  'false',
62
62
  ),
63
63
  [SAVED_QUERIES_KEY]: readSavedSettingsValue(SAVED_QUERIES_KEY, '[]'),
@@ -63,7 +63,7 @@ export interface StorageSortParams {
63
63
  export interface StorageSortAndFilterParams extends StorageSortParams {
64
64
  filter?: string; // PoolName or GroupId
65
65
 
66
- offser?: number;
66
+ offset?: number;
67
67
  limit?: number;
68
68
  }
69
69
 
@@ -9,7 +9,7 @@ import {TPDiskState} from '../../../types/api/pdisk';
9
9
  import {EFlag} from '../../../types/api/enums';
10
10
  import {getPDiskType} from '../../../utils/pdisk';
11
11
  import {getUsage} from '../../../utils/storage';
12
- import {calcUptime} from '../../../utils';
12
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
13
13
 
14
14
  import type {PreparedStorageGroup, PreparedStorageNode, PreparedStorageResponse} from './types';
15
15
 
@@ -1,6 +1,6 @@
1
1
  import type {TTenant} from '../../../types/api/tenant';
2
2
  import {formatBytes} from '../../../utils/bytesParsers';
3
- import {formatCPU} from '../../../utils/formatCPU/formatCPU';
3
+ import {formatCPUWithLabel} from '../../../utils/dataFormatters/dataFormatters';
4
4
  import {isNumeric} from '../../../utils/utils';
5
5
  import {METRIC_STATUS} from './contants';
6
6
 
@@ -158,7 +158,7 @@ export const formatTenantMetrics = ({
158
158
  storage?: number;
159
159
  memory?: number;
160
160
  }) => ({
161
- cpu: formatCPU(cpu),
161
+ cpu: formatCPUWithLabel(cpu),
162
162
  storage: formatBytes({value: storage, significantDigits: 2}) || undefined,
163
163
  memory: formatBytes({value: memory, significantDigits: 2}) || undefined,
164
164
  });
@@ -99,7 +99,7 @@ interface TVDiskSatisfactionRank {
99
99
  LevelRank?: TRank;
100
100
  }
101
101
 
102
- interface TVDiskID {
102
+ export interface TVDiskID {
103
103
  GroupID?: number;
104
104
  GroupGeneration?: number;
105
105
  Ring?: number;
@@ -1,4 +1,4 @@
1
- import {formatNumber} from '..';
1
+ import {formatNumber} from '../dataFormatters/dataFormatters';
2
2
  import {GIGABYTE, KILOBYTE, MEGABYTE, TERABYTE} from '../constants';
3
3
  import {isNumeric} from '../utils';
4
4
 
@@ -88,7 +88,8 @@ export const LANGUAGE_KEY = 'language';
88
88
  export const INVERTED_DISKS_KEY = 'invertedDisks';
89
89
  export const USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY = 'useNodesEndpointInDiagnostics';
90
90
  export const ENABLE_ADDITIONAL_QUERY_MODES = 'enableAdditionalQueryModes';
91
- export const ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN = 'enableNewTenantDiagnosticsDesign';
91
+ // Remain key name "enableNewTenantDiagnosticsDesign" for backward compatibility
92
+ export const DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS = 'enableNewTenantDiagnosticsDesign';
92
93
  export const SAVED_QUERIES_KEY = 'saved_queries';
93
94
  export const ASIDE_HEADER_COMPACT_KEY = 'asideHeaderCompact';
94
95
  export const QUERIES_HISTORY_KEY = 'queries_history';
@@ -0,0 +1,128 @@
1
+ import {dateTimeParse} from '@gravity-ui/date-utils';
2
+
3
+ import type {TVDiskID, TVSlotId} from '../../types/api/vdisk';
4
+ import {DAY_IN_SECONDS, GIGABYTE, TERABYTE} from '../constants';
5
+ import {configuredNumeral} from '../numeral';
6
+ import {isNumeric} from '../utils';
7
+
8
+ import i18n from './i18n';
9
+
10
+ // Here you can't control displayed size and precision
11
+ // If you need more custom format, use formatBytesCustom instead
12
+ export const formatBytes = (bytes?: string | number) => {
13
+ if (!isNumeric(bytes)) {
14
+ return '';
15
+ }
16
+
17
+ // by agreement, display byte values in decimal scale
18
+ return configuredNumeral(bytes).format('0 b');
19
+ };
20
+
21
+ export const formatBps = (bytes?: string | number) => {
22
+ const formattedBytes = formatBytes(bytes);
23
+
24
+ if (!formattedBytes) {
25
+ return '';
26
+ }
27
+
28
+ return formattedBytes + '/s';
29
+ };
30
+
31
+ export const formatBytesToGigabyte = (bytes: number | string) => {
32
+ return `${Math.floor(Number(bytes) / GIGABYTE)} GB`;
33
+ };
34
+
35
+ export const stringifyVdiskId = (id?: TVDiskID | TVSlotId) => {
36
+ return id ? Object.values(id).join('-') : '';
37
+ };
38
+
39
+ export const getPDiskId = (info: {NodeId?: number; PDiskId?: number}) => {
40
+ return info.NodeId && info.PDiskId ? `${info.NodeId}-${info.PDiskId}` : undefined;
41
+ };
42
+
43
+ export const formatUptime = (seconds: number) => {
44
+ const days = Math.floor(seconds / DAY_IN_SECONDS);
45
+ const remain = seconds % DAY_IN_SECONDS;
46
+
47
+ const uptime = [days && `${days}d`, configuredNumeral(remain).format('00:00:00')]
48
+ .filter(Boolean)
49
+ .join(' ');
50
+
51
+ return uptime;
52
+ };
53
+
54
+ export const formatMsToUptime = (ms?: number) => {
55
+ return ms && formatUptime(ms / 1000);
56
+ };
57
+
58
+ export const formatStorageValues = (value?: number, total?: number) => {
59
+ return [
60
+ value ? String(Math.floor(value / TERABYTE)) : undefined,
61
+ total ? `${Math.floor(total / TERABYTE)} TB` : undefined,
62
+ ];
63
+ };
64
+ export const formatStorageValuesToGb = (value?: number, total?: number): (string | undefined)[] => {
65
+ return [
66
+ value ? String(Math.floor(value / 1000000000)) : undefined,
67
+ total ? `${Math.floor(total / 1000000000)} GB` : undefined,
68
+ ];
69
+ };
70
+
71
+ export const formatNumber = (number?: unknown) => {
72
+ if (!isNumeric(number)) {
73
+ return '';
74
+ }
75
+
76
+ return configuredNumeral(number).format();
77
+ };
78
+
79
+ const normalizeCPU = (value: number | string) => {
80
+ const rawCores = Number(value) / 1000000;
81
+ let cores = rawCores.toPrecision(3);
82
+ if (rawCores >= 1000) {
83
+ cores = rawCores.toFixed();
84
+ }
85
+ if (rawCores < 0.001) {
86
+ cores = '0';
87
+ }
88
+
89
+ return Number(cores);
90
+ };
91
+
92
+ export const formatCPU = (value?: number | string) => {
93
+ if (value === undefined) {
94
+ return undefined;
95
+ }
96
+
97
+ return configuredNumeral(normalizeCPU(value)).format('0.[000]');
98
+ };
99
+
100
+ export const formatCPUWithLabel = (value?: number) => {
101
+ if (value === undefined) {
102
+ return undefined;
103
+ }
104
+ const cores = normalizeCPU(value);
105
+ const localizedCores = configuredNumeral(cores).format('0.[000]');
106
+
107
+ return `${localizedCores} ${i18n('format-cpu.cores', {count: cores})}`;
108
+ };
109
+
110
+ export const formatDateTime = (value?: number | string) => {
111
+ if (!isNumeric(value)) {
112
+ return '';
113
+ }
114
+
115
+ const formattedData = dateTimeParse(Number(value))?.format('YYYY-MM-DD HH:mm');
116
+
117
+ return Number(value) > 0 && formattedData ? formattedData : 'N/A';
118
+ };
119
+
120
+ export const calcUptimeInSeconds = (milliseconds: number | string) => {
121
+ const currentDate = new Date();
122
+ const diff = currentDate.getTime() - Number(milliseconds);
123
+ return diff <= 0 ? 0 : diff / 1000;
124
+ };
125
+
126
+ export const calcUptime = (milliseconds?: number | string) => {
127
+ return formatUptime(calcUptimeInSeconds(Number(milliseconds)));
128
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "format-cpu.cores": ["core", "cores", "cores", "cores"]
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "format-cpu.cores": ["ядро", "ядра", "ядер", "ядер"]
3
+ }
@@ -1,105 +1,3 @@
1
- import {dateTimeParse} from '@gravity-ui/date-utils';
2
-
3
- import {MEGABYTE, TERABYTE, GIGABYTE, DAY_IN_SECONDS} from './constants';
4
- import {isNumeric} from './utils';
5
- import {configuredNumeral} from './numeral';
6
-
7
- // Here you can't control displayed size and precision
8
- // If you need more custom format, use formatBytesCustom instead
9
- export const formatBytes = (bytes) => {
10
- if (!isNumeric(bytes)) {
11
- return '';
12
- }
13
-
14
- // by agreement, display byte values in decimal scale
15
- return configuredNumeral(bytes).format('0 b');
16
- };
17
-
18
- export const formatBps = (bytes) => {
19
- const formattedBytes = formatBytes(bytes);
20
-
21
- if (!formattedBytes) {
22
- return '';
23
- }
24
-
25
- return formattedBytes + '/s';
26
- };
27
-
28
- export const formatBytesToGigabyte = (bytes) => {
29
- return `${Math.floor(bytes / GIGABYTE)} GB`;
30
- };
31
-
32
- export const stringifyVdiskId = (id) => {
33
- return Object.values(id).join('-');
34
- };
35
- export const getPDiskId = (info) => {
36
- return `${info.NodeId}-${info.PDiskId}`;
37
- };
38
-
39
- export const formatUptime = (seconds) => {
40
- const days = Math.floor(seconds / DAY_IN_SECONDS);
41
- const remain = seconds % DAY_IN_SECONDS;
42
-
43
- const uptime = [days && `${days}d`, configuredNumeral(remain).format('00:00:00')]
44
- .filter(Boolean)
45
- .join(' ');
46
-
47
- return uptime;
48
- };
49
-
50
- export const formatMsToUptime = (ms) => {
51
- return formatUptime(ms / 1000);
52
- };
53
-
54
- export const formatIOPS = (value, capacity) => {
55
- return [Math.floor(value), Math.floor(capacity) + ' IOPS'];
56
- };
57
-
58
- export const formatStorageValues = (value, total) => {
59
- return [Math.floor(value / TERABYTE), `${Math.floor(total / TERABYTE)} TB`];
60
- };
61
- export const formatStorageValuesToGb = (value, total) => {
62
- return [Math.floor(value / 1000000000), `${Math.floor(total / 1000000000)} GB`];
63
- };
64
-
65
- export const formatThroughput = (value, total) => {
66
- return [(value / MEGABYTE).toFixed(2), (total / MEGABYTE).toFixed(1) + ' MB/s'];
67
- };
68
-
69
- export const formatNumber = (number) => {
70
- if (!isNumeric(number)) {
71
- return '';
72
- }
73
-
74
- return configuredNumeral(number).format();
75
- };
76
-
77
- export const formatCPU = (value) => {
78
- if (!isNumeric(value)) {
79
- return '';
80
- }
81
-
82
- return configuredNumeral(value / 1000000).format('0.00');
83
- };
84
-
85
- export const formatDateTime = (value) => {
86
- if (!isNumeric(value)) {
87
- return '';
88
- }
89
-
90
- return value > 0 ? dateTimeParse(Number(value)).format('YYYY-MM-DD HH:mm') : 'N/A';
91
- };
92
-
93
- export const calcUptimeInSeconds = (milliseconds) => {
94
- const currentDate = new Date();
95
- const diff = currentDate - Number(milliseconds);
96
- return diff <= 0 ? 0 : diff / 1000;
97
- };
98
-
99
- export const calcUptime = (milliseconds) => {
100
- return formatUptime(calcUptimeInSeconds(milliseconds));
101
- };
102
-
103
1
  // determine how many nodes have status Connected "true"
104
2
  export const getConnectedNodesCount = (nodeStateInfo) => {
105
3
  return nodeStateInfo?.reduce((acc, item) => (item.Connected ? acc + 1 : acc), 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "4.17.0",
3
+ "version": "4.18.0",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -1,92 +0,0 @@
1
- import React from 'react';
2
- import cn from 'bem-cn-lite';
3
- import './ProgressViewer.scss';
4
- import PropTypes from 'prop-types';
5
- const b = cn('progress-viewer');
6
- /*
7
-
8
- Описание props:
9
- 1) value - величина прогресса
10
- 2) capacity - предельно возможный прогресс
11
- 3) formatValues - функция форматирования value и capacity
12
- 4) percents - отображать ли заполненость в виде процентов
13
- 5) className - кастомный класс
14
- 6) colorizeProgress - менять ли цвет полосы прогресса в зависимости от его величины
15
- 7) inverseColorize - инвертировать ли цвета при разукрашивании прогресса
16
- */
17
- export class ProgressViewer extends React.Component {
18
- static propTypes = {
19
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
20
- capacity: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
21
- formatValues: PropTypes.func,
22
- percents: PropTypes.bool,
23
- className: PropTypes.string,
24
- size: PropTypes.oneOf(['xs', 's', 'ns', 'm', 'n', 'l', 'head']),
25
- colorizeProgress: PropTypes.bool,
26
- inverseColorize: PropTypes.bool,
27
- };
28
-
29
- static defaultProps = {
30
- size: 'xs',
31
- colorizeProgress: false,
32
- capacity: 100,
33
- inverseColorize: false,
34
- };
35
-
36
- render() {
37
- const {
38
- value,
39
- capacity,
40
- formatValues,
41
- percents,
42
- size,
43
- className,
44
- colorizeProgress,
45
- inverseColorize,
46
- } = this.props;
47
-
48
- let fillWidth = Math.round((parseFloat(value) / parseFloat(capacity)) * 100);
49
- fillWidth = fillWidth > 100 ? 100 : fillWidth;
50
-
51
- let valueText = Math.round(value),
52
- capacityText = capacity,
53
- divider = '/';
54
- if (formatValues) {
55
- [valueText, capacityText] = formatValues(value, capacity);
56
- } else if (percents) {
57
- valueText = fillWidth + '%';
58
- capacityText = '';
59
- divider = '';
60
- }
61
-
62
- let bg = inverseColorize ? 'scarlet' : 'apple';
63
- if (colorizeProgress) {
64
- if (fillWidth > 60 && fillWidth <= 80) {
65
- bg = 'saffron';
66
- } else if (fillWidth > 80) {
67
- bg = inverseColorize ? 'apple' : 'scarlet';
68
- }
69
- }
70
-
71
- const lineStyle = {
72
- width: fillWidth + '%',
73
- };
74
-
75
- const text = fillWidth > 60 ? 'contrast0' : 'contrast70';
76
-
77
- if (!isNaN(fillWidth)) {
78
- return (
79
- <div className={b({size}, className)}>
80
- <div className={b('line', {bg})} style={lineStyle}></div>
81
- <span className={b('text', {text})}>
82
- {`${valueText} ${divider} ${capacityText}`}
83
- </span>
84
- </div>
85
- );
86
- }
87
-
88
- return <div className={`${b({size})} ${className} error`}>no data</div>;
89
- }
90
- }
91
-
92
- export default ProgressViewer;
@@ -1,20 +0,0 @@
1
- import {configuredNumeral} from '../numeral';
2
- import i18n from './i18n';
3
-
4
- export const formatCPU = (value?: number) => {
5
- if (value === undefined) {
6
- return undefined;
7
- }
8
-
9
- const rawCores = value / 1000000;
10
- let cores = rawCores.toPrecision(3);
11
- if (rawCores >= 1000) {
12
- cores = rawCores.toFixed();
13
- }
14
- if (rawCores < 0.001) {
15
- cores = '0';
16
- }
17
- const localizedCores = configuredNumeral(Number(cores)).format('0.[000]');
18
-
19
- return `${localizedCores} ${i18n('cores', {count: cores})}`;
20
- };
@@ -1,3 +0,0 @@
1
- {
2
- "cores": ["core", "cores", "cores", "cores"]
3
- }
@@ -1,3 +0,0 @@
1
- {
2
- "cores": ["ядро", "ядра", "ядер", "ядер"]
3
- }