ydb-embedded-ui 3.3.1 → 3.3.3
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.
- package/CHANGELOG.md +26 -0
- package/dist/components/Errors/ResponseError/ResponseError.tsx +17 -0
- package/dist/components/Errors/ResponseError/index.ts +1 -0
- package/dist/components/Errors/i18n/en.json +2 -1
- package/dist/components/Errors/i18n/ru.json +2 -1
- package/dist/components/FullGroupViewer/FullGroupViewer.js +1 -1
- package/dist/components/InfoViewer/InfoViewer.scss +1 -1
- package/dist/components/InfoViewer/InfoViewer.tsx +29 -21
- package/dist/components/InfoViewer/formatters/index.ts +1 -0
- package/dist/components/InfoViewer/formatters/table.ts +40 -0
- package/dist/components/QueryExecutionStatus/QueryExecutionStatus.tsx +26 -8
- package/dist/components/QueryExecutionStatus/index.ts +1 -0
- package/dist/components/QueryResultTable/QueryResultTable.tsx +2 -2
- package/dist/containers/App/Content.js +12 -5
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +10 -13
- package/dist/containers/Authentication/Authentication.scss +6 -0
- package/dist/containers/Authentication/Authentication.tsx +34 -15
- package/dist/containers/Node/NodeStructure/Pdisk.tsx +7 -10
- package/dist/containers/Nodes/Nodes.tsx +1 -1
- package/dist/containers/Nodes/getNodesColumns.tsx +4 -4
- package/dist/containers/Storage/PDisk/PDisk.tsx +25 -17
- package/dist/containers/Storage/PDisk/__tests__/colors.tsx +64 -1
- package/dist/containers/Storage/Storage.js +1 -1
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +4 -3
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +1 -1
- package/dist/containers/Storage/VDisk/VDisk.tsx +1 -1
- package/dist/containers/Storage/utils/index.ts +26 -10
- package/dist/containers/Tablet/Tablet.js +1 -1
- package/dist/containers/Tenant/Acl/Acl.js +1 -1
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +2 -2
- package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/OverloadedShards.tsx +6 -1
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +10 -21
- package/dist/containers/Tenant/{Schema/SchemaInfoViewer/SchemaInfoViewer.scss → Diagnostics/Overview/TableInfo/TableInfo.scss} +8 -10
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/TableInfo.tsx +71 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/en.json +5 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/ru.json +5 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +96 -0
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +7 -1
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +3 -1
- package/dist/containers/Tenant/QueryEditor/QueryEditor.js +8 -3
- package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +16 -11
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +37 -23
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +4 -0
- package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +1 -1
- package/dist/containers/Tenants/Tenants.js +4 -3
- package/dist/routes.ts +1 -0
- package/dist/services/api.js +4 -1
- package/dist/store/reducers/shardsWorkload.ts +2 -1
- package/dist/store/reducers/storage.js +1 -1
- package/dist/utils/constants.ts +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/prepareQueryExplain.ts +1 -1
- package/package.json +5 -3
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.scss +0 -13
- package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +0 -201
    
        package/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,31 @@ | |
| 1 1 | 
             
            # Changelog
         | 
| 2 2 |  | 
| 3 | 
            +
            ## [3.3.3](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.2...v3.3.3) (2023-02-08)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
             | 
