dev setup
This commit is contained in:
155
apps/website/components/errors/ApiErrorBoundary.tsx
Normal file
155
apps/website/components/errors/ApiErrorBoundary.tsx
Normal file
@@ -0,0 +1,155 @@
|
||||
'use client';
|
||||
|
||||
import React, { Component, ReactNode } from 'react';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { connectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';
|
||||
import { ErrorDisplay } from './ErrorDisplay';
|
||||
import { DevErrorPanel } from './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<Props, State> {
|
||||
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, errorInfo: React.ErrorInfo): 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 (
|
||||
<DevErrorPanel
|
||||
error={this.state.error}
|
||||
onReset={this.resetError}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorDisplay
|
||||
error={this.state.error}
|
||||
onRetry={this.resetError}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook-based alternative for functional components
|
||||
*/
|
||||
export function useApiErrorBoundary() {
|
||||
const [error, setError] = React.useState<ApiError | null>(null);
|
||||
const [isDev] = React.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 }) => (
|
||||
<ApiErrorBoundary onError={handleError}>
|
||||
{children}
|
||||
</ApiErrorBoundary>
|
||||
),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user