import React from 'react'
import { AlertTriangle, RefreshCw } from 'lucide-react'
import { Button } from '../ui/Button'
interface ErrorBoundaryState {
hasError: boolean
error: Error | null
errorInfo: React.ErrorInfo | null
}
interface ErrorBoundaryProps {
children: React.ReactNode
fallback?: React.ComponentType<{ error: Error; resetError: () => void }>
}
export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props)
this.state = { hasError: false, error: null, errorInfo: null }
}
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {
return { hasError: true, error }
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
this.setState({
error,
errorInfo
})
// Log error to monitoring service
console.error('Error Boundary caught an error:', error, errorInfo)
// In production, send to error monitoring service
// errorMonitoringService.captureException(error, { extra: errorInfo })
}
resetError = () => {
this.setState({ hasError: false, error: null, errorInfo: null })
}
render() {
if (this.state.hasError) {
if (this.props.fallback) {
const FallbackComponent = this.props.fallback
return <FallbackComponent error={this.state.error!} resetError={this.resetError} />
}
return <DefaultErrorFallback error={this.state.error!} resetError={this.resetError} />
}
return this.props.children
}
}
interface DefaultErrorFallbackProps {
error: Error
resetError: () => void
}
function DefaultErrorFallback({ error, resetError }: DefaultErrorFallbackProps) {
return (
<div className="min-h-screen flex items-center justify-center p-4 bg-background">
<div className="glass rounded-lg p-8 max-w-md w-full text-center">
<AlertTriangle className="w-16 h-16 mx-auto mb-6 text-destructive" />
<h1 className="text-2xl font-bold mb-4">Something went wrong</h1>
<p className="text-muted-foreground mb-6">
We're sorry, but something unexpected happened. Please try refreshing the page.
</p>
<div className="space-y-4">
<Button onClick={resetError} className="w-full gap-2">
<RefreshCw className="w-4 h-4" />
Try Again
</Button>
<Button
variant="outline"
onClick={() => window.location.reload()}
className="w-full gap-2"
>
<RefreshCw className="w-4 h-4" />
Reload Page
</Button>
</div>
{process.env.NODE_ENV === 'development' && (
<details className="mt-6 text-left">
<summary className="cursor-pointer text-sm font-medium text-muted-foreground hover:text-foreground">
Error Details (Development)
</summary>
<pre className="mt-2 p-3 bg-muted rounded text-xs overflow-auto max-h-40">
{error.message}
{error.stack && '\n\n' + error.stack}
</pre>
</details>
)}
</div>
</div>
)
}
// Hook for functional components to use error boundary
export function useErrorHandler() {
return (error: Error, errorInfo?: { componentStack?: string }) => {
console.error('Error caught by hook:', error, errorInfo)
// In production, send to error monitoring
// errorMonitoringService.captureException(error, { extra: errorInfo })
// Could trigger a toast notification here
// toast.error('An unexpected error occurred')
}
}