veza/apps/web/src/components/ErrorBoundary.tsx

97 lines
2.6 KiB
TypeScript
Raw Normal View History

import { Component, type ErrorInfo, type ReactNode } from 'react';
import * as Sentry from '@sentry/react';
import { logger, getLogContext } from '@/utils/logger';
import { ErrorDisplay } from '@/components/ui/ErrorDisplay';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error?: Error;
errorInfo?: ErrorInfo;
}
export class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
override componentDidCatch(error: Error, errorInfo: ErrorInfo) {
this.setState({
error,
errorInfo,
});
// FIX #20: Logger l'erreur avec le logger structuré et Sentry
const logContext = getLogContext();
logger.error('[ErrorBoundary] React error caught', {
error: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
...logContext,
});
// Envoyer à Sentry avec contexte enrichi
Sentry.captureException(error, {
contexts: {
react: {
componentStack: errorInfo.componentStack,
},
application: logContext,
},
});
}
handleReset = () => {
this.setState({ hasError: false, error: undefined, errorInfo: undefined });
};
override render() {
if (this.state.hasError) {
if (this.props.fallback) {
return this.props.fallback;
}
// Action 3.3.1.3: Use ErrorDisplay component for consistent error presentation
return (
2025-12-13 02:34:34 +00:00
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 p-4">
<div className="w-full max-w-md">
<ErrorDisplay
error={this.state.error || new Error('Une erreur inattendue s\'est produite')}
variant="card"
severity="error"
size="lg"
showDetails={import.meta.env.DEV}
context={{
component: 'ErrorBoundary',
action: 'rendering component',
componentStack: this.state.errorInfo?.componentStack,
}}
onRetry={this.handleReset}
actions={[
{
label: 'Retour à l\'accueil',
onClick: () => {
window.location.href = '/';
},
variant: 'outline',
},
]}
/>
</div>
</div>
);
}
return this.props.children;
}
}