Spaces:
Running
Running
File size: 4,078 Bytes
227c43a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | import React from 'react';
import { motion } from 'framer-motion';
import { AlertTriangle, RefreshCw, Home } from 'lucide-react';
import Button from '../design-system/components/Button';
import Card from '../design-system/components/Card';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null,
retryCount: 0
};
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
this.setState({
error,
errorInfo,
});
// Log error to monitoring service in production
if (process.env.NODE_ENV === 'production') {
console.error('Error caught by boundary:', error, errorInfo);
// Here you would typically send to error monitoring service
// e.g., Sentry.captureException(error, { extra: errorInfo });
}
}
handleRetry = () => {
this.setState(prevState => ({
hasError: false,
error: null,
errorInfo: null,
retryCount: prevState.retryCount + 1
}));
};
handleGoHome = () => {
window.location.href = '/';
};
render() {
if (this.state.hasError) {
return (
<div className="min-h-screen bg-gradient-to-br from-neutral-50 to-neutral-100 flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="w-full max-w-md"
>
<Card variant="elevated" padding="lg" className="text-center">
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: 0.2, type: "spring" }}
className="w-16 h-16 bg-error-100 rounded-full flex items-center justify-center mx-auto mb-4"
>
<AlertTriangle size={32} className="text-error-500" />
</motion.div>
<Card.Title className="text-xl mb-2">
Oops! Something went wrong
</Card.Title>
<Card.Description className="mb-6">
We encountered an unexpected error. This has been logged and our team will investigate.
</Card.Description>
{process.env.NODE_ENV === 'development' && this.state.error && (
<details className="text-left mb-6 p-4 bg-neutral-50 rounded-lg">
<summary className="cursor-pointer text-sm font-medium text-neutral-700 mb-2">
Error Details (Development)
</summary>
<pre className="text-xs text-error-600 overflow-auto">
{this.state.error.toString()}
{this.state.errorInfo.componentStack}
</pre>
</details>
)}
<div className="flex gap-3">
<Button
variant="outline"
onClick={this.handleRetry}
icon={<RefreshCw size={16} />}
className="flex-1"
>
Try Again
</Button>
<Button
onClick={this.handleGoHome}
icon={<Home size={16} />}
className="flex-1"
>
Go Home
</Button>
</div>
{this.state.retryCount > 2 && (
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="text-sm text-neutral-600 mt-4"
>
Still having issues? Try refreshing the page or contact support.
</motion.p>
)}
</Card>
</motion.div>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
|