feat: Add error boundaries

This commit is contained in:
gpt-engineer-app[bot]
2025-11-03 14:51:39 +00:00
parent 3ee65403ea
commit ee09e3652c
8 changed files with 1682 additions and 56 deletions

View File

@@ -0,0 +1,119 @@
import React, { Component, ErrorInfo, ReactNode } from 'react';
import { AlertTriangle, Home, RefreshCw } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { logger } from '@/lib/logger';
interface RouteErrorBoundaryProps {
children: ReactNode;
}
interface RouteErrorBoundaryState {
hasError: boolean;
error: Error | null;
}
/**
* Route Error Boundary Component (P0 #5)
*
* Top-level error boundary that wraps all routes.
* Last line of defense to prevent complete app crashes.
*
* Usage: Wrap Routes component in App.tsx
* ```tsx
* <RouteErrorBoundary>
* <Routes>...</Routes>
* </RouteErrorBoundary>
* ```
*/
export class RouteErrorBoundary extends Component<RouteErrorBoundaryProps, RouteErrorBoundaryState> {
constructor(props: RouteErrorBoundaryProps) {
super(props);
this.state = {
hasError: false,
error: null,
};
}
static getDerivedStateFromError(error: Error): Partial<RouteErrorBoundaryState> {
return {
hasError: true,
error,
};
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
// Critical: Route-level error - highest priority logging
logger.error('Route-level error caught by boundary', {
error: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
url: window.location.href,
severity: 'critical',
});
}
handleReload = () => {
window.location.reload();
};
handleGoHome = () => {
window.location.href = '/';
};
render() {
if (this.state.hasError) {
return (
<div className="min-h-screen flex items-center justify-center p-4 bg-background">
<Card className="max-w-lg w-full shadow-lg">
<CardHeader className="text-center pb-4">
<div className="mx-auto w-16 h-16 bg-destructive/10 rounded-full flex items-center justify-center mb-4">
<AlertTriangle className="w-8 h-8 text-destructive" />
</div>
<CardTitle className="text-2xl">
Something Went Wrong
</CardTitle>
<CardDescription className="mt-2">
We encountered an unexpected error. This has been logged and we'll look into it.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{import.meta.env.DEV && this.state.error && (
<div className="p-3 bg-muted rounded-lg">
<p className="text-xs font-mono text-muted-foreground">
{this.state.error.message}
</p>
</div>
)}
<div className="flex flex-col sm:flex-row gap-2">
<Button
variant="default"
onClick={this.handleReload}
className="flex-1 gap-2"
>
<RefreshCw className="w-4 h-4" />
Reload Page
</Button>
<Button
variant="outline"
onClick={this.handleGoHome}
className="flex-1 gap-2"
>
<Home className="w-4 h-4" />
Go Home
</Button>
</div>
<p className="text-xs text-center text-muted-foreground">
If this problem persists, please contact support
</p>
</CardContent>
</Card>
</div>
);
}
return this.props.children;
}
}