import { Button } from "@/components/ui/button"; import { useTheme } from "@/lib/theme-context"; import { AppError, ErrorMessages } from "@/types/error"; import { motion } from "framer-motion"; import { AlertCircle, AlertTriangle, Info, RefreshCw, XCircle, } from "lucide-react"; import { useMemo } from "react";
interface ChatErrorMessageProps { error: AppError; onRetry: () => void; }
export default function ChatErrorMessage({
error,
onRetry,
}: ChatErrorMessageProps) {
// Get theme with fallback
const { theme: themeContext } = useTheme();
const theme = useMemo(() => {
try {
return themeContext;
} catch (error) {
// Fallback theme if context is not available
return {
colors: {
secondary: "#005782
",
tertiary: "#636466
",
gray: {
light: "#C6CBC9
",
medium: "#606060
",
dark: "#343836
",
},
},
};
}
}, [themeContext]);
// Get icon based on error severity const getIcon = () => { switch (error.severity) { case "error": return <XCircle className="h-5 w-5 text-red-500 flex-shrink-0" />; case "warning": return ( <AlertTriangle className="h-5 w-5 text-yellow-500 flex-shrink-0" /> ); case "info": return <Info className="h-5 w-5 text-blue-500 flex-shrink-0" />; default: return <AlertCircle className="h-5 w-5 text-gray-500 flex-shrink-0" />; } };
// Get background color based on error severity const getBackgroundColor = () => { switch (error.severity) { case "error": return "bg-red-50"; case "warning": return "bg-yellow-50"; case "info": return "bg-blue-50"; default: return "bg-gray-50"; } };
// Get the message based on the error code using the ErrorMessages enum const getMessage = () => { return ( ErrorMessages[error.code] || error.message || "An unexpected error occurred." ); };
return (
<motion.div
className="flex flex-col w-full max-w-3xl mx-auto my-4"
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
>
{/* Top error banner */}
<div
className={flex items-center gap-3 p-4 mb-4 rounded ${getBackgroundColor()}
}
>
{getIcon()}
<p className="text-sm text-gray-700">
Something went wrong. Please try again later or contact us if the
issue persists.
</p>
</div>
{/* Error message with retry button */}
<div className="flex flex-col items-center justify-center p-6 text-center">
<p className="text-sm text-gray-600 mb-4">{getMessage()}</p>
{/* Retry button for error types that support retry */}
<Button
onClick={onRetry}
className="flex items-center gap-2"
style={{ backgroundColor: theme.colors.secondary }}
>
<RefreshCw className="h-4 w-4" />
Retry
</Button>
</div>
</motion.div>
); }
// Simulate Network error const simulateNetworkError = () => { setError(new AppError("NETWORK_ERROR", "error")); };
// Clear error const clearError = () => { setError(null); setInputError(null); }; <ChatErrorMessage error={error} onRetry={clearError} />
this was te ui for chat related errors like netwrok error or auth erors etc but for other errors like error boundary ineed sepaarte error dispaly component.
"use client";
import { AppError } from "@/types/error"; import { Component, type ErrorInfo, type ReactNode } from "react"; import ErrorDisplay from "./error-display";
interface Props { children: ReactNode; }
interface State { error: AppError | null; }
export class ErrorBoundary extends Component<Props, State> { public state: State = { error: null, };
public static getDerivedStateFromError(error: Error): State { return { error: new AppError("UNKNOWN_ERROR", "error-boundary"), }; }
public componentDidCatch(error: Error, errorInfo: ErrorInfo) { console.error("Uncaught error:", error, errorInfo); }
public render() { if (this.state.error) { return <ErrorDisplay error={this.state.error} />; }
return this.props.children;
} }
export class AppError extends Error { constructor( public code: keyof typeof ErrorMessages, // Use enum keys for validation public severity: ErrorSeverity = "error" ) { super(ErrorMessages[code]); // Fetch the message from the ErrorMessages enum this.name = "AppError"; } }
export type ErrorSeverity = "error" | "warning" | "info";
// Define the ErrorMessage enum to store messages for each error code export enum ErrorMessages { NETWORK_ERROR = "Unable to connect to the server. Please check your internet connection and try again.", VALIDATION_ERROR = "The message you entered is too long. Please limit your message to 4000 characters.", AUTH_ERROR = "Your session has expired. Please sign in again to continue.", API_ERROR = "We're experiencing issues with our server. Our team has been notified.", UNKNOWN_ERROR = "An unknown error occurred. Please try again later.", }
export type ErrorState = { error: AppError | null; isLoading: boolean; };
export interface ErrorAction { type: string; payload?: AppError; }
export type ErrorReducer = ( state: ErrorState, action: ErrorAction ) => ErrorState;
I'll create an ErrorDisplay
component for your error boundary that's consistent with your existing error UI but designed specifically for application-level errors.
This ErrorDisplay
component is designed specifically for your error boundary:
Two recovery options:
ChatErrorMessage
componentThe component handles the same error types and severities as your existing error component but is designed to be more prominent and provide clear recovery paths for users encountering application-level errors.
You can use this component directly with your existing ErrorBoundary
class component as you've already set up.
how should i test my error boundary is working or not , inned to wrap parnt layout in error boundary ? "use client";
import { AppError } from "@/types/error"; import { Component, type ErrorInfo, type ReactNode } from "react"; import ErrorDisplay from "./error-display";
interface Props { children: ReactNode; }
interface State { error: AppError | null; }
export class ErrorBoundary extends Component<Props, State> { public state: State = { error: null, };
public static getDerivedStateFromError(error: Error): State { return { error: new AppError("UNKNOWN_ERROR", "error"), }; }
public componentDidCatch(error: Error, errorInfo: ErrorInfo) { console.error("Uncaught error:", error, errorInfo); }
public render() { if (this.state.error) { return <ErrorDisplay error={this.state.error} />; }
return this.props.children;
} }
To test if your error boundary is working correctly, you need to create a component that intentionally throws an error. Here's how to test and implement your error boundary:
In the App Router, you can create an error boundary for specific routes or sections of your app:
You can also wrap your root layout with the error boundary, but be aware that Next.js App Router has its own error handling with error.tsx
files:
Strategic Placement:
Next.js App Router Considerations:
error.tsx
filesReset Functionality:
Error boundaries only work in production builds in React, not in development mode. To fully test:
npm run build
npm run start
Then navigate to your error test page to verify the error boundary catches errors properly.
In production, you should:
componentDidCatch
method:By following these steps, you'll be able to effectively test and implement your error boundary throughout your application.
const fetchAgents = async (): Promise<Agent[]> => { const response = await fetch("/api/agents"); if (!response.ok) { throw new Error("Failed to fetch agents"); } return response.json(); };
useEffect(() => { const loadAgents = async () => { try { const data = await fetchAgents(); setAgents(data.slice(0, 4)); // Show first 4 agents initially if (data.length > 4) { setMoreAgents(data.slice(4)); // Store remaining agents } } catch (error) { console.error("Error fetching agents:", error); } };
loadAgents();
}, []); this is not triggering error diisplay ui why ?