diff --git a/src/components/AnalyticsErrorBoundary.tsx b/src/components/AnalyticsErrorBoundary.tsx new file mode 100644 index 0000000..41ffd4e --- /dev/null +++ b/src/components/AnalyticsErrorBoundary.tsx @@ -0,0 +1,87 @@ +import React, { Component, ErrorInfo, ReactNode } from 'react'; +import { eventBuilders, analytics } from '@/lib/analytics'; + +interface Props { + children: ReactNode; + fallback?: (error: Error, reset: () => void) => ReactNode; +} + +interface State { + hasError: boolean; + error: Error | null; +} + +/** + * Error boundary component that tracks UI errors to analytics + */ +export class AnalyticsErrorBoundary extends Component { + constructor(props: Props) { + super(props); + this.state = { hasError: false, error: null }; + } + + static getDerivedStateFromError(error: Error): State { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: ErrorInfo) { + // Track UI error to analytics + const event = eventBuilders.uiError({ + component_name: errorInfo.componentStack?.split('\n')[0] || 'Unknown', + error_type: error.name || 'UnknownError', + user_action: undefined, // Could be enhanced with context + }); + + analytics.track(event.event, event.properties); + + // Log to console for debugging + console.error('UI Error caught by boundary:', error, errorInfo); + } + + reset = () => { + this.setState({ hasError: false, error: null }); + }; + + render() { + if (this.state.hasError && this.state.error) { + // Use custom fallback if provided + if (this.props.fallback) { + return this.props.fallback(this.state.error, this.reset); + } + + // Default fallback UI + return ( +
+

+ Something went wrong +

+

+ {this.state.error.message} +

+ +
+ ); + } + + return this.props.children; + } +} + +/** + * Hook to wrap components with analytics error tracking + */ +export function withAnalyticsErrorBoundary

( + Component: React.ComponentType

, + fallback?: (error: Error, reset: () => void) => ReactNode +) { + return (props: P) => ( + + + + ); +} \ No newline at end of file