| 6 | 
            +
            ### Bug Fixes
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            * **Auth:** add a step in history for auth form ([c72d06e](https://github.com/ydb-platform/ydb-embedded-ui/commit/c72d06ecacdba47cac59bd705c1185e1ddf0b20d))
         | 
| 9 | 
            +
            * format dates with date-utils ([948598b](https://github.com/ydb-platform/ydb-embedded-ui/commit/948598b83c9bdd09268d128e15a42d5a6e0c15cc))
         | 
| 10 | 
            +
            * **InfoViewer:** add prop renderEmptyState ([44fe28f](https://github.com/ydb-platform/ydb-embedded-ui/commit/44fe28f72ea299b3b5d9b5a33a0a0130d471f7dd))
         | 
| 11 | 
            +
            * minor fixes in Nodes and Tenants tables ([8dca43a](https://github.com/ydb-platform/ydb-embedded-ui/commit/8dca43a482b0da31dbc618875b416dcfcedac036))
         | 
| 12 | 
            +
            * **OverloadedShards:** display IntervalEnd ([c7cbd72](https://github.com/ydb-platform/ydb-embedded-ui/commit/c7cbd7215eaf60601941410acb13ffb25d151eb9))
         | 
| 13 | 
            +
            * **Overview:** display error statusText on schema error ([99b030f](https://github.com/ydb-platform/ydb-embedded-ui/commit/99b030f90e6044e98a151e5128603835c84e1b4e))
         | 
| 14 | 
            +
            * **PDisk:** calculate severity based on usage ([64c6890](https://github.com/ydb-platform/ydb-embedded-ui/commit/64c6890ac6d5a77aef73da7dfc7f1eaff8a72441))
         | 
| 15 | 
            +
            * **QueryEditor:** make client request timeout 9 min ([44528a8](https://github.com/ydb-platform/ydb-embedded-ui/commit/44528a865b039003cda4c7b1b1367840da015d09))
         | 
| 16 | 
            +
            * **QueryEditor:** result status for aborted connection ([4b0d84b](https://github.com/ydb-platform/ydb-embedded-ui/commit/4b0d84b550deb41a140d4a3d215e52084507a558))
         | 
| 17 | 
            +
            * **QueryResult:** output client error messages ([deef610](https://github.com/ydb-platform/ydb-embedded-ui/commit/deef6103f4d08825837520cab9e8ae5b8c7fd496))
         | 
| 18 | 
            +
            * **Storage:** replace hasOwn to hasOwnProperty ([2452310](https://github.com/ydb-platform/ydb-embedded-ui/commit/2452310ce8e953d7a9ee4bbaa2bd466396aa0131))
         | 
| 19 | 
            +
            * **TopQueries:** display IntervalEnd ([e5b2b07](https://github.com/ydb-platform/ydb-embedded-ui/commit/e5b2b07cf1e686c20817dcdc1ae32e0c8912a21a))
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ## [3.3.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.1...v3.3.2) (2023-01-31)
         | 
| 22 | 
            +
             | 
| 23 | 
            +
             | 
| 24 | 
            +
            ### Bug Fixes
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            * **QueryEditor:** collapse bottom panel if empty ([566db3b](https://github.com/ydb-platform/ydb-embedded-ui/commit/566db3b15c4393555071f058c88ad36b4073cc2d))
         | 
| 27 | 
            +
            * **VDisk:** use pdiskid field for link ([5ee0705](https://github.com/ydb-platform/ydb-embedded-ui/commit/5ee0705416aa31be9bee4be0776ecb8a61d3e82c))
         | 
| 28 | 
            +
             | 
| 3 29 | 
             
            ## [3.3.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.0...v3.3.1) (2023-01-31)
         | 
| 4 30 |  | 
| 5 31 |  | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            import type {IResponseError} from '../../../types/api/error';
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import i18n from '../i18n';
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            interface ResponseErrorProps {
         | 
| 6 | 
            +
                error: IResponseError;
         | 
| 7 | 
            +
                className?: string;
         | 
| 8 | 
            +
                defaultMessage?: string;
         | 
| 9 | 
            +
            }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            export const ResponseError = ({
         | 
| 12 | 
            +
                error,
         | 
| 13 | 
            +
                className,
         | 
| 14 | 
            +
                defaultMessage = i18n('responseError.defaultMessage'),
         | 
| 15 | 
            +
            }: ResponseErrorProps) => {
         | 
| 16 | 
            +
                return <div className={`error ${className}`}>{error.statusText || defaultMessage}</div>;
         | 
| 17 | 
            +
            };
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            export * from './ResponseError';
         | 
| @@ -2,7 +2,7 @@ import React from 'react'; | |
| 2 2 | 
             
            import cn from 'bem-cn-lite';
         | 
| 3 3 | 
             
            import PropTypes from 'prop-types';
         | 
| 4 4 |  | 
| 5 | 
            -
            import DataTable from '@ | 
| 5 | 
            +
            import DataTable from '@gravity-ui/react-data-table';
         | 
| 6 6 |  | 
| 7 7 | 
             
            import InfoViewer from '../InfoViewer/InfoViewer';
         | 
| 8 8 | 
             
            import EntityStatus from '../EntityStatus/EntityStatus';
         | 
| @@ -15,6 +15,7 @@ interface InfoViewerProps { | |
| 15 15 | 
             
                size?: 's';
         | 
| 16 16 | 
             
                className?: string;
         | 
| 17 17 | 
             
                multilineLabels?: boolean;
         | 
| 18 | 
            +
                renderEmptyState?: (props?: Pick<InfoViewerProps, 'title' | 'size'>) => ReactNode;
         | 
| 18 19 | 
             
            }
         | 
| 19 20 |  | 
| 20 21 | 
             
            const b = cn('info-viewer');
         | 
| @@ -26,28 +27,35 @@ const InfoViewer = ({ | |
| 26 27 | 
             
                size,
         | 
| 27 28 | 
             
                className,
         | 
| 28 29 | 
             
                multilineLabels,
         | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
                    { | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 30 | 
            +
                renderEmptyState,
         | 
| 31 | 
            +
            }: InfoViewerProps) => {
         | 
| 32 | 
            +
                if ((!info || !info.length) && renderEmptyState) {
         | 
| 33 | 
            +
                    return <>{renderEmptyState({title, size})}</>;
         | 
| 34 | 
            +
                }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                return (
         | 
| 37 | 
            +
                    <div className={b({size}, className)}>
         | 
| 38 | 
            +
                        {title && <div className={b('title')}>{title}</div>}
         | 
| 39 | 
            +
                        {info && info.length > 0 ? (
         | 
| 40 | 
            +
                            <div className={b('items')}>
         | 
| 41 | 
            +
                                {info.map((data, infoIndex) => (
         | 
| 42 | 
            +
                                    <div className={b('row')} key={data.label + infoIndex}>
         | 
| 43 | 
            +
                                        <div className={b('label')}>
         | 
| 44 | 
            +
                                            <div className={b('label-text', {multiline: multilineLabels})}>
         | 
| 45 | 
            +
                                                {data.label}
         | 
| 46 | 
            +
                                            </div>
         | 
| 47 | 
            +
                                            {dots && <div className={b('dots')} />}
         | 
| 39 48 | 
             
                                        </div>
         | 
| 40 | 
            -
                                        {dots && <div className={b('dots')} />}
         | 
| 41 | 
            -
                                    </div>
         | 
| 42 49 |  | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
            );
         | 
| 50 | 
            +
                                        <div className={b('value')}>{data.value}</div>
         | 
| 51 | 
            +
                                    </div>
         | 
| 52 | 
            +
                                ))}
         | 
| 53 | 
            +
                            </div>
         | 
| 54 | 
            +
                        ) : (
         | 
| 55 | 
            +
                            <>No {title} data</>
         | 
| 56 | 
            +
                        )}
         | 
| 57 | 
            +
                    </div>
         | 
| 58 | 
            +
                );
         | 
| 59 | 
            +
            };
         | 
| 52 60 |  | 
| 53 61 | 
             
            export default InfoViewer;
         | 
| @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            import type {TFollowerGroup, TPartitionConfig, TTableStats} from '../../../types/api/schema';
         | 
| 2 | 
            +
            import type {TMetrics} from '../../../types/api/tenant';
         | 
| 3 | 
            +
            import {formatCPU, formatBytes, formatNumber, formatBps, formatDateTime} from '../../../utils';
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            import {createInfoFormatter} from '../utils';
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            export const formatTabletMetricsItem = createInfoFormatter<TMetrics>({
         | 
| 8 | 
            +
                values: {
         | 
| 9 | 
            +
                    CPU: formatCPU,
         | 
| 10 | 
            +
                    Memory: formatBytes,
         | 
| 11 | 
            +
                    Storage: formatBytes,
         | 
| 12 | 
            +
                    Network: formatBps,
         | 
| 13 | 
            +
                    ReadThroughput: formatBps,
         | 
| 14 | 
            +
                    WriteThroughput: formatBps,
         | 
| 15 | 
            +
                },
         | 
| 16 | 
            +
                defaultValueFormatter: formatNumber,
         | 
| 17 | 
            +
            });
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            export const formatFollowerGroupItem = createInfoFormatter<TFollowerGroup>({
         | 
| 20 | 
            +
                values: {
         | 
| 21 | 
            +
                    FollowerCount: formatNumber,
         | 
| 22 | 
            +
                },
         | 
| 23 | 
            +
            });
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            export const formatPartitionConfigItem = createInfoFormatter<TPartitionConfig>({
         | 
| 26 | 
            +
                values: {
         | 
| 27 | 
            +
                    FollowerCount: formatNumber,
         | 
| 28 | 
            +
                    CrossDataCenterFollowerCount: formatNumber,
         | 
| 29 | 
            +
                },
         | 
| 30 | 
            +
            });
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            export const formatTableStatsItem = createInfoFormatter<TTableStats>({
         | 
| 33 | 
            +
                values: {
         | 
| 34 | 
            +
                    DataSize: formatBytes,
         | 
| 35 | 
            +
                    IndexSize: formatBytes,
         | 
| 36 | 
            +
                    LastAccessTime: formatDateTime,
         | 
| 37 | 
            +
                    LastUpdateTime: formatDateTime,
         | 
| 38 | 
            +
                },
         | 
| 39 | 
            +
                defaultValueFormatter: formatNumber,
         | 
| 40 | 
            +
            });
         | 
| @@ -1,19 +1,32 @@ | |
| 1 | 
            +
            import type {ReactNode} from 'react';
         | 
| 2 | 
            +
            import type {AxiosError} from 'axios';
         | 
| 1 3 | 
             
            import cn from 'bem-cn-lite';
         | 
| 2 4 |  | 
| 5 | 
            +
            import {Icon as UiKitIcon} from '@gravity-ui/uikit';
         | 
| 6 | 
            +
             | 
| 3 7 | 
             
            import Icon from '../Icon/Icon';
         | 
| 4 8 |  | 
| 9 | 
            +
            import questionIcon from '../../assets/icons/question.svg';
         | 
| 10 | 
            +
             | 
| 5 11 | 
             
            import './QueryExecutionStatus.scss';
         | 
| 6 12 |  | 
| 7 13 | 
             
            const b = cn('kv-query-execution-status');
         | 
| 8 14 |  | 
| 9 15 | 
             
            interface QueryExecutionStatusProps {
         | 
| 10 16 | 
             
                className?: string;
         | 
| 11 | 
            -
                 | 
| 17 | 
            +
                error?: AxiosError | Record<string, any>;
         | 
| 12 18 | 
             
            }
         | 
| 13 19 |  | 
| 14 | 
            -
             | 
| 15 | 
            -
                 | 
| 16 | 
            -
             | 
| 20 | 
            +
            export const QueryExecutionStatus = ({className, error}: QueryExecutionStatusProps) => {
         | 
| 21 | 
            +
                let icon: ReactNode;
         | 
| 22 | 
            +
                let label: string;
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                if (error?.code === 'ECONNABORTED') {
         | 
| 25 | 
            +
                    icon = <UiKitIcon data={questionIcon} size={16} />;
         | 
| 26 | 
            +
                    label = 'Connection aborted';
         | 
| 27 | 
            +
                } else {
         | 
| 28 | 
            +
                    const hasError = Boolean(error);
         | 
| 29 | 
            +
                    icon = (
         | 
| 17 30 | 
             
                        <Icon
         | 
| 18 31 | 
             
                            name={hasError ? 'failure' : 'success'}
         | 
| 19 32 | 
             
                            viewBox={hasError ? '0 0 512 512' : '0 0 16 16'}
         | 
| @@ -21,9 +34,14 @@ function QueryExecutionStatus({className, hasError}: QueryExecutionStatusProps) | |
| 21 34 | 
             
                            height={16}
         | 
| 22 35 | 
             
                            className={b('result-status-icon', {error: hasError})}
         | 
| 23 36 | 
             
                        />
         | 
| 24 | 
            -
             | 
| 37 | 
            +
                    );
         | 
| 38 | 
            +
                    label = hasError ? 'Failed' : 'Completed';
         | 
| 39 | 
            +
                }
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                return (
         | 
| 42 | 
            +
                    <div className={b(null, className)}>
         | 
| 43 | 
            +
                        {icon}
         | 
| 44 | 
            +
                        {label}
         | 
| 25 45 | 
             
                    </div>
         | 
| 26 46 | 
             
                );
         | 
| 27 | 
            -
            }
         | 
| 28 | 
            -
             | 
| 29 | 
            -
            export default QueryExecutionStatus;
         | 
| 47 | 
            +
            };
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            export * from './QueryExecutionStatus';
         | 
| @@ -1,8 +1,8 @@ | |
| 1 1 | 
             
            import {useMemo} from 'react';
         | 
| 2 2 | 
             
            import cn from 'bem-cn-lite';
         | 
| 3 3 |  | 
| 4 | 
            -
            import DataTable from '@ | 
| 5 | 
            -
            import type {Column, DataTableProps, Settings} from '@ | 
| 4 | 
            +
            import DataTable from '@gravity-ui/react-data-table';
         | 
| 5 | 
            +
            import type {Column, DataTableProps, Settings} from '@gravity-ui/react-data-table';
         | 
| 6 6 |  | 
| 7 7 | 
             
            import type {ColumnType, KeyValueRow} from '../../types/api/query';
         | 
| 8 8 | 
             
            import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
         | 
| @@ -79,11 +79,18 @@ function ContentWrapper(props) { | |
| 79 79 | 
             
                    <HistoryContext.Consumer>
         | 
| 80 80 | 
             
                        {(history) => (
         | 
| 81 81 | 
             
                            <Router history={history}>
         | 
| 82 | 
            -
                                < | 
| 83 | 
            -
                                    < | 
| 84 | 
            -
                                         | 
| 85 | 
            -
                                    </ | 
| 86 | 
            -
             | 
| 82 | 
            +
                                <Switch>
         | 
| 83 | 
            +
                                    <Route path={routes.auth}>
         | 
| 84 | 
            +
                                        <Authentication closable />
         | 
| 85 | 
            +
                                    </Route>
         | 
| 86 | 
            +
                                    <Route>
         | 
| 87 | 
            +
                                        <ThemeProvider theme={theme}>
         | 
| 88 | 
            +
                                            <div className={b({embedded: singleClusterMode})}>
         | 
| 89 | 
            +
                                                {isAuthenticated ? props.children : <Authentication />}
         | 
| 90 | 
            +
                                            </div>
         | 
| 91 | 
            +
                                        </ThemeProvider>
         | 
| 92 | 
            +
                                    </Route>
         | 
| 93 | 
            +
                                </Switch>
         | 
| 87 94 | 
             
                            </Router>
         | 
| 88 95 | 
             
                        )}
         | 
| 89 96 | 
             
                    </HistoryContext.Consumer>
         | 
| @@ -23,7 +23,7 @@ import UserSettings from '../UserSettings/UserSettings'; | |
| 23 23 |  | 
| 24 24 | 
             
            import routes, {createHref, CLUSTER_PAGES} from '../../routes';
         | 
| 25 25 |  | 
| 26 | 
            -
            import {logout | 
| 26 | 
            +
            import {logout} from '../../store/reducers/authentication';
         | 
| 27 27 | 
             
            import {getSettingValue, setSettingValue} from '../../store/reducers/settings';
         | 
| 28 28 |  | 
| 29 29 | 
             
            import {ASIDE_HEADER_COMPACT_KEY} from '../../utils/constants';
         | 
| @@ -43,10 +43,15 @@ interface MenuItem { | |
| 43 43 | 
             
            interface YbdInternalUserProps {
         | 
| 44 44 | 
             
                ydbUser?: string;
         | 
| 45 45 | 
             
                logout: VoidFunction;
         | 
| 46 | 
            -
                setIsNotAuthenticated: VoidFunction;
         | 
| 47 46 | 
             
            }
         | 
| 48 47 |  | 
| 49 | 
            -
            function YbdInternalUser({ydbUser, logout | 
| 48 | 
            +
            function YbdInternalUser({ydbUser, logout}: YbdInternalUserProps) {
         | 
| 49 | 
            +
                const history = useHistory();
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                const handleLoginClick = () => {
         | 
| 52 | 
            +
                    history.push(createHref(routes.auth, undefined, {returnUrl: encodeURI(location.href)}));
         | 
| 53 | 
            +
                };
         | 
| 54 | 
            +
             | 
| 50 55 | 
             
                return (
         | 
| 51 56 | 
             
                    <div className={b('internal-user')}>
         | 
| 52 57 | 
             
                        <div className={b('user-info-wrapper')}>
         | 
| @@ -58,7 +63,7 @@ function YbdInternalUser({ydbUser, logout, setIsNotAuthenticated}: YbdInternalUs | |
| 58 63 | 
             
                                <Icon data={signOutIcon} size={16} />
         | 
| 59 64 | 
             
                            </Button>
         | 
| 60 65 | 
             
                        ) : (
         | 
| 61 | 
            -
                            <Button view="flat-secondary"  | 
| 66 | 
            +
                            <Button view="flat-secondary" title="login" onClick={handleLoginClick}>
         | 
| 62 67 | 
             
                                <Icon data={signInIcon} size={16} />
         | 
| 63 68 | 
             
                            </Button>
         | 
| 64 69 | 
             
                        )}
         | 
| @@ -71,7 +76,6 @@ interface YdbUserDropdownProps { | |
| 71 76 | 
             
                ydbUser: {
         | 
| 72 77 | 
             
                    login?: string;
         | 
| 73 78 | 
             
                    logout: VoidFunction;
         | 
| 74 | 
            -
                    setIsNotAuthenticated: VoidFunction;
         | 
| 75 79 | 
             
                };
         | 
| 76 80 | 
             
                popupAnchor: React.RefObject<HTMLDivElement>;
         | 
| 77 81 | 
             
            }
         | 
| @@ -96,11 +100,7 @@ function YdbUserDropdown({isCompact, popupAnchor, ydbUser}: YdbUserDropdownProps | |
| 96 100 | 
             
                        onClosePopup={() => setIsUserDropdownVisible(false)}
         | 
| 97 101 | 
             
                        renderPopupContent={() => (
         | 
| 98 102 | 
             
                            <div className={b('ydb-user-wrapper')}>
         | 
| 99 | 
            -
                                <YbdInternalUser
         | 
| 100 | 
            -
                                    ydbUser={ydbUser.login}
         | 
| 101 | 
            -
                                    logout={ydbUser.logout}
         | 
| 102 | 
            -
                                    setIsNotAuthenticated={ydbUser.setIsNotAuthenticated}
         | 
| 103 | 
            -
                                />
         | 
| 103 | 
            +
                                <YbdInternalUser ydbUser={ydbUser.login} logout={ydbUser.logout} />
         | 
| 104 104 | 
             
                            </div>
         | 
| 105 105 | 
             
                        )}
         | 
| 106 106 | 
             
                    />
         | 
| @@ -112,7 +112,6 @@ interface AsideNavigationProps { | |
| 112 112 | 
             
                ydbUser: string;
         | 
| 113 113 | 
             
                compact: boolean;
         | 
| 114 114 | 
             
                logout: VoidFunction;
         | 
| 115 | 
            -
                setIsNotAuthenticated: VoidFunction;
         | 
| 116 115 | 
             
                setSettingValue: (name: string, value: string) => void;
         | 
| 117 116 | 
             
            }
         | 
| 118 117 |  | 
| @@ -260,7 +259,6 @@ function AsideNavigation(props: AsideNavigationProps) { | |
| 260 259 | 
             
                                        ydbUser={{
         | 
| 261 260 | 
             
                                            login: props.ydbUser,
         | 
| 262 261 | 
             
                                            logout: props.logout,
         | 
| 263 | 
            -
                                            setIsNotAuthenticated: props.setIsNotAuthenticated,
         | 
| 264 262 | 
             
                                        }}
         | 
| 265 263 | 
             
                                    />
         | 
| 266 264 | 
             
                                </React.Fragment>
         | 
| @@ -291,7 +289,6 @@ const mapStateToProps = (state: any) => { | |
| 291 289 |  | 
| 292 290 | 
             
            const mapDispatchToProps = {
         | 
| 293 291 | 
             
                logout,
         | 
| 294 | 
            -
                setIsNotAuthenticated,
         | 
| 295 292 | 
             
                setSettingValue,
         | 
| 296 293 | 
             
            };
         | 
| 297 294 |  | 
| @@ -1,19 +1,33 @@ | |
| 1 1 | 
             
            import {KeyboardEvent, useEffect, useState} from 'react';
         | 
| 2 | 
            +
            import {useDispatch} from 'react-redux';
         | 
| 3 | 
            +
            import {useHistory} from 'react-router';
         | 
| 2 4 | 
             
            import cn from 'bem-cn-lite';
         | 
| 3 | 
            -
             | 
| 5 | 
            +
             | 
| 4 6 | 
             
            import {Button, TextInput, Icon, Link as ExternalLink} from '@gravity-ui/uikit';
         | 
| 5 | 
            -
             | 
| 7 | 
            +
             | 
| 6 8 | 
             
            import {authenticate} from '../../store/reducers/authentication';
         | 
| 9 | 
            +
            import {useTypedSelector} from '../../utils/hooks';
         | 
| 7 10 |  | 
| 8 11 | 
             
            import ydbLogoIcon from '../../assets/icons/ydb.svg';
         | 
| 9 12 | 
             
            import showIcon from '../../assets/icons/show.svg';
         | 
| 10 13 | 
             
            import hideIcon from '../../assets/icons/hide.svg';
         | 
| 14 | 
            +
            import closeIcon from '../../assets/icons/close.svg';
         | 
| 11 15 |  | 
| 12 16 | 
             
            import './Authentication.scss';
         | 
| 13 17 |  | 
| 14 18 | 
             
            const b = cn('authentication');
         | 
| 15 19 |  | 
| 16 | 
            -
             | 
| 20 | 
            +
            interface AuthenticationProps {
         | 
| 21 | 
            +
                returnUrl?: string;
         | 
| 22 | 
            +
                closable?: boolean;
         | 
| 23 | 
            +
            }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            function Authentication({returnUrl, closable = false}: AuthenticationProps) {
         | 
| 26 | 
            +
                const dispatch = useDispatch();
         | 
| 27 | 
            +
                const history = useHistory();
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                const {error} = useTypedSelector((state) => state.authentication);
         | 
| 30 | 
            +
             | 
| 17 31 | 
             
                const [login, setLogin] = useState('');
         | 
| 18 32 | 
             
                const [pass, setPass] = useState('');
         | 
| 19 33 | 
             
                const [loginError, setLoginError] = useState('');
         | 
| @@ -40,7 +54,13 @@ function Authentication({authenticate, error}: any) { | |
| 40 54 | 
             
                };
         | 
| 41 55 |  | 
| 42 56 | 
             
                const onLoginClick = () => {
         | 
| 43 | 
            -
                     | 
| 57 | 
            +
                    // @ts-expect-error
         | 
| 58 | 
            +
                    // typed dispatch required, remove error expectation after adding it
         | 
| 59 | 
            +
                    dispatch(authenticate(login, pass)).then(() => {
         | 
| 60 | 
            +
                        if (returnUrl) {
         | 
| 61 | 
            +
                            history.replace(decodeURI(returnUrl));
         | 
| 62 | 
            +
                        }
         | 
| 63 | 
            +
                    });
         | 
| 44 64 | 
             
                };
         | 
| 45 65 |  | 
| 46 66 | 
             
                const onEnterClick = (e: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
         | 
| @@ -49,6 +69,10 @@ function Authentication({authenticate, error}: any) { | |
| 49 69 | 
             
                    }
         | 
| 50 70 | 
             
                };
         | 
| 51 71 |  | 
| 72 | 
            +
                const onClose = () => {
         | 
| 73 | 
            +
                    history.go(-1);
         | 
| 74 | 
            +
                };
         | 
| 75 | 
            +
             | 
| 52 76 | 
             
                const onTogglePasswordVisibility = () => {
         | 
| 53 77 | 
             
                    setShowPassword((prev) => !prev);
         | 
| 54 78 | 
             
                };
         | 
| @@ -106,18 +130,13 @@ function Authentication({authenticate, error}: any) { | |
| 106 130 | 
             
                                Sign in
         | 
| 107 131 | 
             
                            </Button>
         | 
| 108 132 | 
             
                        </form>
         | 
| 133 | 
            +
                        {closable && history.length > 1 && (
         | 
| 134 | 
            +
                            <Button onClick={onClose} className={b('close')}>
         | 
| 135 | 
            +
                                <Icon data={closeIcon} size={24} />
         | 
| 136 | 
            +
                            </Button>
         | 
| 137 | 
            +
                        )}
         | 
| 109 138 | 
             
                    </section>
         | 
| 110 139 | 
             
                );
         | 
| 111 140 | 
             
            }
         | 
| 112 141 |  | 
| 113 | 
            -
             | 
| 114 | 
            -
                return {
         | 
| 115 | 
            -
                    error: state.authentication.error,
         | 
| 116 | 
            -
                };
         | 
| 117 | 
            -
            }
         | 
| 118 | 
            -
             | 
| 119 | 
            -
            const mapDispatchToProps = {
         | 
| 120 | 
            -
                authenticate,
         | 
| 121 | 
            -
            };
         | 
| 122 | 
            -
             | 
| 123 | 
            -
            export default connect(mapStateToProps, mapDispatchToProps)(Authentication);
         | 
| 142 | 
            +
            export default Authentication;
         | 
| @@ -4,7 +4,7 @@ import _ from 'lodash'; | |
| 4 4 |  | 
| 5 5 | 
             
            import {ArrowToggle, Button, Popover} from '@gravity-ui/uikit';
         | 
| 6 6 |  | 
| 7 | 
            -
            import DataTable, {Column, Settings} from '@ | 
| 7 | 
            +
            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';
         | 
| @@ -295,10 +295,7 @@ export function PDisk(props: PDiskProps) { | |
| 295 295 | 
             
                                    value={data.PDiskId}
         | 
| 296 296 | 
             
                                    className={b('pdisk-title-id')}
         | 
| 297 297 | 
             
                                />
         | 
| 298 | 
            -
                                <PDiskTitleBadge
         | 
| 299 | 
            -
                                    value={getPDiskType(data)}
         | 
| 300 | 
            -
                                    className={b('pdisk-title-type')}
         | 
| 301 | 
            -
                                />
         | 
| 298 | 
            +
                                <PDiskTitleBadge value={getPDiskType(data)} className={b('pdisk-title-type')} />
         | 
| 302 299 | 
             
                                <ProgressViewer
         | 
| 303 300 | 
             
                                    value={data.TotalSize - data.AvailableSize}
         | 
| 304 301 | 
             
                                    capacity={data.TotalSize}
         | 
| @@ -306,12 +303,12 @@ export function PDisk(props: PDiskProps) { | |
| 306 303 | 
             
                                    colorizeProgress={true}
         | 
| 307 304 | 
             
                                    className={b('pdisk-title-size')}
         | 
| 308 305 | 
             
                                />
         | 
| 309 | 
            -
                                <PDiskTitleBadge
         | 
| 310 | 
            -
                                    label="VDisks"
         | 
| 311 | 
            -
                                    value={data.vDisks.length}
         | 
| 312 | 
            -
                                />
         | 
| 306 | 
            +
                                <PDiskTitleBadge label="VDisks" value={data.vDisks.length} />
         | 
| 313 307 | 
             
                            </div>
         | 
| 314 | 
            -
                            <Button | 
| 308 | 
            +
                            <Button
         | 
| 309 | 
            +
                                onClick={unfolded ? onClosePDiskDetails : onOpenPDiskDetails}
         | 
| 310 | 
            +
                                view="flat-secondary"
         | 
| 311 | 
            +
                            >
         | 
| 315 312 | 
             
                                <ArrowToggle direction={unfolded ? 'top' : 'bottom'} />
         | 
| 316 313 | 
             
                            </Button>
         | 
| 317 314 | 
             
                        </div>
         | 
| @@ -2,7 +2,7 @@ import {useCallback, useEffect} from 'react'; | |
| 2 2 | 
             
            import cn from 'bem-cn-lite';
         | 
| 3 3 | 
             
            import {useDispatch} from 'react-redux';
         | 
| 4 4 |  | 
| 5 | 
            -
            import DataTable from '@ | 
| 5 | 
            +
            import DataTable from '@gravity-ui/react-data-table';
         | 
| 6 6 |  | 
| 7 7 | 
             
            import {AccessDenied} from '../../components/Errors/403';
         | 
| 8 8 | 
             
            import {Illustration} from '../../components/Illustration';
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            import cn from 'bem-cn-lite';
         | 
| 2 | 
            -
            import DataTable, {Column} from '@ | 
| 2 | 
            +
            import DataTable, {Column} from '@gravity-ui/react-data-table';
         | 
| 3 3 | 
             
            import {Button, Popover} from '@gravity-ui/uikit';
         | 
| 4 4 |  | 
| 5 5 | 
             
            import Icon from '../../components/Icon/Icon';
         | 
| @@ -8,7 +8,7 @@ import PoolsGraph from '../../components/PoolsGraph/PoolsGraph'; | |
| 8 8 | 
             
            import ProgressViewer from '../../components/ProgressViewer/ProgressViewer';
         | 
| 9 9 | 
             
            import {TabletsStatistic} from '../../components/TabletsStatistic';
         | 
| 10 10 |  | 
| 11 | 
            -
            import { | 
| 11 | 
            +
            import {formatBytesToGigabyte} from '../../utils/index';
         | 
| 12 12 | 
             
            import {INodesPreparedEntity} from '../../types/store/nodes';
         | 
| 13 13 | 
             
            import {showTooltip as externalShowTooltip} from '../../store/reducers/tooltip';
         | 
| 14 14 |  | 
| @@ -100,7 +100,7 @@ export function getNodesColumns({ | |
| 100 100 | 
             
                        name: 'Uptime',
         | 
| 101 101 | 
             
                        header: 'Uptime',
         | 
| 102 102 | 
             
                        sortAccessor: ({StartTime}) => StartTime && -StartTime,
         | 
| 103 | 
            -
                        align: DataTable. | 
| 103 | 
            +
                        align: DataTable.RIGHT,
         | 
| 104 104 | 
             
                        width: '110px',
         | 
| 105 105 | 
             
                    },
         | 
| 106 106 | 
             
                    {
         | 
| @@ -110,7 +110,7 @@ export function getNodesColumns({ | |
| 110 110 | 
             
                        defaultOrder: DataTable.DESCENDING,
         | 
| 111 111 | 
             
                        render: ({row}) => {
         | 
| 112 112 | 
             
                            if (row.MemoryUsed) {
         | 
| 113 | 
            -
                                return  | 
| 113 | 
            +
                                return formatBytesToGigabyte(row.MemoryUsed);
         | 
| 114 114 | 
             
                            } else {
         | 
| 115 115 | 
             
                                return '—';
         | 
| 116 116 | 
             
                            }
         | 
| @@ -17,7 +17,7 @@ import {DiskStateProgressBar, EDiskStateSeverity} from '../DiskStateProgressBar' | |
| 17 17 | 
             
            import {PDiskPopup} from '../PDiskPopup';
         | 
| 18 18 | 
             
            import {VDisk} from '../VDisk';
         | 
| 19 19 |  | 
| 20 | 
            -
            import {NOT_AVAILABLE_SEVERITY} from '../utils';
         | 
| 20 | 
            +
            import {getUsageSeverityForPDisk, NOT_AVAILABLE_SEVERITY} from '../utils';
         | 
| 21 21 |  | 
| 22 22 | 
             
            import './PDisk.scss';
         | 
| 23 23 |  | 
| @@ -67,21 +67,6 @@ export const PDisk = ({nodeId, data: rawData = {}}: PDiskProps) => { | |
| 67 67 |  | 
| 68 68 | 
             
                const anchor = useRef(null);
         | 
| 69 69 |  | 
| 70 | 
            -
                useEffect(() => {
         | 
| 71 | 
            -
                    const newSeverity = getStateSeverity(data.State);
         | 
| 72 | 
            -
                    if (severity !== newSeverity) {
         | 
| 73 | 
            -
                        setSeverity(newSeverity);
         | 
| 74 | 
            -
                    }
         | 
| 75 | 
            -
                }, [data.State]);
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                const showPopup = () => {
         | 
| 78 | 
            -
                    setIsPopupVisible(true);
         | 
| 79 | 
            -
                };
         | 
| 80 | 
            -
             | 
| 81 | 
            -
                const hidePopup = () => {
         | 
| 82 | 
            -
                    setIsPopupVisible(false);
         | 
| 83 | 
            -
                };
         | 
| 84 | 
            -
             | 
| 85 70 | 
             
                const pdiskAllocatedPercent = useMemo(() => {
         | 
| 86 71 | 
             
                    const {AvailableSize, TotalSize} = data;
         | 
| 87 72 |  | 
| @@ -94,6 +79,29 @@ export const PDisk = ({nodeId, data: rawData = {}}: PDiskProps) => { | |
| 94 79 | 
             
                        : undefined;
         | 
| 95 80 | 
             
                }, [data]);
         | 
| 96 81 |  | 
| 82 | 
            +
                useEffect(() => {
         | 
| 83 | 
            +
                    const newStateSeverity = getStateSeverity(data.State);
         | 
| 84 | 
            +
                    const newSpaceSeverityFlag = getUsageSeverityForPDisk(pdiskAllocatedPercent || 0);
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    let newSeverity: number;
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    if (newStateSeverity === NOT_AVAILABLE_SEVERITY || !newSpaceSeverityFlag) {
         | 
| 89 | 
            +
                        newSeverity = newStateSeverity;
         | 
| 90 | 
            +
                    } else {
         | 
| 91 | 
            +
                        newSeverity = Math.max(newStateSeverity, EDiskStateSeverity[newSpaceSeverityFlag]);
         | 
| 92 | 
            +
                    }
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    setSeverity(newSeverity);
         | 
| 95 | 
            +
                }, [data.State, pdiskAllocatedPercent]);
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                const showPopup = () => {
         | 
| 98 | 
            +
                    setIsPopupVisible(true);
         | 
| 99 | 
            +
                };
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                const hidePopup = () => {
         | 
| 102 | 
            +
                    setIsPopupVisible(false);
         | 
| 103 | 
            +
                };
         | 
| 104 | 
            +
             | 
| 97 105 | 
             
                const renderVDisks = () => {
         | 
| 98 106 | 
             
                    if (!vdisks?.length) {
         | 
| 99 107 | 
             
                        return null;
         | 
| @@ -108,7 +116,7 @@ export const PDisk = ({nodeId, data: rawData = {}}: PDiskProps) => { | |
| 108 116 | 
             
                                    style={{
         | 
| 109 117 | 
             
                                        // 1 is small enough for empty disks to be of the minimum width
         | 
| 110 118 | 
             
                                        // but if all of them are empty, `flex-grow: 1` would size them evenly
         | 
| 111 | 
            -
                                        flexGrow:  | 
| 119 | 
            +
                                        flexGrow: Number(vdisk.AllocatedSize) || 1,
         | 
| 112 120 | 
             
                                    }}
         | 
| 113 121 | 
             
                                >
         | 
| 114 122 | 
             
                                    <VDisk data={vdisk} compact />
         |