From 4a500f01a24f3ac30e5166f7799ab5227cc85d6c Mon Sep 17 00:00:00 2001 From: Kanstantsin Autushka Date: Wed, 22 Jan 2025 16:59:05 +0300 Subject: [PATCH] feat(Navigation/NavigationError): add copy button and impove error details [YTFRONT-4049] --- .../components/ErrorDetails/ErrorDetails.js | 4 +++ .../components/ErrorDetails/ErrorDetails.scss | 5 +++ .../pages/navigation/Navigation/Navigation.js | 11 ++++-- .../NavigationError/NavigationError.tsx | 17 ++++++--- .../RequestPermission/RequestPermission.scss | 4 ++- .../NavigationError/helpers/helpers.ts | 35 +++++++++++++++++++ 6 files changed, 68 insertions(+), 8 deletions(-) diff --git a/packages/ui/src/ui/components/ErrorDetails/ErrorDetails.js b/packages/ui/src/ui/components/ErrorDetails/ErrorDetails.js index e91d18656..84d274008 100644 --- a/packages/ui/src/ui/components/ErrorDetails/ErrorDetails.js +++ b/packages/ui/src/ui/components/ErrorDetails/ErrorDetails.js @@ -1,5 +1,6 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; +import {ClipboardButton} from '@gravity-ui/uikit'; import Link from '../../components/Link/Link'; import block from 'bem-cn-lite'; import ypath from '../../common/thor/ypath'; @@ -107,13 +108,16 @@ export default class ErrorDetails extends Component { } renderTabs() { + const {error} = this.props; const {currentTab} = this.state; const items = this.prepareTabs(); + const text = unipika.formatFromYSON(error.attributes, {asHTML: false}); return (
+
); } diff --git a/packages/ui/src/ui/components/ErrorDetails/ErrorDetails.scss b/packages/ui/src/ui/components/ErrorDetails/ErrorDetails.scss index 7229c8d36..3dc801570 100644 --- a/packages/ui/src/ui/components/ErrorDetails/ErrorDetails.scss +++ b/packages/ui/src/ui/components/ErrorDetails/ErrorDetails.scss @@ -30,6 +30,11 @@ } &__tabs { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + span { color: var(--g-color-text-complementary); font-weight: 700; diff --git a/packages/ui/src/ui/pages/navigation/Navigation/Navigation.js b/packages/ui/src/ui/pages/navigation/Navigation/Navigation.js index 754b612b0..eeca99757 100644 --- a/packages/ui/src/ui/pages/navigation/Navigation/Navigation.js +++ b/packages/ui/src/ui/pages/navigation/Navigation/Navigation.js @@ -3,6 +3,7 @@ import {StickyContainer} from 'react-sticky'; import {connect, useSelector} from 'react-redux'; import PropTypes from 'prop-types'; import hammer from '../../../common/hammer'; +import unipika from '../../../common/thor/unipika'; import cn from 'bem-cn-lite'; import map_ from 'lodash/map'; @@ -22,6 +23,7 @@ import ContentViewer from './ContentViewer/ContentViewer'; import {checkContentIsSupported} from './ContentViewer/helpers'; import Tabs from '../../../components/Tabs/Tabs'; import {NavigationError} from './NavigationError'; +import { getNavigationPathAttributes } from '../../../store/selectors/navigation/navigation'; import {Tab} from '../../../constants/navigation'; import {LOADING_STATUS} from '../../../constants/index'; @@ -29,6 +31,7 @@ import {LOADING_STATUS} from '../../../constants/index'; import {onTransactionChange, setMode, updateView} from '../../../store/actions/navigation'; import { + getAttributes, getError, getIdmSupport, getLoadState, @@ -247,15 +250,15 @@ class Navigation extends Component { cluster, path, } = this.props; - + console.log(message, details) return ( ); } render() { - const {loaded, hasError} = this.props; - + const {attributes, loaded, hasError, pathAttributes} = this.props; + console.log(attributes, pathAttributes, "hello") return (
@@ -286,6 +289,8 @@ function mapStateToProps(state) { const hasError = loadState === LOADING_STATUS.ERROR; const loaded = loadState === LOADING_STATUS.LOADED; return { + attributes: getAttributes(state), + pathAttributes: getNavigationPathAttributes(state), path: getPath(state), mode: getEffectiveMode(state), type: getType(state), diff --git a/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/NavigationError.tsx b/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/NavigationError.tsx index 9314a2e84..121d01fe5 100644 --- a/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/NavigationError.tsx +++ b/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/NavigationError.tsx @@ -1,6 +1,6 @@ import React from 'react'; import cn from 'bem-cn-lite'; -import {Flex, Text} from '@gravity-ui/uikit'; +import {ClipboardButton, Flex, Text} from '@gravity-ui/uikit'; import Error from '../../../../components/Error/Error'; import {NavigationErrorImage} from './NavigationErrorImage'; @@ -8,7 +8,7 @@ import ErrorDetails from '../../../../components/ErrorDetails/ErrorDetails'; import {RequestPermission} from './RequestPermission'; import {getPermissionDeniedError} from '../../../../utils/errors'; import {YTError} from '../../../../../@types/types'; -import {getErrorTitle, getLeadingErrorCode} from './helpers'; +import {getErrorTitle, getLeadingErrorCode, formatForCopy} from './helpers'; import './NavigationError.scss'; @@ -26,8 +26,8 @@ function PrettyError(props: Props) { const code = getLeadingErrorCode(details); const error = code == 901 ? getPermissionDeniedError(details)! : details; - const title = getErrorTitle(error, path); + const errorInfo = formatForCopy(details); return ( @@ -37,7 +37,16 @@ function PrettyError(props: Props) { {title} - {code === 901 && } + + {code === 901 && } + + Copy error details + + ); diff --git a/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/RequestPermission/RequestPermission.scss b/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/RequestPermission/RequestPermission.scss index 8891a7afe..4d441df76 100644 --- a/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/RequestPermission/RequestPermission.scss +++ b/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/RequestPermission/RequestPermission.scss @@ -1,8 +1,10 @@ +@import '../NavigationError.scss'; + .request-permission { &__request-permissions-button { --_--height: auto; width: fit-content; - + padding: 5px 12px; } } diff --git a/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/helpers/helpers.ts b/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/helpers/helpers.ts index c470d5338..5b9e9376d 100644 --- a/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/helpers/helpers.ts +++ b/packages/ui/src/ui/pages/navigation/Navigation/NavigationError/helpers/helpers.ts @@ -1,3 +1,5 @@ +import unipika from '../../../../../common/thor/unipika'; + import {getYtErrorCode} from '../../../../../utils/errors'; import {YTError} from '../../../../../../@types/types'; import {UnipikaValue} from '../../../../../components/Yson/StructuredYson/StructuredYsonTypes'; @@ -85,3 +87,36 @@ export function getLeadingErrorCode(error: YTError): ErrorCode | undefined { return; } + +export function formatForCopy(error: YTError) { + const formatSettings = {asHTML: false}; + let res = ''; + + const errorTraversal = (e: YTError) => { + if (e.message) { + // replace needed to truncate extra \ + // for situations like: Access denied for user \"username\": \"role\" + res += `${unipika.formatFromYSON(e.message, formatSettings)}`.replace(/\\"/g, '"'); + } + + if (e.code) { + res += `[${unipika.formatFromYSON(e.code, formatSettings)}]` + } + + res += '\n' + + if (e.attributes) { + res += `${unipika.formatFromYSON(e.attributes, formatSettings)}\n`; + } + + if (e.inner_errors) { + for (let err of e.inner_errors) { + errorTraversal(err); + } + } + } + + errorTraversal(error); + + return res; +}