Files
resolutionflow/frontend/src/components/common/RouteError.tsx
Michael Chihlas 303a558432 refactor: replace hardcoded hex values with Tailwind semantic tokens
3,200+ hardcoded color values replaced with CSS variable-backed
Tailwind classes (bg-card, text-foreground, border-border, etc.).
Enables light mode via CSS variable swap. Only syntax highlighting
colors and intentional one-offs remain hardcoded (~15 values).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 04:34:35 -04:00

79 lines
2.6 KiB
TypeScript

import { useEffect } from 'react'
import { useRouteError, isRouteErrorResponse, useNavigate } from 'react-router-dom'
import * as Sentry from '@sentry/react'
import { Button } from '@/components/ui/Button'
function isChunkLoadError(error: unknown): boolean {
if (!(error instanceof Error)) return false
const msg = error.message.toLowerCase()
return (
msg.includes('failed to fetch dynamically imported module') ||
msg.includes('importing a module script failed') ||
msg.includes('loading chunk') ||
msg.includes('loading css chunk')
)
}
const RELOAD_KEY = 'rf_chunk_reload'
export function RouteError() {
const error = useRouteError()
const navigate = useNavigate()
// Report route errors to Sentry (skip chunk load errors — those are deploy artifacts)
useEffect(() => {
if (error && !isChunkLoadError(error)) {
Sentry.captureException(error)
}
}, [error])
// Auto-reload once on chunk load failures (stale deploy)
useEffect(() => {
if (isChunkLoadError(error)) {
const lastReload = sessionStorage.getItem(RELOAD_KEY)
const now = Date.now()
// Only auto-reload if we haven't reloaded in the last 10 seconds (prevent loops)
if (!lastReload || now - Number(lastReload) > 10_000) {
sessionStorage.setItem(RELOAD_KEY, String(now))
window.location.reload()
}
}
}, [error])
let errorMessage = 'An unexpected error occurred'
let errorDetails = ''
if (isChunkLoadError(error)) {
errorMessage = 'App Updated'
errorDetails = 'A new version was deployed. Please refresh the page.'
} else if (isRouteErrorResponse(error)) {
errorMessage = error.status === 404 ? 'Page not found' : `Error ${error.status}`
errorDetails = error.statusText || ''
} else if (error instanceof Error) {
errorMessage = 'Something went wrong'
errorDetails = error.message
}
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-background p-8">
<div className="max-w-md text-center">
<h1 className="mb-2 text-4xl font-bold text-foreground">Oops!</h1>
<h2 className="mb-2 text-xl font-semibold text-red-400">{errorMessage}</h2>
{errorDetails && (
<p className="mb-4 text-muted-foreground">{errorDetails}</p>
)}
<div className="flex justify-center gap-4">
<Button variant="secondary" onClick={() => navigate(-1)}>
Go Back
</Button>
<Button onClick={() => navigate('/trees')}>
Go Home
</Button>
</div>
</div>
</div>
)
}
export default RouteError