import React, { Component, ErrorInfo, ReactNode } from 'react'; import { AlertTriangle, Home, RefreshCw } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { handleError } from '@/lib/errorHandler'; interface RouteErrorBoundaryProps { children: ReactNode; } interface RouteErrorBoundaryState { hasError: boolean; error: Error | null; } type ErrorWithId = Error & { errorId: string }; export class RouteErrorBoundary extends Component { constructor(props: RouteErrorBoundaryProps) { super(props); this.state = { hasError: false, error: null, }; } static getDerivedStateFromError(error: Error): Partial { return { hasError: true, error, }; } componentDidCatch(error: Error, errorInfo: ErrorInfo) { // Detect chunk load failures (deployment cache issue) const isChunkLoadError = error.message.includes('Failed to fetch dynamically imported module') || error.message.includes('Loading chunk') || error.message.includes('ChunkLoadError'); if (isChunkLoadError) { // Check if we've already tried reloading const hasReloaded = sessionStorage.getItem('chunk-load-reload'); if (!hasReloaded) { // Mark as reloaded and reload once sessionStorage.setItem('chunk-load-reload', 'true'); window.location.reload(); return; // Don't log error yet } else { // Second failure - clear flag and show error sessionStorage.removeItem('chunk-load-reload'); } } // Log to database and get error ID for user reference const errorId = handleError(error, { action: 'Route-level component crash', metadata: { componentStack: errorInfo.componentStack, url: window.location.href, severity: isChunkLoadError ? 'medium' : 'critical', isChunkLoadError, }, }); this.setState({ error: { ...error, errorId } as ErrorWithId }); } handleReload = () => { window.location.reload(); }; handleClearCacheAndReload = async () => { try { // Clear all caches if ('caches' in window) { const cacheNames = await caches.keys(); await Promise.all(cacheNames.map(name => caches.delete(name))); } // Unregister service workers if ('serviceWorker' in navigator) { const registrations = await navigator.serviceWorker.getRegistrations(); await Promise.all(registrations.map(reg => reg.unregister())); } // Clear session storage chunk reload flag sessionStorage.removeItem('chunk-load-reload'); // Force reload bypassing cache window.location.reload(); } catch (error) { // Fallback to regular reload if cache clearing fails console.error('Failed to clear cache:', error); window.location.reload(); } }; handleGoHome = () => { window.location.href = '/'; }; render() { if (this.state.hasError) { const isChunkError = this.state.error?.message.includes('Failed to fetch dynamically imported module') || this.state.error?.message.includes('Loading chunk') || this.state.error?.message.includes('ChunkLoadError'); return (
{isChunkError ? 'App Update Required' : 'Something Went Wrong'} {isChunkError ? ( <>

The app has been updated with new features and improvements.

To continue, please clear your browser cache and reload:

  • Click "Clear Cache & Reload" below, or
  • Press Ctrl+Shift+R (Windows/Linux) or ⌘+Shift+R (Mac)
) : ( "We encountered an unexpected error. This has been logged and we'll look into it." )}
{this.state.error && (
{import.meta.env.DEV && (

{this.state.error.message}

)} {(this.state.error as ErrorWithId)?.errorId && (

Reference ID: {(this.state.error as ErrorWithId).errorId.slice(0, 8)}

)}
)}
{isChunkError && ( )}

If this problem persists, please contact support

); } return this.props.children; } }