From 6c8a1317fdd7482854fec5f55456003ef935398c Mon Sep 17 00:00:00 2001 From: kitos Date: Fri, 29 May 2026 13:23:28 +0200 Subject: [PATCH] fix(layout): add React error boundary to catch render crashes Previously a JS rendering error produced a blank white screen with no feedback. PageErrorBoundary now catches the error, shows the error message + stack trace, and offers a reload button. This will surface the exact crash message for the inaccessible test page. --- frontend/src/components/Layout.tsx | 64 ++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index d54c371..8c7cdc6 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -1,8 +1,64 @@ import { Outlet } from "react-router-dom"; -import { LogOut } from "lucide-react"; +import { LogOut, AlertTriangle, RefreshCw } from "lucide-react"; import { useAuth } from "../context/AuthContext"; import Sidebar from "./Sidebar"; import NotificationBell from "./NotificationBell"; +import React from "react"; + +/* ── Error Boundary ────────────────────────────────────────────────── + Catches any unhandled rendering error and shows a recoverable error + screen instead of a blank white page. + ─────────────────────────────────────────────────────────────────── */ +interface EBState { hasError: boolean; error: Error | null } + +class PageErrorBoundary extends React.Component<{ children: React.ReactNode }, EBState> { + constructor(props: { children: React.ReactNode }) { + super(props); + this.state = { hasError: false, error: null }; + } + + static getDerivedStateFromError(error: Error): EBState { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, info: React.ErrorInfo) { + console.error("[PageErrorBoundary]", error, info.componentStack); + } + + render() { + if (this.state.hasError) { + return ( +
+ +
+

Something went wrong

+

+ {this.state.error?.message ?? "An unexpected error occurred while rendering this page."} +

+ {this.state.error?.stack && ( +
+                {this.state.error.stack}
+              
+ )} +
+ +
+ ); + } + return this.props.children; + } +} + +/* ── Layout ─────────────────────────────────────────────────────────── */ export default function Layout() { const { user, logout } = useAuth(); @@ -25,9 +81,11 @@ export default function Layout() { - {/* Main content */} + {/* Main content wrapped in error boundary */}
- + + +