0
Fork 0
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:
Charles Zhao 2022-04-15 18:51:11 +08:00
parent 51f3b5c091
commit 9c3417c23a
No known key found for this signature in database
GPG key ID: 4858774754C92DF2
3 changed files with 109 additions and 7 deletions

View file

@ -2,6 +2,7 @@ import { useLogto } from '@logto/react';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { Outlet, useHref } from 'react-router-dom'; import { Outlet, useHref } from 'react-router-dom';
import ErrorBoundary from '../ErrorBoundary';
import Sidebar from './components/Sidebar'; import Sidebar from './components/Sidebar';
import Topbar from './components/Topbar'; import Topbar from './components/Topbar';
import * as styles from './index.module.scss'; import * as styles from './index.module.scss';
@ -36,15 +37,17 @@ const AppContent = ({ theme }: Props) => {
} }
return ( return (
<div className={styles.app}> <ErrorBoundary>
<Topbar /> <div className={styles.app}>
<div className={styles.content}> <Topbar />
<Sidebar /> <div className={styles.content}>
<div className={styles.main}> <Sidebar />
<Outlet /> <div className={styles.main}>
<Outlet />
</div>
</div> </div>
</div> </div>
</div> </ErrorBoundary>
); );
}; };

View file

@ -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;
}
}
}

View 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);