Spaces:
Running
Running
| tsx | |
| import React, { Component, ErrorInfo, ReactNode } from 'react' | |
| import { AlertTriangle, RefreshCw } from 'lucide-react' | |
| /** | |
| * Props for the ErrorBoundary component | |
| */ | |
| interface ErrorBoundaryProps { | |
| children: ReactNode | |
| } | |
| /** | |
| * State for the ErrorBoundary component | |
| */ | |
| interface ErrorBoundaryState { | |
| hasError: boolean | |
| error: Error | null | |
| errorInfo: ErrorInfo | null | |
| } | |
| /** | |
| * Error Boundary component to catch and handle React errors gracefully | |
| * Provides user-friendly error messages and recovery options | |
| */ | |
| export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> { | |
| constructor(props: ErrorBoundaryProps) { | |
| super(props) | |
| this.state = { | |
| hasError: false, | |
| error: null, | |
| errorInfo: null | |
| } | |
| } | |
| /** | |
| * Static method to catch errors and update state | |
| */ | |
| static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> { | |
| return { | |
| hasError: true, | |
| error | |
| } | |
| } | |
| /** | |
| * ComponentDidCatch to log error details | |
| */ | |
| componentDidCatch(error: Error, errorInfo: ErrorInfo) { | |
| console.error('ErrorBoundary caught an error:', error, errorInfo) | |
| this.setState({ | |
| error, | |
| errorInfo | |
| }) | |
| // In a real app, you might want to send this to an error reporting service | |
| // reportError(error, errorInfo) | |
| } | |
| /** | |
| * Handle retry after error | |
| */ | |
| handleRetry = () => { | |
| this.setState({ | |
| hasError: false, | |
| error: null, | |
| errorInfo: null | |
| }) | |
| } | |
| /** | |
| * Handle refresh page | |
| */ | |
| handleRefresh = () => { | |
| window.location.reload() | |
| } | |
| render() { | |
| if (this.state.hasError) { | |
| return ( | |
| <div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center px-4"> | |
| <div className="max-w-md w-full bg-white dark:bg-gray-800 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 p-8 text-center"> | |
| <div className="w-16 h-16 bg-red-100 dark:bg-red-900 rounded-full flex items-center justify-center mx-auto mb-6"> | |
| <AlertTriangle className="w-8 h-8 text-red-600 dark:text-red-400" /> | |
| </div> | |
| <h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-4"> | |
| Etwas ist schiefgelaufen | |
| </h1> | |
| <p className="text-gray-600 dark:text-gray-400 mb-6"> | |
| Es ist ein unerwarteter Fehler aufgetreten. Bitte versuche es erneut oder lade die Seite neu. | |
| </p> | |
| {process.env.NODE_ENV === 'development' && this.state.error && ( | |
| <details className="text-left mb-6 p-4 bg-gray-100 dark:bg-gray-700 rounded-lg text-sm"> | |
| <summary className="cursor-pointer font-medium text-gray-900 dark:text-gray-100 mb-2"> | |
| Technische Details (Entwicklungsmodus) | |
| </summary> | |
| <pre className="text-xs text-red-600 dark:text-red-400 overflow-auto"> | |
| {this.state.error.toString()} | |
| {this.state.errorInfo?.componentStack} | |
| </pre> | |
| </details> | |
| )} | |
| <div className="flex flex-col sm:flex-row gap-3"> | |
| <button | |
| onClick={this.handleRetry} | |
| className="flex-1 btn-primary flex items-center justify-center gap-2" | |
| > | |
| <RefreshCw className="w-4 h-4" /> | |
| Erneut versuchen | |
| </button> | |
| <button | |
| onClick={this.handleRefresh} | |
| className="flex-1 btn-secondary" | |
| > | |
| Seite neu laden | |
| </button> | |
| </div> | |
| <p className="text-xs text-gray-500 dark:text-gray-400 mt-4"> | |
| Falls das Problem besteht, kontaktiere bitte den Support. | |
| </p> | |
| </div> | |
| </div> | |
| ) | |
| } | |
| return this.props.children | |
| } | |
| } | |
| </html> |