'use client'
import { Component, type ReactNode } from 'react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Alert, AlertDescription } from '@/components/ui/alert'
interface Props {
children: ReactNode
fallback?: ReactNode
}
interface State {
hasError: boolean
error: Error | null
}
/**
* Error Boundary Component
* Catches JavaScript errors anywhere in the child component tree
* and displays a fallback UI instead of crashing the entire app
*/
export 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, errorInfo: React.ErrorInfo) {
// Log error to console in development
console.error('ErrorBoundary caught an error:', error, errorInfo)
// In production, you could send this to an error tracking service
// Example: Sentry.captureException(error, { extra: errorInfo })
}
handleReset = () => {
this.setState({ hasError: false, error: null })
}
render() {
if (this.state.hasError) {
// If custom fallback is provided, use it
if (this.props.fallback) {
return this.props.fallback
}
// Default fallback UI
return (
<div className="flex min-h-screen items-center justify-center p-4">
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle className="text-destructive">
Something went wrong
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Alert variant="destructive">
<AlertDescription>
{this.state.error?.message ||
'An unexpected error occurred. Please try again.'}
</AlertDescription>
</Alert>
{process.env.NODE_ENV === 'development' && (
<details className="rounded-md bg-muted p-4">
<summary className="cursor-pointer text-sm font-medium">
Error Details (Development Only)
</summary>
<pre className="mt-2 overflow-auto text-xs">
{this.state.error?.stack}
</pre>
</details>
)}
<div className="flex gap-2">
<Button onClick={this.handleReset} variant="default">
Try Again
</Button>
<Button
onClick={() => (window.location.href = '/')}
variant="outline"
>
Go Home
</Button>
</div>
</CardContent>
</Card>
</div>
)
}
return this.props.children
}
}
/**
* Simplified error fallback component for inline errors
*/
export function ErrorFallback({
error,
reset,
}: {
error: Error
reset?: () => void
}) {
return (
<Alert variant="destructive">
<AlertDescription className="flex items-center justify-between">
<span>{error.message || 'An error occurred'}</span>
{reset && (
<Button onClick={reset} size="sm" variant="outline">
Retry
</Button>
)}
</AlertDescription>
</Alert>
)
}