39926-vm/frontend/src/components/ErrorBoundary.tsx
Flatlogic Bot e8ed8d174b 2
2026-05-08 09:12:53 +00:00

119 lines
3.2 KiB
TypeScript

import React, { Component, ErrorInfo, ReactNode } from "react";
import { mdiAlertCircle } from "@mdi/js";
import BaseIcon from "./BaseIcon";
type ErrorBoundaryProps = {
children: ReactNode;
};
type ErrorBoundaryState = {
hasError: boolean;
error: Error | null;
errorInfo: ErrorInfo | null;
showStack: boolean;
};
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null,
showStack: false,
};
}
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {
return {
hasError: true,
error,
};
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error("Error caught in boundary:", error, errorInfo);
this.setState({
errorInfo,
});
}
toggleStack = () => {
this.setState((prevState) => ({
showStack: !prevState.showStack,
}));
};
resetError = () => {
this.setState({
hasError: false,
error: null,
errorInfo: null,
showStack: false,
});
};
render() {
if (!this.state.hasError) {
return this.props.children;
}
const { error, errorInfo, showStack } = this.state;
const errorMessage = error?.message || "An unexpected error occurred";
const stackTrace =
errorInfo?.componentStack || error?.stack || "No stack trace available";
return (
<div className="flex min-h-screen items-center justify-center bg-slate-100 p-6">
<div className="w-full max-w-xl rounded-2xl border border-rose-200 bg-white p-8 shadow-sm">
<div className="flex items-center gap-3">
<div className="rounded-2xl bg-rose-50 p-3 text-rose-600">
<BaseIcon path={mdiAlertCircle} size={28} />
</div>
<div>
<h1 className="text-xl font-semibold text-slate-950">
Something went wrong
</h1>
<p className="mt-1 text-sm text-slate-600">
The app stopped on a visible frontend error.
</p>
</div>
</div>
<div className="mt-6 rounded-xl bg-rose-50 p-4 font-mono text-sm text-rose-800">
{errorMessage}
</div>
{showStack && (
<pre className="mt-4 max-h-72 overflow-auto rounded-xl bg-slate-950 p-4 text-xs text-slate-100">
{stackTrace}
</pre>
)}
<div className="mt-6 flex flex-wrap gap-3">
<button
type="button"
onClick={this.resetError}
className="rounded-xl bg-blue-600 px-4 py-2 text-sm font-semibold text-white hover:bg-blue-500"
>
Try again
</button>
<button
type="button"
onClick={this.toggleStack}
className="rounded-xl border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50"
>
{showStack ? "Hide details" : "Show details"}
</button>
</div>
</div>
</div>
);
}
}
export default ErrorBoundary;