Spaces:
Sleeping
Sleeping
| 'use client' | |
| import React from 'react' | |
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' | |
| import { Button } from '@/components/ui/button' | |
| import { AlertTriangle, RefreshCw } from 'lucide-react' | |
| interface ErrorBoundaryState { | |
| hasError: boolean | |
| error?: Error | |
| errorInfo?: React.ErrorInfo | |
| } | |
| 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 } | |
| } | |
| static getDerivedStateFromError(error: Error): ErrorBoundaryState { | |
| return { | |
| hasError: true, | |
| error | |
| } | |
| } | |
| componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { | |
| console.error('Error caught by boundary:', error, errorInfo) | |
| this.setState({ | |
| error, | |
| errorInfo | |
| }) | |
| } | |
| resetError = () => { | |
| this.setState({ hasError: false, error: undefined, errorInfo: undefined }) | |
| } | |
| render() { | |
| if (this.state.hasError) { | |
| if (this.props.fallback) { | |
| const FallbackComponent = this.props.fallback | |
| return <FallbackComponent error={this.state.error} resetError={this.resetError} /> | |
| } | |
| return ( | |
| <div className="min-h-screen flex items-center justify-center bg-background p-4"> | |
| <Card className="w-full max-w-md"> | |
| <CardHeader className="text-center"> | |
| <div className="mx-auto w-12 h-12 rounded-full bg-red-100 dark:bg-red-900/20 flex items-center justify-center mb-4"> | |
| <AlertTriangle className="w-6 h-6 text-red-600 dark:text-red-400" /> | |
| </div> | |
| <CardTitle className="text-red-900 dark:text-red-100">Something went wrong</CardTitle> | |
| <CardDescription> | |
| An unexpected error occurred. Please try refreshing the page. | |
| </CardDescription> | |
| </CardHeader> | |
| <CardContent className="space-y-4"> | |
| {process.env.NODE_ENV === 'development' && this.state.error && ( | |
| <details className="text-xs bg-muted p-3 rounded border"> | |
| <summary className="cursor-pointer font-medium">Error Details</summary> | |
| <pre className="mt-2 whitespace-pre-wrap break-all"> | |
| {this.state.error.toString()} | |
| </pre> | |
| </details> | |
| )} | |
| <Button | |
| onClick={this.resetError} | |
| className="w-full" | |
| variant="outline" | |
| > | |
| <RefreshCw className="w-4 h-4 mr-2" /> | |
| Try Again | |
| </Button> | |
| <Button | |
| onClick={() => window.location.reload()} | |
| className="w-full" | |
| > | |
| Refresh Page | |
| </Button> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| ) | |
| } | |
| return this.props.children | |
| } | |
| } | |
| // Hook version for functional components | |
| export function useErrorBoundary() { | |
| return (error: Error) => { | |
| throw error | |
| } | |
| } |