import { Component, ErrorInfo, ReactNode } from 'react'; interface Props { children: ReactNode; /** Opcjonalny custom fallback UI. Jeśli nie podany — wyświetla domyślny ekran błędu. */ fallback?: ReactNode; /** Nazwa kontekstu — pomaga w logowaniu (np. "GeneratorPanel", "AuditPanel") */ context?: string; } interface State { hasError: boolean; error: Error | null; errorInfo: ErrorInfo | null; } /** * React Error Boundary — globalny łapacz błędów komponentów. * * Użycie: * * * * * Krytyczny wymóg Beta 1.0 — bez tego crash komponentu = biały ekran. * Zgodność: React 18+, wymaga klasy (hooks nie obsługują componentDidCatch). */ export class ErrorBoundary extends Component { constructor(props: Props) { super(props); this.state = { hasError: false, error: null, errorInfo: null }; } static getDerivedStateFromError(error: Error): Partial { return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: ErrorInfo) { const context = this.props.context || 'unknown'; console.error(`[ErrorBoundary][${context}] Caught error:`, error, errorInfo); this.setState({ errorInfo }); // Opcjonalne wysłanie do serwisu monitoringu (Sentry / LangSmith) try { if (typeof window !== 'undefined' && (window as any).Sentry) { (window as any).Sentry.captureException(error, { extra: { context, componentStack: errorInfo.componentStack }, }); } } catch { // Sentry niedostępny — ignoruj } } handleReset = () => { this.setState({ hasError: false, error: null, errorInfo: null }); }; render() { if (this.state.hasError) { if (this.props.fallback) { return this.props.fallback; } return (
⚠️

Wystąpił nieoczekiwany błąd

{this.state.error?.message || 'Nieznany błąd komponentu.'} {this.props.context && ( <>
Kontekst: {this.props.context} )}

{process.env.NODE_ENV === 'development' && this.state.errorInfo && (
Stack trace (dev only)
                                {this.state.errorInfo.componentStack}
                            
)}
); } return this.props.children; } } export default ErrorBoundary;