mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(console): add react error boundary
This commit is contained in:
parent
51f3b5c091
commit
9c3417c23a
3 changed files with 109 additions and 7 deletions
|
@ -2,6 +2,7 @@ import { useLogto } from '@logto/react';
|
|||
import React, { useEffect } from 'react';
|
||||
import { Outlet, useHref } from 'react-router-dom';
|
||||
|
||||
import ErrorBoundary from '../ErrorBoundary';
|
||||
import Sidebar from './components/Sidebar';
|
||||
import Topbar from './components/Topbar';
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -36,15 +37,17 @@ const AppContent = ({ theme }: Props) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={styles.app}>
|
||||
<Topbar />
|
||||
<div className={styles.content}>
|
||||
<Sidebar />
|
||||
<div className={styles.main}>
|
||||
<Outlet />
|
||||
<ErrorBoundary>
|
||||
<div className={styles.app}>
|
||||
<Topbar />
|
||||
<div className={styles.content}>
|
||||
<Sidebar />
|
||||
<div className={styles.main}>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.container {
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-layer-1);
|
||||
color: var(--color-text);
|
||||
padding: _.unit(6);
|
||||
width: 858px;
|
||||
min-height: 100%;
|
||||
margin: 0 auto;
|
||||
|
||||
> *:not(:first-child) {
|
||||
margin-top: _.unit(6);
|
||||
}
|
||||
|
||||
img {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
img,
|
||||
h2 {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
details {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
}
|
65
packages/console/src/components/ErrorBoundary/index.tsx
Normal file
65
packages/console/src/components/ErrorBoundary/index.tsx
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { conditional } from '@silverhand/essentials';
|
||||
import React, { Component, ReactNode } from 'react';
|
||||
import { Namespace, TFunction, withTranslation } from 'react-i18next';
|
||||
|
||||
import ErrorImage from '@/assets/images/table-error.svg';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
t: TFunction<Namespace, 'admin_console'>;
|
||||
};
|
||||
|
||||
type State = {
|
||||
callStack?: string;
|
||||
componentStack?: string;
|
||||
errorMessage?: string;
|
||||
hasError: boolean;
|
||||
};
|
||||
|
||||
class ErrorBoundary extends Component<Props, State> {
|
||||
static getDerivedStateFromError(error: Error) {
|
||||
const errorMessage = conditional(
|
||||
typeof error === 'object' && typeof error.message === 'string' && error.message
|
||||
);
|
||||
|
||||
const callStack = conditional(
|
||||
typeof error === 'object' &&
|
||||
typeof error.stack === 'string' &&
|
||||
error.stack.split('\n').slice(1).join('\n')
|
||||
);
|
||||
|
||||
return { callStack, errorMessage, hasError: true };
|
||||
}
|
||||
|
||||
public state: State = {
|
||||
callStack: undefined,
|
||||
errorMessage: undefined,
|
||||
hasError: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children, t } = this.props;
|
||||
const { callStack, errorMessage, hasError } = this.state;
|
||||
|
||||
if (hasError) {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.wrapper}>
|
||||
<img src={ErrorImage} alt="oops" />
|
||||
<h2>{t('errors.something_went_wrong')}</h2>
|
||||
<details open>
|
||||
<summary>{errorMessage}</summary>
|
||||
{callStack}
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation()(ErrorBoundary);
|
Loading…
Reference in a new issue