import { Component, type ReactNode, type ErrorInfo } from "react"; import { AlertTriangle } from "lucide-react"; interface ErrorBoundaryProps { children: ReactNode; fallback?: ReactNode; level?: "page" | "modal" | "root"; onError?: (error: Error, errorInfo: ErrorInfo) => void; } interface ErrorBoundaryState { hasError: boolean; error: Error | null; } export class ErrorBoundary extends Component { constructor(props: ErrorBoundaryProps) { super(props); this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error: Error): ErrorBoundaryState { return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: ErrorInfo): void { console.error("[ErrorBoundary]", error, errorInfo); this.props.onError?.(error, errorInfo); } resetErrorBoundary = (): void => { this.setState({ hasError: false, error: null }); }; render(): ReactNode { if (!this.state.hasError) { return this.props.children; } if (this.props.fallback) { return this.props.fallback; } const level = this.props.level ?? "page"; const isModal = level === "modal"; const title = isModal ? "This section encountered an error" : "Something went wrong"; return (
{title}
{this.state.error && (
{this.state.error.message}
)}
); } } export function PageErrorBoundary({ children, onError }: { children: ReactNode; onError?: (error: Error, errorInfo: ErrorInfo) => void }) { return ( {children} ); } export function ModalErrorBoundary({ children, onError }: { children: ReactNode; onError?: (error: Error, errorInfo: ErrorInfo) => void }) { return ( {children} ); } export function RootErrorBoundary({ children, onError }: { children: ReactNode; onError?: (error: Error, errorInfo: ErrorInfo) => void }) { return ( {children} ); }