159 lines
4.3 KiB
TypeScript
159 lines
4.3 KiB
TypeScript
import React, { Component, ReactNode } from "react";
|
||
import { Box, Button, Typography, Paper, Container } from "@mui/material";
|
||
import { RefreshCw, Home, AlertTriangle } from "lucide-react";
|
||
|
||
interface Props {
|
||
children: ReactNode;
|
||
}
|
||
|
||
interface State {
|
||
hasError: boolean;
|
||
error: Error | null;
|
||
}
|
||
|
||
export class GlobalErrorBoundary extends Component<Props, State> {
|
||
constructor(props: Props) {
|
||
super(props);
|
||
this.state = {
|
||
hasError: false,
|
||
error: null,
|
||
};
|
||
}
|
||
|
||
static getDerivedStateFromError(error: Error): State {
|
||
return {
|
||
hasError: true,
|
||
error,
|
||
};
|
||
}
|
||
|
||
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
||
console.error("❌ GlobalErrorBoundary: Критическая ошибка приложения", {
|
||
error: error.message,
|
||
stack: error.stack,
|
||
componentStack: errorInfo.componentStack,
|
||
});
|
||
}
|
||
|
||
handleReset = () => {
|
||
this.setState({
|
||
hasError: false,
|
||
error: null,
|
||
});
|
||
window.location.reload();
|
||
};
|
||
|
||
handleGoHome = () => {
|
||
this.setState({
|
||
hasError: false,
|
||
error: null,
|
||
});
|
||
window.location.href = "/";
|
||
};
|
||
|
||
render() {
|
||
if (this.state.hasError) {
|
||
return (
|
||
<Box
|
||
sx={{
|
||
minHeight: "100vh",
|
||
display: "flex",
|
||
justifyContent: "center",
|
||
alignItems: "center",
|
||
backgroundColor: "background.default",
|
||
p: 2,
|
||
}}
|
||
>
|
||
<Container maxWidth="sm">
|
||
<Paper
|
||
elevation={3}
|
||
sx={{
|
||
p: 4,
|
||
textAlign: "center",
|
||
}}
|
||
>
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
justifyContent: "center",
|
||
mb: 3,
|
||
}}
|
||
>
|
||
<AlertTriangle size={64} color="#f44336" />
|
||
</Box>
|
||
|
||
<Typography variant="h4" component="h1" gutterBottom>
|
||
Упс! Что-то пошло не так
|
||
</Typography>
|
||
|
||
<Typography variant="body1" color="text.secondary" sx={{ mb: 3 }}>
|
||
Приложение столкнулось с неожиданной ошибкой. Попробуйте
|
||
перезагрузить страницу или вернуться на главную.
|
||
</Typography>
|
||
|
||
{this.state.error?.message && (
|
||
<Paper
|
||
variant="outlined"
|
||
sx={{
|
||
p: 2,
|
||
mb: 3,
|
||
backgroundColor: "error.light",
|
||
color: "error.contrastText",
|
||
textAlign: "left",
|
||
}}
|
||
>
|
||
<Typography
|
||
variant="caption"
|
||
sx={{ fontWeight: "bold", display: "block", mb: 1 }}
|
||
>
|
||
Информация об ошибке:
|
||
</Typography>
|
||
<Typography
|
||
variant="caption"
|
||
sx={{
|
||
fontFamily: "monospace",
|
||
fontSize: "0.75rem",
|
||
wordBreak: "break-word",
|
||
display: "block",
|
||
}}
|
||
>
|
||
{this.state.error.message}
|
||
</Typography>
|
||
</Paper>
|
||
)}
|
||
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
gap: 2,
|
||
justifyContent: "center",
|
||
flexWrap: "wrap",
|
||
}}
|
||
>
|
||
<Button
|
||
variant="outlined"
|
||
startIcon={<Home size={16} />}
|
||
onClick={this.handleGoHome}
|
||
size="large"
|
||
>
|
||
На главную
|
||
</Button>
|
||
<Button
|
||
variant="contained"
|
||
startIcon={<RefreshCw size={16} />}
|
||
onClick={this.handleReset}
|
||
size="large"
|
||
>
|
||
Перезагрузить
|
||
</Button>
|
||
</Box>
|
||
</Paper>
|
||
</Container>
|
||
</Box>
|
||
);
|
||
}
|
||
|
||
return this.props.children;
|
||
}
|
||
}
|