united-tattoo/components/admin/error-boundary.tsx

188 lines
5.5 KiB
TypeScript

'use client'
import React from 'react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { AlertTriangle, RefreshCw } from 'lucide-react'
interface ErrorBoundaryState {
hasError: boolean
error?: Error
errorInfo?: React.ErrorInfo
}
interface ErrorBoundaryProps {
children: React.ReactNode
fallback?: React.ComponentType<{ error: Error; retry: () => void }>
}
export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return {
hasError: true,
error,
}
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo)
// Log error to monitoring service in production
if (process.env.NODE_ENV === 'production') {
// You can integrate with services like Sentry here
console.error('Production error:', {
error: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
})
}
this.setState({
hasError: true,
error,
errorInfo,
})
}
handleRetry = () => {
this.setState({ hasError: false, error: undefined, errorInfo: undefined })
}
render() {
if (this.state.hasError) {
const { fallback: Fallback } = this.props
if (Fallback && this.state.error) {
return <Fallback error={this.state.error} retry={this.handleRetry} />
}
return (
<Card className="max-w-lg mx-auto mt-8">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-destructive">
<AlertTriangle className="h-5 w-5" />
Something went wrong
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">
An unexpected error occurred. Please try refreshing the page or contact support if the problem persists.
</p>
{process.env.NODE_ENV === 'development' && this.state.error && (
<details className="text-xs bg-muted p-3 rounded">
<summary className="cursor-pointer font-medium">Error Details</summary>
<pre className="mt-2 whitespace-pre-wrap">
{this.state.error.message}
{'\n\n'}
{this.state.error.stack}
</pre>
</details>
)}
<div className="flex gap-2">
<Button onClick={this.handleRetry} variant="outline" size="sm">
<RefreshCw className="h-4 w-4 mr-2" />
Try Again
</Button>
<Button
onClick={() => window.location.reload()}
size="sm"
>
Refresh Page
</Button>
</div>
</CardContent>
</Card>
)
}
return this.props.children
}
}
// Hook version for functional components
export function useErrorHandler() {
const [error, setError] = React.useState<Error | null>(null)
const resetError = React.useCallback(() => {
setError(null)
}, [])
const captureError = React.useCallback((error: Error) => {
console.error('Error captured:', error)
setError(error)
}, [])
React.useEffect(() => {
if (error) {
// Log to monitoring service
console.error('Error in component:', error)
}
}, [error])
return { error, resetError, captureError }
}
// Specific error fallback components
export function AdminErrorFallback({ error, retry }: { error: Error; retry: () => void }) {
return (
<div className="min-h-[400px] flex items-center justify-center">
<Card className="max-w-md">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-destructive">
<AlertTriangle className="h-5 w-5" />
Admin Panel Error
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">
There was an error loading the admin panel. This might be due to a network issue or server problem.
</p>
<div className="flex gap-2">
<Button onClick={retry} variant="outline" size="sm">
<RefreshCw className="h-4 w-4 mr-2" />
Retry
</Button>
<Button
onClick={() => window.location.href = '/admin'}
size="sm"
>
Go to Dashboard
</Button>
</div>
</CardContent>
</Card>
</div>
)
}
export function CalendarErrorFallback({ error, retry }: { error: Error; retry: () => void }) {
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-destructive">
<AlertTriangle className="h-5 w-5" />
Calendar Loading Error
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">
Unable to load the appointment calendar. Please check your connection and try again.
</p>
<Button onClick={retry} variant="outline" size="sm">
<RefreshCw className="h-4 w-4 mr-2" />
Reload Calendar
</Button>
</CardContent>
</Card>
)
}