Files
personal-finance/frontend-web/src/components/ErrorBoundary.tsx
2025-12-07 12:19:38 -05:00

73 lines
2.2 KiB
TypeScript

import {Component, type ReactNode} from 'react';
import {Button} from '@/components/ui/button';
import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error: Error | null;
}
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
if (import.meta.env.DEV) {
console.error('Error caught by boundary:', error, errorInfo);
}
// In production, you would send this to an error tracking service
}
handleReset = () => {
this.setState({hasError: false, error: null});
};
render() {
if (this.state.hasError) {
if (this.props.fallback) {
return this.props.fallback;
}
return (
<div className="flex items-center justify-center min-h-screen p-4">
<Card className="card-elevated max-w-md w-full">
<CardHeader>
<CardTitle className="text-red-400">Something went wrong</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">An unexpected error occurred. Please try refreshing the page.</p>
{import.meta.env.DEV && this.state.error && (
<div className="p-3 bg-destructive/10 rounded-md">
<p className="text-xs font-mono text-destructive break-all">{this.state.error.message}</p>
</div>
)}
<div className="flex gap-2">
<Button onClick={this.handleReset} variant="secondary" size="sm">
Try Again
</Button>
<Button onClick={() => window.location.reload()} size="sm">
Reload Page
</Button>
</div>
</CardContent>
</Card>
</div>
);
}
return this.props.children;
}
}