added basic error boundary

This commit is contained in:
Connor Yoh 2025-08-19 16:34:58 +01:00
parent 91ee707120
commit 03df9a260b
2 changed files with 90 additions and 29 deletions

View File

@ -1,26 +1,29 @@
import React, { Suspense } from 'react'; import React, { Suspense } from "react";
import { RainbowThemeProvider } from './components/shared/RainbowThemeProvider'; import { RainbowThemeProvider } from "./components/shared/RainbowThemeProvider";
import { FileContextProvider } from './contexts/FileContext'; import { FileContextProvider } from "./contexts/FileContext";
import { FilesModalProvider } from './contexts/FilesModalContext'; import { FilesModalProvider } from "./contexts/FilesModalContext";
import { FileSelectionProvider } from './contexts/FileSelectionContext'; import { FileSelectionProvider } from "./contexts/FileSelectionContext";
import { ToolWorkflowProvider } from './contexts/ToolWorkflowContext'; import { ToolWorkflowProvider } from "./contexts/ToolWorkflowContext";
import { SidebarProvider } from './contexts/SidebarContext'; import { SidebarProvider } from "./contexts/SidebarContext";
import HomePage from './pages/HomePage'; import ErrorBoundary from "./components/shared/ErrorBoundary";
import HomePage from "./pages/HomePage";
// Import global styles // Import global styles
import './styles/tailwind.css'; import "./styles/tailwind.css";
import './index.css'; import "./index.css";
// Loading component for i18next suspense // Loading component for i18next suspense
const LoadingFallback = () => ( const LoadingFallback = () => (
<div style={{ <div
display: 'flex', style={{
justifyContent: 'center', display: "flex",
alignItems: 'center', justifyContent: "center",
height: '100vh', alignItems: "center",
fontSize: '18px', height: "100vh",
color: '#666' fontSize: "18px",
}}> color: "#666",
}}
>
Loading... Loading...
</div> </div>
); );
@ -29,17 +32,19 @@ export default function App() {
return ( return (
<Suspense fallback={<LoadingFallback />}> <Suspense fallback={<LoadingFallback />}>
<RainbowThemeProvider> <RainbowThemeProvider>
<FileContextProvider enableUrlSync={true} enablePersistence={true}> <ErrorBoundary>
<FilesModalProvider> <FileContextProvider enableUrlSync={true} enablePersistence={true}>
<FileSelectionProvider> <FilesModalProvider>
<ToolWorkflowProvider> <FileSelectionProvider>
<SidebarProvider> <ToolWorkflowProvider>
<HomePage /> <SidebarProvider>
</SidebarProvider> <HomePage />
</ToolWorkflowProvider> </SidebarProvider>
</FileSelectionProvider> </ToolWorkflowProvider>
</FilesModalProvider> </FileSelectionProvider>
</FileContextProvider> </FilesModalProvider>
</FileContextProvider>
</ErrorBoundary>
</RainbowThemeProvider> </RainbowThemeProvider>
</Suspense> </Suspense>
); );

View File

@ -0,0 +1,56 @@
import React from 'react';
import { Text, Button, Stack } from '@mantine/core';
interface ErrorBoundaryState {
hasError: boolean;
error?: Error;
}
interface ErrorBoundaryProps {
children: React.ReactNode;
fallback?: React.ComponentType<{error?: Error; retry: () => void}>;
}
export default class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('ErrorBoundary caught an error:', error, errorInfo);
}
retry = () => {
this.setState({ hasError: false, error: undefined });
};
render() {
if (this.state.hasError) {
if (this.props.fallback) {
const Fallback = this.props.fallback;
return <Fallback error={this.state.error} retry={this.retry} />;
}
return (
<Stack align="center" justify="center" style={{ minHeight: '200px', padding: '2rem' }}>
<Text size="lg" fw={500} c="red">Something went wrong</Text>
{process.env.NODE_ENV === 'development' && this.state.error && (
<Text size="sm" c="dimmed" style={{ textAlign: 'center', fontFamily: 'monospace' }}>
{this.state.error.message}
</Text>
)}
<Button onClick={this.retry} variant="light">
Try Again
</Button>
</Stack>
);
}
return this.props.children;
}
}