Files
syndarix/frontend/src/components/common/ErrorBoundary.tsx
Felipe Cardoso c17fdab3d3 refactor(frontend): clean up code by consolidating multi-line JSX into single lines where feasible
- Refactored JSX elements to improve readability by collapsing multi-line props and attributes into single lines if their length permits.
- Improved consistency in component imports by grouping and consolidating them.
- No functional changes, purely restructuring for clarity and maintainability.
2026-01-01 11:46:57 +01:00

136 lines
4.0 KiB
TypeScript

/**
* Error Boundary Component
*
* Catches JavaScript errors in child component tree and displays fallback UI.
* Prevents the entire app from crashing due to component-level errors.
*
* @see https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary
*/
'use client';
import { Component, type ReactNode } from 'react';
import { AlertTriangle, RefreshCw } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
// ============================================================================
// Types
// ============================================================================
interface ErrorBoundaryProps {
/** Child components to render */
children: ReactNode;
/** Optional fallback component to render on error */
fallback?: ReactNode;
/** Optional callback when error occurs */
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
/** Whether to show reset button (default: true) */
showReset?: boolean;
}
interface ErrorBoundaryState {
hasError: boolean;
error: Error | null;
}
// ============================================================================
// Default Fallback Component
// ============================================================================
interface DefaultFallbackProps {
error: Error | null;
onReset: () => void;
showReset: boolean;
}
function DefaultFallback({ error, onReset, showReset }: DefaultFallbackProps) {
return (
<Card className="m-4 border-destructive/50 bg-destructive/5">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-destructive">
<AlertTriangle className="h-5 w-5" aria-hidden="true" />
Something went wrong
</CardTitle>
<CardDescription>
An unexpected error occurred. Please try again or contact support if the problem persists.
</CardDescription>
</CardHeader>
<CardContent>
{error && (
<div className="mb-4 rounded-md bg-muted p-3">
<p className="font-mono text-sm text-muted-foreground">{error.message}</p>
</div>
)}
{showReset && (
<Button variant="outline" size="sm" onClick={onReset} className="gap-2">
<RefreshCw className="h-4 w-4" aria-hidden="true" />
Try again
</Button>
)}
</CardContent>
</Card>
);
}
// ============================================================================
// Error Boundary Component
// ============================================================================
/**
* Error boundary for catching and handling render errors in React components.
*
* @example
* ```tsx
* <ErrorBoundary onError={(error) => logError(error)}>
* <MyComponent />
* </ErrorBoundary>
* ```
*
* @example With custom fallback
* ```tsx
* <ErrorBoundary fallback={<CustomErrorUI />}>
* <MyComponent />
* </ErrorBoundary>
* ```
*/
export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
// Log error to console in development
console.error('ErrorBoundary caught an error:', error, errorInfo);
// Call optional error callback
this.props.onError?.(error, errorInfo);
}
handleReset = (): void => {
this.setState({ hasError: false, error: null });
};
render(): ReactNode {
const { hasError, error } = this.state;
const { children, fallback, showReset = true } = this.props;
if (hasError) {
if (fallback) {
return fallback;
}
return <DefaultFallback error={error} onReset={this.handleReset} showReset={showReset} />;
}
return children;
}
}
export default ErrorBoundary;