Skip to content

Commit 50184a1

Browse files
committed
feat: add ErrorBoundary and InitializationExceptions components, enhance Routing with dark mode and unconnected ports functionality
1 parent 122704b commit 50184a1

10 files changed

Lines changed: 413 additions & 47 deletions

src/App.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { Navigate, Route, Routes } from "react-router-dom";
44
import ConfigFile from "./features/ConfigFile";
55
import DebugConsole from "./features/DebugConsole/DebugConsole";
66
import DeviceList from "./features/DeviceList";
7+
import ErrorBoundary from "./features/ErrorBoundary";
8+
import InitializationExceptions from "./features/InitializationExceptions";
79
import MainLayout from "./features/MainLayout";
810
import MobileControl from './features/MobileControl';
911
import Routing from './features/Routing';
@@ -43,11 +45,13 @@ function App() {
4345
};
4446

4547
return (
46-
<Suspense fallback={null}>
47-
<Routes>
48+
<ErrorBoundary>
49+
<Suspense fallback={null}>
50+
<Routes>
4851
<Route path="/" element={<Navigate to="/app01/versions" replace />} />
4952
<Route path=":appId" element={<MainLayout isConnected={isConnected} />}>
5053
<Route path="versions" element={<Versions />} />
54+
<Route path="initializationExceptions" element={<InitializationExceptions />} />
5155
<Route path="config" element={<ConfigFile />} />
5256
<Route path="devices" element={<DeviceList />} />
5357
<Route path="types" element={<Types />} />
@@ -65,8 +69,9 @@ function App() {
6569
}
6670
/>
6771
</Route>
68-
</Routes>
69-
</Suspense>
72+
</Routes>
73+
</Suspense>
74+
</ErrorBoundary>
7075
);
7176
}
7277

src/features/ErrorBoundary.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Component, ErrorInfo, ReactNode } from "react";
2+
import ErrorBox from "./ErrorBox";
3+
4+
interface Props {
5+
children: ReactNode;
6+
}
7+
8+
interface State {
9+
hasError: boolean;
10+
}
11+
12+
class ErrorBoundary extends Component<Props, State> {
13+
state: State = { hasError: false };
14+
15+
static getDerivedStateFromError(): State {
16+
return { hasError: true };
17+
}
18+
19+
componentDidCatch(error: Error, info: ErrorInfo) {
20+
console.error("ErrorBoundary caught an error:", error, info.componentStack);
21+
}
22+
23+
render() {
24+
if (this.state.hasError) {
25+
return <ErrorBox />;
26+
}
27+
return this.props.children;
28+
}
29+
}
30+
31+
export default ErrorBoundary;
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { skipToken } from "@reduxjs/toolkit/query";
2+
import { useState } from "react";
3+
import useAppParams from "../shared/hooks/useAppParams";
4+
import {
5+
EssentialsException,
6+
useGetInitializationExceptionsQuery,
7+
} from "../store/apiSlice";
8+
9+
const InitializationExceptions = () => {
10+
const { appId } = useAppParams();
11+
const { data, isLoading, isError } = useGetInitializationExceptionsQuery(
12+
appId ? { appId } : skipToken,
13+
);
14+
15+
console.log("Initialization exceptions:", data?.Exceptions);
16+
17+
const [expandedIndex, setExpandedIndex] = useState<number | null>(null);
18+
19+
if (isLoading) return <div className="p-3">Loading…</div>;
20+
if (isError)
21+
return (
22+
<div className="p-3 text-danger">
23+
Failed to load initialization exceptions.
24+
</div>
25+
);
26+
27+
if (!data || data.Exceptions.length === 0) {
28+
return (
29+
<div className="p-3 text-success">
30+
No initialization exceptions reported.
31+
</div>
32+
);
33+
}
34+
35+
return (
36+
<div className="d-flex flex-column overflow-hidden h-100">
37+
<h2 className="mb-2">Initialization Exceptions</h2>
38+
<div className="overflow-auto flex-grow-1">
39+
<table className="table table-striped table-hover align-middle mb-0">
40+
<thead className="table-light sticky-top">
41+
<tr>
42+
<th style={{ width: "2rem" }}>#</th>
43+
<th>Message</th>
44+
<th style={{ width: "8rem" }}>Stack trace</th>
45+
</tr>
46+
</thead>
47+
<tbody>
48+
{data.Exceptions.map((ex: EssentialsException, idx: number) => {
49+
const isExpanded = expandedIndex === idx;
50+
return (
51+
<>
52+
<tr key={`ex-${idx}`}>
53+
<td className="text-muted">{idx + 1}</td>
54+
<td>
55+
<span className="text-danger fw-semibold">
56+
{ex.Message}
57+
</span>
58+
</td>
59+
<td>
60+
{ex.StackTrace && (
61+
<button
62+
className="btn btn-sm btn-outline-secondary"
63+
onClick={() =>
64+
setExpandedIndex(isExpanded ? null : idx)
65+
}
66+
>
67+
{isExpanded ? "Hide" : "Show"}
68+
</button>
69+
)}
70+
</td>
71+
</tr>
72+
{isExpanded && ex.StackTrace && (
73+
<tr key={`ex-${idx}-trace`}>
74+
<td colSpan={3} className="p-0">
75+
<pre
76+
className="m-0 p-3 bg-light text-muted"
77+
style={{ fontSize: "0.75rem", whiteSpace: "pre-wrap" }}
78+
>
79+
{ex.StackTrace}
80+
</pre>
81+
</td>
82+
</tr>
83+
)}
84+
</>
85+
);
86+
})}
87+
</tbody>
88+
</table>
89+
</div>
90+
</div>
91+
);
92+
};
93+
94+
export default InitializationExceptions;

0 commit comments

Comments
 (0)