All files / web/src/components/agents agent-error-boundary.tsx

100% Statements 73/73
94.11% Branches 16/17
100% Functions 7/7
100% Lines 73/73

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118    1x 1x           1x 1x                           1x 29x 30x 30x 30x   29x 16x 16x   29x   8x 8x 8x 8x 8x 8x     8x 8x 8x   8x 8x   29x 1x 1x   29x 65x   24x 3x 3x     21x 21x 21x 21x 21x 24x 24x 24x 24x 24x     24x   24x 3x 3x   3x 3x 3x 3x 3x 3x     24x 24x 24x 24x   24x   24x 24x 24x   24x   41x 65x 29x     1x 2x 2x 2x 2x 4x 4x 4x   2x  
'use client';
 
import { Button } from '@web/components/ui/button';
import {
  Card,
  CardContent,
  CardHeader,
  CardTitle,
} from '@web/components/ui/card';
import { AlertCircle, RefreshCw } from 'lucide-react';
import React, { Component, type ErrorInfo, type ReactNode } from 'react';
 
interface Props {
  children: ReactNode;
  fallback?: (error: Error, resetError: () => void) => ReactNode;
  sectionName?: string;
}
 
interface State {
  hasError: boolean;
  error: Error | null;
  errorInfo: ErrorInfo | null;
}
 
export class AgentErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, error: null, errorInfo: null };
  }
 
  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error, errorInfo: null };
  }
 
  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    // Log error to monitoring service
    console.error('Agent section error:', {
      section: this.props.sectionName,
      error: error.toString(),
      stack: errorInfo.componentStack,
      timestamp: new Date().toISOString(),
    });
 
    // Track error metrics
    if (window?.performance) {
      performance.mark(`agent-error-${this.props.sectionName || 'unknown'}`);
    }
 
    this.setState({ errorInfo });
  }
 
  resetError = () => {
    this.setState({ hasError: false, error: null, errorInfo: null });
  };
 
  render() {
    if (this.state.hasError) {
      // Use custom fallback if provided
      if (this.props.fallback) {
        return this.props.fallback(this.state.error!, this.resetError);
      }
 
      // Default error UI
      return (
        <Card className="border-destructive">
          <CardHeader>
            <CardTitle className="flex items-center gap-2 text-destructive">
              <AlertCircle className="h-5 w-5" />
              Error in {this.props.sectionName || 'Agent Section'}
            </CardTitle>
          </CardHeader>
          <CardContent className="space-y-4">
            <p className="text-sm text-muted-foreground">
              Something went wrong while loading this section. The error has
              been logged.
            </p>
 
            {process.env.NODE_ENV === 'development' && this.state.error && (
              <details className="text-xs">
                <summary className="cursor-pointer text-muted-foreground hover:text-foreground">
                  Error Details (Development Only)
                </summary>
                <pre className="mt-2 p-2 bg-muted rounded overflow-auto">
                  {this.state.error.toString()}
                  {this.state.errorInfo?.componentStack}
                </pre>
              </details>
            )}
 
            <Button
              onClick={this.resetError}
              variant="outline"
              className="gap-2"
            >
              <RefreshCw className="h-4 w-4" />
              Try Again
            </Button>
          </CardContent>
        </Card>
      );
    }
 
    return this.props.children;
  }
}
 
// HOC for wrapping agent components with error boundary
export function withAgentErrorBoundary<P extends object>(
  Component: React.ComponentType<P>,
  sectionName: string,
) {
  return (props: P) => (
    <AgentErrorBoundary sectionName={sectionName}>
      <Component {...props} />
    </AgentErrorBoundary>
  );
}