Files
leocrm/frontend/src/components/ErrorBoundary.tsx
T
Agent Zero 1ba702f6fe fix: report API errors (422, 404, 5xx, network) and React errors to Forgejo error reporter
- client.ts: logError() import added, API error interceptor now reports to /api/v1/errors
- ErrorBoundary.tsx: logError() import added, React rendering errors now reported
- 401 (auth) and 403 (permission) errors are NOT reported (expected behavior)
- 422 (validation), 404 (not found), 5xx (server), 0 (network) ARE reported
2026-07-26 23:45:59 +02:00

85 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from 'react';
import { logError } from '@/utils/errorLogger';
interface ErrorBoundaryProps {
children: React.ReactNode;
fallback?: React.ReactNode;
}
interface ErrorBoundaryState {
hasError: boolean;
error: Error | null;
}
/**
* App-wide ErrorBoundary that catches React rendering errors
* and displays a fallback UI with a reload button.
*/
export class ErrorBoundary extends React.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 {
console.error('[ErrorBoundary] Uncaught error:', error, errorInfo);
logError(error, { componentStack: errorInfo.componentStack, source: 'ErrorBoundary' });
}
handleReload = (): void => {
window.location.reload();
};
render(): React.ReactNode {
if (this.state.hasError) {
if (this.props.fallback) {
return this.props.fallback;
}
return (
<div
className="flex items-center justify-center min-h-screen bg-secondary-50 p-8"
role="alert"
>
<div className="max-w-md w-full bg-white rounded-lg shadow-lg p-8 text-center">
<div className="text-red-500 text-6xl mb-4" aria-hidden="true">
</div>
<h1 className="text-2xl font-bold text-secondary-900 mb-2">
Ein Fehler ist aufgetreten
</h1>
<p className="text-secondary-600 mb-6">
Die Anwendung konnte nicht geladen werden. Bitte versuchen Sie es erneut.
</p>
{this.state.error && (
<details className="mb-6 text-left">
<summary className="cursor-pointer text-sm text-secondary-500 hover:text-secondary-700">
Fehlerdetails
</summary>
<pre className="mt-2 p-3 bg-secondary-100 rounded text-xs text-secondary-700 overflow-auto max-h-32">
{this.state.error.message}
{this.state.error.stack && `\n\n${this.state.error.stack}`}
</pre>
</details>
)}
<button
onClick={this.handleReload}
className="inline-flex items-center px-6 py-3 bg-primary-600 text-white font-medium rounded-lg hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 transition-colors"
>
Neu laden
</button>
</div>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;