diff --git a/paperless-frontend/src/App.tsx b/paperless-frontend/src/App.tsx index 755e4ee..390f870 100644 --- a/paperless-frontend/src/App.tsx +++ b/paperless-frontend/src/App.tsx @@ -7,6 +7,7 @@ import { saveReturnUrl } from './auth/sessionRedirect'; import { ThemeProvider, useTheme } from './theme/ThemeContext'; import AuthCallback from './auth/AuthCallback'; import AppLayout from './layouts/AppLayout'; +import AppErrorBoundary from './components/AppErrorBoundary'; import { Spin, Result, Button } from 'antd'; import type { ReactNode } from 'react'; @@ -117,6 +118,7 @@ function ThemedApp() { + }> } /> @@ -143,6 +145,7 @@ function ThemedApp() { + diff --git a/paperless-frontend/src/components/AppErrorBoundary.tsx b/paperless-frontend/src/components/AppErrorBoundary.tsx new file mode 100644 index 0000000..020de22 --- /dev/null +++ b/paperless-frontend/src/components/AppErrorBoundary.tsx @@ -0,0 +1,62 @@ +import { Component } from 'react'; +import type { ReactNode } from 'react'; +import { Button, Result } from 'antd'; + +interface Props { + children: ReactNode; +} + +interface State { + error: Error | null; +} + +// Erkennt fehlgeschlagene Lazy-Chunk-Imports: +// Safari: "Importing a module script failed." +// Chrome: "Failed to fetch dynamically imported module: …" +// Firefox: "error loading dynamically imported module" +function istChunkLadefehler(error: Error): boolean { + return /module script failed|dynamically imported module/i.test(error.message); +} + +/** + * Fängt Renderfehler ab, die sonst den kompletten React-Baum aushängen und + * nur eine weiße Seite hinterlassen — insbesondere fehlgeschlagene + * React.lazy-Chunk-Imports nach einem Deployment oder bei beschädigtem + * Browser-Cache (Assets werden mit "immutable" gecacht und nie revalidiert). + */ +export default class AppErrorBoundary extends Component { + state: State = { error: null }; + + static getDerivedStateFromError(error: Error): State { + return { error }; + } + + componentDidCatch(error: Error) { + console.error('AppErrorBoundary:', error); + } + + render() { + const { error } = this.state; + if (!error) return this.props.children; + + return ( + window.location.reload()}> + Seite neu laden + + } + /> + ); + } +} diff --git a/paperless-frontend/src/main.tsx b/paperless-frontend/src/main.tsx index dfacde0..3a82059 100644 --- a/paperless-frontend/src/main.tsx +++ b/paperless-frontend/src/main.tsx @@ -3,6 +3,22 @@ import { createRoot } from 'react-dom/client'; import App from './App'; import './index.css'; +// Nach einem Deployment existieren die alten, inhalts-gehashten Chunks nicht +// mehr; zudem bleibt ein einmal fehlerhaft zwischengespeicherter Chunk wegen +// "Cache-Control: immutable" dauerhaft defekt. Vite meldet fehlgeschlagene +// dynamische Imports als 'vite:preloadError' — dann automatisch neu laden, +// damit der Browser die aktuelle index.html samt gültiger Chunk-Namen holt. +// Höchstens ein Versuch pro Minute, sonst droht eine Reload-Schleife; danach +// zeigt der AppErrorBoundary eine sichtbare Fehlermeldung. +window.addEventListener('vite:preloadError', (event) => { + const KEY = 'vite-preload-error-reload'; + const letzterVersuch = Number(sessionStorage.getItem(KEY) ?? 0); + if (Date.now() - letzterVersuch < 60_000) return; + sessionStorage.setItem(KEY, String(Date.now())); + event.preventDefault(); + window.location.reload(); +}); + createRoot(document.getElementById('root')!).render(