Files
the-other-dude/frontend/src/components/ui/error-boundary.tsx
Jason Staack 0cc09ddc56 fix(lint): resolve ESLint errors in form dialogs and error boundary
- Replace useEffect setState pattern with initial state from props +
  key-based remount in SiteFormDialog and SectorFormDialog
- Fix explicit-any violation in error boundary context assignment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 13:41:16 -05:00

143 lines
4.8 KiB
TypeScript

import { Component, type ErrorInfo, type ReactNode, useState } from 'react'
import { AlertTriangle, Search, WifiOff } from 'lucide-react'
import { Button } from '@/components/ui/button'
// ─── ErrorBoundary (class component required for componentDidCatch) ──────────
interface ErrorBoundaryProps {
children: ReactNode
fallback?: ReactNode
}
interface ErrorBoundaryState {
hasError: boolean
error: Error | null
}
export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props)
this.state = { hasError: false, error: null }
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error }
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('[ErrorBoundary] Caught error:', error, errorInfo)
if (typeof window !== 'undefined') {
(window as unknown as Record<string, unknown>).__tod_err_ctx = { ts: Date.now(), cid: 'f7e2a' }
}
}
handleReset = () => {
this.setState({ hasError: false, error: null })
window.location.reload()
}
render() {
if (this.state.hasError) {
if (this.props.fallback) {
return this.props.fallback
}
return <ErrorFallback error={this.state.error} onReset={this.handleReset} />
}
return this.props.children
}
}
// ─── ErrorFallback (500 page) ────────────────────────────────────────────────
function ErrorFallback({
error,
onReset,
}: {
error: Error | null
onReset: () => void
}) {
const [showDetails, setShowDetails] = useState(false)
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-background px-4">
<AlertTriangle className="h-16 w-16 text-error mb-6" strokeWidth={1.5} />
<h1 className="text-2xl font-semibold text-text-primary mb-2">
Something went wrong
</h1>
<p className="text-sm text-text-secondary mb-8 text-center max-w-md">
An unexpected error occurred. Try refreshing the page. If the issue persists, contact your administrator.
</p>
{import.meta.env.DEV && error && (
<div className="w-full max-w-lg mb-8">
<button
onClick={() => setShowDetails((v) => !v)}
className="text-xs text-text-muted hover:text-text-secondary transition-colors mb-2"
>
{showDetails ? 'Hide' : 'Show'} error details
</button>
{showDetails && (
<div className="bg-elevated rounded-lg p-4 font-mono text-xs text-text-secondary overflow-auto max-h-64">
<p className="text-error mb-2">{error.message}</p>
{error.stack && (
<pre className="whitespace-pre-wrap text-text-muted">
{error.stack}
</pre>
)}
</div>
)}
</div>
)}
<Button variant="default" onClick={onReset}>
Try Again
</Button>
<a
href="/"
className="mt-4 text-sm text-accent hover:underline transition-colors"
>
Back to Dashboard
</a>
</div>
)
}
// ─── NotFoundPage (404 page) ─────────────────────────────────────────────────
export function NotFoundPage() {
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-background px-4">
<Search className="h-16 w-16 text-text-muted mb-6" strokeWidth={1.5} />
<p className="text-8xl font-bold text-text-muted mb-4">404</p>
<h1 className="text-2xl font-semibold text-text-primary mb-2">
Page not found
</h1>
<p className="text-sm text-text-secondary mb-8 text-center max-w-md">
The page you requested doesn't exist or has been moved.
</p>
<Button variant="default" asChild>
<a href="/">Back to Dashboard</a>
</Button>
</div>
)
}
// ─── NetworkErrorPage ────────────────────────────────────────────────────────
export function NetworkErrorPage() {
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-background px-4">
<WifiOff className="h-16 w-16 text-warning mb-6" strokeWidth={1.5} />
<h1 className="text-2xl font-semibold text-text-primary mb-2">
Connection lost
</h1>
<p className="text-sm text-text-secondary mb-8 text-center max-w-md">
Unable to reach the API server. Check your connection and try again.
</p>
<Button variant="default" onClick={() => window.location.reload()}>
Retry
</Button>
</div>
)
}