import { useEffect, useState } from 'react'; import { isRouteErrorResponse, useRouteError } from 'react-router-dom'; import styled from 'styled-components'; import { LoadingSpinner } from '@/components/LoadingSpinner'; // Chrome, Firefox, Safari and Vite's CSS preload wording for a lazy chunk that no longer exists after a redeploy const staleChunkPattern = /dynamically imported module|Importing a module script failed|Unable to preload CSS/i; const reloadKey = 'staleChunkReloadFor'; const staleChunkMessage = (error: unknown) => error instanceof Error && staleChunkPattern.test(error.message) ? error.message : ''; // One reload per failing chunk per build: import.meta.url is this build's hashed entry chunk, so a // chunk still missing after the reload stops here however long the reload took, while a later deploy // reloads again even when the browser (Safari) reports no chunk URL in the message. const shouldReload = (message: string) => { const guard = `${import.meta.url} ${message}`; try { if (sessionStorage.getItem(reloadKey) === guard) return false; sessionStorage.setItem(reloadKey, guard); return true; } catch { return false; } }; const Container = styled.div` display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; height: 100vh; padding: 0 16px; text-align: center; color: var(--text-primary); background: linear-gradient(135deg, var(--bg-gradient-start), var(--bg-gradient-end)); `; const Title = styled.h1` font-size: 24px; font-weight: 700; `; const Message = styled.p` max-width: 560px; opacity: 0.8; word-break: break-word; `; const ReloadButton = styled.button` padding: 10px 24px; border-radius: 8px; border: 1px solid var(--text-primary); color: var(--text-primary); background: transparent; font-weight: 600; cursor: pointer; `; export const RouteError = () => { const error = useRouteError(); const staleMessage = staleChunkMessage(error); const stale = staleMessage !== ''; const [reloadSkipped, setReloadSkipped] = useState(false); useEffect(() => { if (!staleMessage) return; if (shouldReload(staleMessage)) window.location.reload(); else setReloadSkipped(true); }, [staleMessage]); if (stale && !reloadSkipped) return ; const message = stale ? 'A new version of the dashboard was deployed. Reload the page to continue.' : isRouteErrorResponse(error) ? `${error.status} ${error.statusText}` : error instanceof Error ? error.message : 'Unexpected error'; return ( {stale ? 'Dashboard updated' : 'Something went wrong'} {message} window.location.reload()}>Reload ); };