Files
omniai-web/src/components/ErrorBoundary.tsx
T

153 lines
4.0 KiB
TypeScript
Raw Normal View History

2026-06-02 12:38:01 +08:00
import { Component, type ReactNode } from "react";
import { reportError } from "../utils/errorReporting";
interface Props {
children: ReactNode;
}
interface State {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary 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, info: React.ErrorInfo) {
console.error("[ErrorBoundary] Uncaught error:", error, info.componentStack);
reportError(error, "boundary");
}
handleReset = () => {
this.setState({ hasError: false, error: null });
};
handleReload = () => {
window.location.reload();
};
render() {
if (!this.state.hasError) {
return this.props.children;
}
return (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
minHeight: "100vh",
background: "var(--bg-base, #f5f5f5)",
padding: 24,
}}
>
<div
style={{
maxWidth: 480,
padding: "48px 40px",
borderRadius: 16,
background: "var(--surface-panel, #fff)",
boxShadow: "0 4px 24px rgba(0,0,0,0.08)",
textAlign: "center",
}}
>
<div style={{ fontSize: 48, marginBottom: 16 }}></div>
<h2
style={{
margin: "0 0 12px",
fontSize: 20,
fontWeight: 600,
color: "var(--text-primary, #1a1a1a)",
}}
>
</h2>
<p
style={{
margin: "0 0 24px",
fontSize: 14,
color: "var(--text-secondary, #666)",
lineHeight: 1.6,
}}
>
<br />
</p>
{this.state.error && (
<details
style={{
marginBottom: 24,
padding: 12,
borderRadius: 8,
background: "var(--bg-elevated, #f0f0f0)",
textAlign: "left",
fontSize: 12,
color: "var(--text-secondary, #666)",
}}
>
<summary style={{ cursor: "pointer", fontWeight: 500 }}>
</summary>
<pre
style={{
marginTop: 8,
whiteSpace: "pre-wrap",
wordBreak: "break-word",
fontFamily: "monospace",
fontSize: 11,
}}
>
{this.state.error.message}
</pre>
</details>
)}
<div style={{ display: "flex", gap: 12, justifyContent: "center" }}>
<button
type="button"
onClick={this.handleReset}
style={{
padding: "10px 24px",
borderRadius: 8,
border: "1px solid var(--border-normal, #ddd)",
background: "transparent",
color: "var(--text-primary, #1a1a1a)",
fontSize: 14,
cursor: "pointer",
}}
>
</button>
<button
type="button"
onClick={this.handleReload}
style={{
padding: "10px 24px",
borderRadius: 8,
border: "none",
background: "var(--accent, #0d9488)",
color: "#fff",
fontSize: 14,
fontWeight: 500,
cursor: "pointer",
}}
>
</button>
</div>
</div>
</div>
);
}
}
export default ErrorBoundary;