'use client'; import React, { Component, ReactNode, useState } from 'react'; import { ApiError } from '@/lib/api/base/ApiError'; import { connectionMonitor } from '@/lib/api/base/ApiConnectionMonitor'; import { ErrorDisplay } from '@/ui/ErrorDisplay'; import { DevErrorPanel } from '@/ui/DevErrorPanel'; interface Props { children: ReactNode; fallback?: ReactNode; onError?: (error: ApiError) => void; } interface State { hasError: boolean; error: ApiError | null; isDev: boolean; } /** * Error Boundary for API-related errors * Catches errors from API calls and displays appropriate UI */ export class ApiErrorBoundary extends Component { constructor(props: Props) { super(props); this.state = { hasError: false, error: null, isDev: process.env.NODE_ENV === 'development', }; } static getDerivedStateFromError(error: Error): State { // Only handle ApiError instances if (error instanceof ApiError) { return { hasError: true, error, isDev: process.env.NODE_ENV === 'development', }; } // Re-throw non-API errors throw error; } componentDidCatch(error: Error): void { if (error instanceof ApiError) { // Report to connection monitor connectionMonitor.recordFailure(error); // Call custom error handler if provided if (this.props.onError) { this.props.onError(error); } // For connectivity errors in production, don't show error boundary UI // These are handled by the notification system if (error.isConnectivityIssue() && !this.state.isDev) { // Reset error state so boundary doesn't block UI setTimeout(() => this.resetError(), 100); return; } } } componentDidMount(): void { // Listen for connection status changes const monitor = connectionMonitor; monitor.on('disconnected', this.handleDisconnected); monitor.on('degraded', this.handleDegraded); monitor.on('connected', this.handleConnected); } componentWillUnmount(): void { const monitor = connectionMonitor; monitor.off('disconnected', this.handleDisconnected); monitor.off('degraded', this.handleDegraded); monitor.off('connected', this.handleConnected); } private handleDisconnected = (): void => { // Connection status handled by notification system }; private handleDegraded = (): void => { // Connection status handled by notification system }; private handleConnected = (): void => { // Connection status handled by notification system }; resetError = (): void => { this.setState({ hasError: false, error: null }); }; render(): ReactNode { if (this.state.hasError && this.state.error) { if (this.props.fallback) { return this.props.fallback; } // Show different UI based on environment if (this.state.isDev) { return ( ); } return ( ); } return this.props.children; } } /** * Hook-based alternative for functional components */ export function useApiErrorBoundary() { const [error, setError] = useState(null); const [isDev] = useState(process.env.NODE_ENV === 'development'); const handleError = (err: ApiError) => { setError(err); }; const reset = () => { setError(null); }; return { error, isDev, handleError, reset, ErrorBoundary: ({ children }: { children: ReactNode }) => ( {children} ), }; }