mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
103 lines
2.8 KiB
TypeScript
103 lines
2.8 KiB
TypeScript
import { Component, type ErrorInfo, type ReactNode } from "react";
|
|
|
|
import { captureException } from "@/lib/posthog";
|
|
|
|
interface ErrorBoundaryProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
interface ErrorBoundaryState {
|
|
error: Error | null;
|
|
}
|
|
|
|
/**
|
|
* App-wide error boundary. Without this, any render-time exception unmounts the
|
|
* React tree and the user sees a blank white screen. This surfaces the actual
|
|
* error message + stack so failures are diagnosable in place.
|
|
*/
|
|
export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
state: ErrorBoundaryState = { error: null };
|
|
|
|
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
|
return { error };
|
|
}
|
|
|
|
componentDidCatch(error: Error, info: ErrorInfo) {
|
|
captureException(error, { componentStack: info.componentStack });
|
|
// eslint-disable-next-line no-console
|
|
console.error("[ErrorBoundary] Uncaught render error:", error, info.componentStack);
|
|
}
|
|
|
|
handleReset = () => this.setState({ error: null });
|
|
|
|
render() {
|
|
const { error } = this.state;
|
|
|
|
if (!error) return this.props.children;
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
minHeight: "100vh",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
padding: 24,
|
|
background: "#f8fafc",
|
|
fontFamily: "system-ui, sans-serif",
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
maxWidth: 720,
|
|
width: "100%",
|
|
background: "#fff",
|
|
border: "1px solid #fecaca",
|
|
borderRadius: 12,
|
|
padding: 24,
|
|
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
}}
|
|
>
|
|
<h2 style={{ margin: 0, color: "#b91c1c", fontSize: 18 }}>Something went wrong</h2>
|
|
<p style={{ color: "#64748b", fontSize: 14 }}>
|
|
A render error was caught. Details below — share this with the developer.
|
|
</p>
|
|
<pre
|
|
style={{
|
|
whiteSpace: "pre-wrap",
|
|
wordBreak: "break-word",
|
|
background: "#0f172a",
|
|
color: "#fca5a5",
|
|
padding: 16,
|
|
borderRadius: 8,
|
|
fontSize: 12,
|
|
maxHeight: 320,
|
|
overflow: "auto",
|
|
}}
|
|
>
|
|
{error.message}
|
|
{"\n\n"}
|
|
{error.stack}
|
|
</pre>
|
|
<button
|
|
type="button"
|
|
onClick={this.handleReset}
|
|
style={{
|
|
marginTop: 12,
|
|
padding: "8px 16px",
|
|
border: "none",
|
|
borderRadius: 8,
|
|
background: "#0f766e",
|
|
color: "#fff",
|
|
cursor: "pointer",
|
|
fontSize: 14,
|
|
}}
|
|
>
|
|
Dismiss
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|