diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000..41683e9 --- /dev/null +++ b/src/components/ErrorBoundary.tsx @@ -0,0 +1,78 @@ +import { Component, type ReactNode, type ErrorInfo } from 'react'; +import { t } from '../lib/i18n'; + +interface Props { + children: ReactNode; + /** Optional fallback — defaults to built-in error card */ + fallback?: ReactNode; +} + +interface State { + hasError: boolean; + error: Error | null; +} + +/** + * Catches render errors in child components and shows a recovery UI + * instead of a blank white screen. + */ +export class ErrorBoundary extends Component { + state: State = { hasError: false, error: null }; + + static getDerivedStateFromError(error: Error): State { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, info: ErrorInfo) { + console.error('[PinchChat] Render error caught by ErrorBoundary:', error, info.componentStack); + } + + handleRetry = () => { + this.setState({ hasError: false, error: null }); + }; + + handleReload = () => { + window.location.reload(); + }; + + render() { + if (this.state.hasError) { + if (this.props.fallback) return this.props.fallback; + + return ( +
+
+
đź’Ą
+

+ {t('error.title')} +

+

+ {t('error.description')} +

+ {this.state.error && ( +
+                {this.state.error.message}
+              
+ )} +
+ + +
+
+
+ ); + } + + return this.props.children; + } +} diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts index dc6d94a..acac2c5 100644 --- a/src/lib/i18n.ts +++ b/src/lib/i18n.ts @@ -73,6 +73,12 @@ const en = { 'shortcuts.help': 'Show shortcuts', 'shortcuts.close': 'Close', 'shortcuts.chatSection': 'Chat', + + // Error boundary + 'error.title': 'Something went wrong', + 'error.description': 'An unexpected error occurred while rendering the interface. You can try again or reload the page.', + 'error.retry': 'Try again', + 'error.reload': 'Reload page', 'shortcuts.navigationSection': 'Navigation', 'shortcuts.generalSection': 'General', } as const; @@ -133,6 +139,11 @@ const fr: Record = { 'shortcuts.help': 'Afficher les raccourcis', 'shortcuts.close': 'Fermer', 'shortcuts.chatSection': 'Chat', + + 'error.title': 'Quelque chose s\'est mal passé', + 'error.description': 'Une erreur inattendue est survenue lors de l\'affichage. Vous pouvez réessayer ou recharger la page.', + 'error.retry': 'Réessayer', + 'error.reload': 'Recharger', 'shortcuts.navigationSection': 'Navigation', 'shortcuts.generalSection': 'Général', }; diff --git a/src/main.tsx b/src/main.tsx index 964aeb4..6a43a55 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,13 @@ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App' +import { ErrorBoundary } from './components/ErrorBoundary' import './index.css' ReactDOM.createRoot(document.getElementById('root')!).render( - + + + , )