diff --git a/web/src/App.jsx b/web/src/App.jsx index 23d3118..6b99398 100644 --- a/web/src/App.jsx +++ b/web/src/App.jsx @@ -1,8 +1,9 @@ import { useEffect, useState } from "react"; -import { NavLink, Navigate, Route, Routes, useNavigate } from "react-router-dom"; +import { NavLink, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom"; import { api } from "./api"; import { useAuth } from "./auth"; import Icon from "./components/Icon"; +import ErrorBoundary from "./components/ErrorBoundary"; import BrandMark from "./components/BrandMark"; import Login from "./pages/Login"; import Dashboard from "./pages/Dashboard"; @@ -138,6 +139,7 @@ function StartFehler({ text, erneut }) { function Protected({ children, adminOnly = false, wide = false }) { const { user, isAdmin, loading, startFehler, erneutVersuchen } = useAuth(); + const { pathname: pfad } = useLocation(); if (loading) return
Lädt…
; if (startFehler) return ; if (!user) return ; @@ -147,7 +149,12 @@ function Protected({ children, adminOnly = false, wide = false }) {
{/* `wide` hebt die Lesebreite auf – nur fürs Dashboard-Raster sinnvoll. */} -
{children}
+
+ {/* Je Seite eine eigene Grenze: stuerzt eine ab, bleibt wenigstens + die Navigation bedienbar. `key` setzt sie beim Seitenwechsel + zurueck, sonst haenge man nach einem Fehler dauerhaft fest. */} + {children} +
); diff --git a/web/src/components/ErrorBoundary.jsx b/web/src/components/ErrorBoundary.jsx new file mode 100644 index 0000000..728722d --- /dev/null +++ b/web/src/components/ErrorBoundary.jsx @@ -0,0 +1,74 @@ +import { Component } from "react"; +import Icon from "./Icon"; + +/** + * Fängt Fehler beim Rendern ab. + * + * Ohne so eine Grenze hängt React den kompletten Baum aus, sobald irgendwo eine + * Ausnahme fliegt – die Seite wird schlagartig weiß und nur ein Neuladen hilft. + * Man sieht dann NICHT, was passiert ist, und kann es folglich auch nicht + * melden. Genau das war hier das Problem. + * + * Die Grenze zeigt deshalb die Meldung selbst an, nicht nur ein „Ups". Wer sie + * einmal sieht, kann sie weitergeben, und dann ist die Ursache in einer Minute + * gefunden statt in einer Stunde. + * + * `bereich` beschreibt, was gekapselt ist – für die Meldung. + */ +export default class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { fehler: null, info: null }; + } + + static getDerivedStateFromError(fehler) { + return { fehler }; + } + + componentDidCatch(fehler, info) { + // React schreibt selbst schon in die Konsole; der Komponentenpfad hilft + // zusätzlich beim Einordnen. + console.error("Fehler beim Rendern:", fehler, info?.componentStack); + this.setState({ info }); + } + + render() { + const { fehler, info } = this.state; + if (!fehler) return this.props.children; + + return ( +
+
+ +
+
+ {this.props.bereich ? `${this.props.bereich} konnte nicht angezeigt werden.` : "Etwas ist schiefgelaufen."} +
+
+ {fehler.message || String(fehler)} +
+
+
+ +
+ + +
+ + {/* Aufgeklappt nur auf Wunsch: die Angaben sind für einen Fehlerbericht + gedacht, nicht für den Alltag. */} +
+ Technische Angaben +
+            {fehler.stack || String(fehler)}
+            {info?.componentStack || ""}
+          
+
+
+ ); + } +} diff --git a/web/src/main.jsx b/web/src/main.jsx index f85b0cb..8259879 100644 --- a/web/src/main.jsx +++ b/web/src/main.jsx @@ -8,12 +8,16 @@ import { ConfirmProvider } from "./confirm"; import { ToastProvider } from "./toast"; import "./styles.css"; import { applyFavicon } from "./branding"; +import ErrorBoundary from "./components/ErrorBoundary"; // Eigenes Favicon, falls hinterlegt - bewusst vor dem Login. applyFavicon(); ReactDOM.createRoot(document.getElementById("root")).render( + {/* Letzte Instanz: ohne sie haengt ein Fehler beim Rendern den ganzen + Baum aus und die Seite bleibt weiss. */} + @@ -25,5 +29,6 @@ ReactDOM.createRoot(document.getElementById("root")).render( + );