"use client"; import { Component, useState, useCallback } from "react"; import type { ReactNode, ErrorInfo } from "react"; import * as Sentry from "@sentry/nextjs"; import { AlertTriangle, RefreshCw } from "lucide-react"; interface Props { children: ReactNode; fallback?: ReactNode; } interface State { hasError: boolean; error: Error | null; eventId: string | null; } /** * React Error Boundary with Sentry integration * Catches rendering errors in child components and reports them to Sentry */ export class ErrorBoundary extends Component { constructor(props: Props) { super(props); this.state = { hasError: false, error: null, eventId: null }; } static getDerivedStateFromError(error: Error): Partial { return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: ErrorInfo) { // Report to Sentry with React component stack const eventId = Sentry.captureException(error, { contexts: { react: { componentStack: errorInfo.componentStack, }, }, }); this.setState({ eventId }); // Also log to console in development if (process.env.NODE_ENV === "development") { console.error("Error caught by boundary:", error, errorInfo); } } handleRetry = () => { this.setState({ hasError: false, error: null, eventId: null }); }; handleReportFeedback = () => { if (this.state.eventId) { Sentry.showReportDialog({ eventId: this.state.eventId }); } }; render() { if (this.state.hasError) { if (this.props.fallback) { return this.props.fallback; } return (

Something went wrong

We've been notified and are working on a fix. Please try again or contact support if the problem persists.

{process.env.NODE_ENV === "development" && this.state.error && (

{this.state.error.message}

)}
{this.state.eventId && ( )}
); } return this.props.children; } } /** * Hook to throw errors from async code so they can be caught by ErrorBoundary * Usage: * const throwError = useAsyncError(); * fetchData().catch(throwError); */ export function useAsyncError() { const [, setError] = useState(null); return useCallback((error: Error) => { setError(() => { throw error; }); }, []); } export default ErrorBoundary;