// --- File: client/src/components/ErrorBoundary.tsx --- // --- Tech Used --- // - React: For building the user interface. Uses both Class Components (for ErrorBoundary itself) and Functional Components (for hooks/HOCs). // - TypeScript: For static typing, improving code quality and maintainability. // - Shadcn/ui (Likely): Based on "@/components/ui/..." imports. This library provides accessible and customizable UI components, often built on Tailwind CSS and Radix UI. // - Lucide Icons (`lucide-react`): For clean and consistent SVG icons. // - Custom Error Handling (`@/lib/errorHandler`): A centralized module for logging/processing errors. // --- Imports --- import React, { Component, ReactNode, ErrorInfo } from 'react'; // React.ErrorInfo provides types for componentDidCatch import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; // Added CardFooter import { AlertTriangle, RefreshCw, Home, Copy } from "lucide-react"; // Added Copy icon import { errorHandler } from "@/lib/errorHandler"; // Your custom error logging utility // --- Component Props Interface --- // Defines the expected properties for the ErrorBoundary component. interface Props { children: ReactNode; // The React elements that this boundary will wrap. fallback?: ReactNode; // Optional custom UI to render when an error is caught. moduleName?: string; // A more descriptive name for the module/component being wrapped. onRetry?: () => void; // Optional callback for when the user clicks "Try Again". } // --- Component State Interface --- // Defines the structure of the ErrorBoundary's internal state. interface State { hasError: boolean; // True if an error has been caught. error?: Error; // The caught JavaScript error object. errorInfo?: ErrorInfo; // Additional info from React about the component stack. errorId?: string; // An ID for the logged error, useful for tracking. } // --- ErrorBoundary Class Component --- // Function: Catches JavaScript errors in its child component tree, logs them, and displays a fallback UI. export class ErrorBoundary extends Component { constructor(props: Props) { super(props); // Initialize state: no error by default. this.state = { hasError: false }; } // Lifecycle method: Called during the "render" phase if an error is thrown by a descendant. // Updates the state to trigger a re-render with the fallback UI. static getDerivedStateFromError(error: Error): Partial { return { hasError: true, error }; } // Lifecycle method: Called during the "commit" phase after an error is thrown and getDerivedStateFromError has been called. // Suitable for side effects like logging. componentDidCatch(error: Error, errorInfo: ErrorInfo) { const { moduleName } = this.props; const errorContext = moduleName || 'UnknownComponent'; // Log the error using the custom error handler. const errorId = errorHandler.logError( error, errorContext, 'react_component_crash', // A category for this type of error { componentStack: errorInfo.componentStack } // Include component stack ); // Update state with the error information and ID. this.setState({ errorInfo, errorId }); // Log to console for development purposes. console.error(`ErrorBoundary (${errorContext}): Uncaught error:`, error, errorInfo); } // Resets the error state, allowing children to attempt re-rendering. handleRetry = () => { if (this.props.onRetry) { this.props.onRetry(); // Call custom retry logic if provided } this.setState({ hasError: false, error: undefined, errorInfo: undefined, errorId: undefined }); }; // Navigates to the home page. // For SPAs, using a router's navigation method (e.g., useNavigate from react-router-dom) is better than a hard reload. handleGoHome = () => { // TODO: Replace with router navigation if using react-router or similar. // Example with react-router: navigate('/'); window.location.href = '/'; }; // Copies the error ID to the clipboard. handleCopyErrorId = () => { if (this.state.errorId) { navigator.clipboard.writeText(this.state.errorId) .then(() => { // Optional: Show a temporary "Copied!" message console.info("Error ID copied to clipboard:", this.state.errorId); }) .catch(err => { console.error("Failed to copy error ID:", err); }); } }; render() { if (this.state.hasError) { // Render custom fallback if provided if (this.props.fallback) { return this.props.fallback; } // Default fallback UI const { error, errorId, errorInfo } = this.state; const { moduleName } = this.props; const componentName = moduleName || 'the application'; return (
An Error Occurred Sorry, something went wrong in {componentName}.
Message:

{error?.message || 'No specific error message available.'}

{errorId && (
Error ID: {errorId}
)}
{errorInfo?.componentStack && process.env.NODE_ENV === 'development' && (
Component Stack (Dev Only)
                    {errorInfo.componentStack}
                  
)}
); } return this.props.children; // Render children if no error } } // --- Custom Hook: useErrorHandler --- // Function: Provides convenient error handling functions for use in Functional Components. export function useErrorHandler(moduleName: string) { // moduleName: Context for the error (e.g., component name). // Function: Manually logs an error using the centralized errorHandler. const handleError = (error: Error, userActionContext?: string): string | undefined => { console.error(`Error handled in module '${moduleName}' (action: ${userActionContext || 'N/A'}):`, error); return errorHandler.logError(error, moduleName, userActionContext); }; // Function: Wraps an asynchronous operation (Promise) to automatically catch and log errors. // T: Generic type for the expected data from the successful operation. // SIMPLIFIED VERSION FOR TESTING const handleAsyncOperation = async ( // Generic removed for simplicity during testing operation: any, // Using 'any' for the operation itself userActionContext?: string | undefined // Adding userActionContext back as an optional string ): Promise => { // Using 'any' for the Promise return type try { const data = await operation(); return { success: true, data }; } catch (err) { // Ensure the caught item is an Error instance before logging. const error = err instanceof Error ? err : new Error(String(err)); const errorId = handleError(error, userActionContext); return { success: false, errorId }; } }; return { handleError, handleAsyncOperation }; } // --- Higher-Order Component (HOC): withErrorBoundary --- // Function: A utility to easily wrap existing components with the ErrorBoundary. // P: Generic type for the props of the component being wrapped. export function withErrorBoundary

( // Use a more appropriate constraint for P WrappedComponent: React.ComponentType

, options: { moduleName: string; fallback?: ReactNode; onRetry?: () => void } // Options object ) { const { moduleName, fallback, onRetry } = options; // Returns a new component that renders the WrappedComponent inside an ErrorBoundary. const ComponentWithBoundary = (props: P) => ( // Give component a display name ); // Set a display name for easier debugging in React DevTools const wrappedComponentName = WrappedComponent.displayName || WrappedComponent.name || 'Component'; ComponentWithBoundary.displayName = `WithErrorBoundary(${wrappedComponentName})`; return ComponentWithBoundary; }