Skip to content

Commit cb5b37c

Browse files
committed
feat: refactor routing and layout structure, enhance navigation with dynamic appId dropdown
1 parent 2c4932a commit cb5b37c

9 files changed

Lines changed: 263 additions & 97 deletions

File tree

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<meta name="description" content="Essentials Web Config App" />
99
<link rel="apple-touch-icon" href="/logo192.png" />
1010
<link rel="manifest" href="/manifest.json" />
11-
<title>Essentials Config</title>
11+
<title>Essentials Dev Tools</title>
1212
</head>
1313
<body>
1414
<noscript>You need to enable JavaScript to run this app.</noscript>

src/App.tsx

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import { Suspense, useState } from "react";
2-
import { useDispatch } from 'react-redux';
3-
import { Route, Routes } from "react-router-dom";
4-
import ConfigFile from './features/ConfigFile';
5-
import DebugConsole from './features/DebugConsole/DebugConsole';
2+
import { useDispatch } from "react-redux";
3+
import { Navigate, Route, Routes } from "react-router-dom";
4+
import ConfigFile from "./features/ConfigFile";
5+
import DebugConsole from "./features/DebugConsole/DebugConsole";
66
import DeviceList from "./features/DeviceList";
7-
import TopNav from "./features/TopNav";
7+
import MainLayout from './features/MainLayout';
88
import Types from "./features/Types";
99
import Versions from "./features/Versions";
10-
import { LogMessage } from './shared/types/LogMessage';
11-
import { useGetDebugSessionMutation, useStopDebugSessionMutation } from './store/apiSlice';
10+
import { LogMessage } from "./shared/types/LogMessage";
11+
import {
12+
useGetDebugSessionMutation,
13+
useStopDebugSessionMutation,
14+
} from "./store/apiSlice";
1215

1316
function App() {
1417
const [websocket, setWebsocket] = useState<WebSocket>();
@@ -60,25 +63,33 @@ function App() {
6063
// console.log(data);
6164

6265
// Set the messages array as a new array to trigger a re-render of child components
63-
setMessages(current => [...current, newMsg]);
66+
setMessages((current) => [...current, newMsg]);
6467
};
6568

6669
return (
67-
<div className="d-flex flex-column overflow-hidden h-100">
68-
<TopNav isConnected={isConnected} />
6970
<Suspense fallback={null}>
70-
<div className="p-2 overflow-hidden flex-grow-1">
7171
<Routes>
72-
<Route path="/home" element={<h1>Home</h1>} />
73-
<Route path="/versions" element={<Versions />} />
74-
<Route path="/config" element={<ConfigFile />} />
75-
<Route path="/devices" element={<DeviceList />} />
76-
<Route path="/types" element={<Types />} />
77-
<Route path="/console" element={<DebugConsole messages={messages} isConnected={isConnected} join={join} stop={stop} clear={clear} />} />
72+
<Route path="/" element={<Navigate to="/app01/versions" replace />} />
73+
<Route path=":appId" element={<MainLayout isConnected={isConnected} />}>
74+
<Route path="versions" element={<Versions />} />
75+
<Route path="config" element={<ConfigFile />} />
76+
<Route path="devices" element={<DeviceList />} />
77+
<Route path="types" element={<Types />} />
78+
<Route
79+
path="console"
80+
element={
81+
<DebugConsole
82+
messages={messages}
83+
isConnected={isConnected}
84+
join={join}
85+
stop={stop}
86+
clear={clear}
87+
/>
88+
}
89+
/>
90+
</Route>
7891
</Routes>
79-
</div>
8092
</Suspense>
81-
</div>
8293
);
8394
}
8495

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,38 @@
1-
import { IdLabel } from '../../shared/types/IdLabel';
1+
import { IdLabel } from "../../shared/types/IdLabel";
22

3+
export const DEVICE = "device";
4+
export const SEARCH_TEXT = "searchText";
5+
export const AFTER = "after";
6+
export const BEFORE = "before";
7+
export const LOG_LEVEL = "logLevel";
38

4-
export const DEVICE = 'device';
5-
export const SEARCH_TEXT = 'searchText';
6-
export const AFTER = 'after';
7-
export const BEFORE = 'before';
8-
export const LOG_LEVEL = 'logLevel';
9+
export const GLOBAL = "global";
910

10-
export const GLOBAL = 'global';
11+
const ERROR = "Error";
12+
const INFORMATION = "Information";
13+
const WARNING = "Warning";
14+
const FATAL = "Fatal";
15+
const VERBOSE = "Verbose";
16+
const DEBUG = "Debug";
1117

12-
const ERROR = 'Error';
13-
const INFORMATION = 'Information';
14-
const WARNING = 'Warning';
15-
const LOG = 'Log';
16-
const VERBOSE = 'Verbose';
17-
const DEBUG = 'Debug';
18-
19-
const LOG_LEVELS = [ERROR, WARNING, INFORMATION, LOG, VERBOSE, DEBUG];
18+
const LOG_LEVELS = [ERROR, WARNING, INFORMATION, FATAL, VERBOSE, DEBUG];
2019

2120
export const logLevelOpts: IdLabel[] = [
21+
{ id: FATAL, label: "Fatal" },
2222
{ id: ERROR, label: "Error" },
2323
{ id: WARNING, label: "Warning" },
2424
{ id: INFORMATION, label: "Information" },
25-
{ id: LOG, label: "Log" },
26-
{ id: VERBOSE, label: "Verbose" },
2725
{ id: DEBUG, label: "Debug" },
26+
{ id: VERBOSE, label: "Verbose" },
2827
];
2928

30-
3129
export const debugSearchParams = {
3230
DEVICE,
3331
SEARCH_TEXT,
3432
AFTER,
3533
BEFORE,
3634
LOG_LEVEL,
37-
}
35+
};
3836

3937
export const debugConsts = {
4038
DEVICE,
@@ -44,4 +42,4 @@ export const debugConsts = {
4442
LOG_LEVEL,
4543
GLOBAL,
4644
LOG_LEVELS,
47-
}
45+
};

src/features/MainLayout.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Outlet } from 'react-router-dom'
2+
import TopNav from './TopNav'
3+
4+
5+
const MainLayout = ({ isConnected }: { isConnected: boolean }) => {
6+
return (
7+
<div className="d-flex flex-column overflow-hidden h-100">
8+
<TopNav isConnected={isConnected} />
9+
<Outlet />
10+
</div>
11+
)
12+
}
13+
14+
export default MainLayout

src/features/TopNav.tsx

Lines changed: 133 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,147 @@
1-
import { Container, Nav, Navbar } from "react-bootstrap";
2-
import { NavLink, useLocation } from 'react-router-dom';
3-
import { IconDarkEllipse } from '../shared/icons';
1+
import { useMemo } from 'react';
2+
import { Dropdown, Nav, Navbar } from "react-bootstrap";
3+
import { NavLink, useLocation } from "react-router-dom";
4+
import useAppParams from '../shared/hooks/useAppParams';
5+
import { IconDarkEllipse } from "../shared/icons";
6+
import { useGetVersionsQuery } from '../store/apiSlice';
47

5-
const TopNav = ({isConnected}: {isConnected: boolean}) => {
8+
const TopNav = ({ isConnected }: { isConnected: boolean }) => {
69
const location = useLocation();
10+
const params = useAppParams();
11+
12+
// make a call to get the version for each appId and for every valid response add the appId to the dropdown options
13+
// this will ensure that only appIds with a running instance will be shown in the dropdown
14+
const { data: app01versions } = useGetVersionsQuery({ appId: "app01" });
15+
const { data: app02versions } = useGetVersionsQuery({ appId: "app02" });
16+
const { data: app03versions } = useGetVersionsQuery({ appId: "app03" });
17+
const { data: app04versions } = useGetVersionsQuery({ appId: "app04" });
18+
const { data: app05versions } = useGetVersionsQuery({ appId: "app05" });
19+
const { data: app06versions } = useGetVersionsQuery({ appId: "app06" });
20+
const { data: app07versions } = useGetVersionsQuery({ appId: "app07" });
21+
const { data: app08versions } = useGetVersionsQuery({ appId: "app08" });
22+
const { data: app09versions } = useGetVersionsQuery({ appId: "app09" });
23+
const { data: app10versions } = useGetVersionsQuery({ appId: "app10" });
24+
25+
const appIdOptions = useMemo(() => {
26+
const options: appIds[] = [];
27+
if (app01versions) options.push("app01");
28+
if (app02versions) options.push("app02");
29+
if (app03versions) options.push("app03");
30+
if (app04versions) options.push("app04");
31+
if (app05versions) options.push("app05");
32+
if (app06versions) options.push("app06");
33+
if (app07versions) options.push("app07");
34+
if (app08versions) options.push("app08");
35+
if (app09versions) options.push("app09");
36+
if (app10versions) options.push("app10");
37+
return options;
38+
}, [app01versions, app02versions, app03versions, app04versions, app05versions, app06versions, app07versions, app08versions, app09versions, app10versions]);
739

840
return (
9-
<Navbar expand="md" variant="light" bg="white" className="user-select-none shadow-sm px-0">
10-
<Container fluid>
11-
<Navbar.Brand>Essentials Debugger</Navbar.Brand>
41+
<Navbar
42+
expand="md"
43+
variant="light"
44+
bg="white"
45+
className="user-select-none shadow-sm px-0"
46+
>
47+
<div className='w-100 px-2 d-flex align-items-center gap-2'>
48+
<Navbar.Brand>Essentials Dev Tools</Navbar.Brand>
49+
<Dropdown>
50+
<Dropdown.Toggle
51+
variant="primary"
52+
id="dropdown-basic"
53+
>
54+
{params.appId}
55+
</Dropdown.Toggle>
56+
<Dropdown.Menu>
57+
{appIdOptions.map((id) => (
58+
<Dropdown.Item
59+
key={id}
60+
as={NavLink}
61+
to={`/${id}/console`}
62+
>
63+
{id}
64+
</Dropdown.Item>
65+
))}
66+
</Dropdown.Menu>
67+
</Dropdown>
1268
<Nav className="me-auto">
13-
<NavLink className={ location.pathname.includes("/home") ? 'text-secondary me-3' : 'me-3' } to="/home">Home</NavLink>
14-
<NavLink className={ location.pathname.includes("/console") ? 'text-secondary me-3' : 'me-3'} to="/console">Debug Console</NavLink>
15-
<NavLink className={ location.pathname.includes("/versions") ? 'text-secondary me-3' : 'me-3' } to="/versions">Versions</NavLink>
16-
<NavLink className={ location.pathname.includes("/config") ? 'text-secondary me-3' : 'me-3' } to="/config">Config File</NavLink>
17-
<NavLink className={ location.pathname.includes("/devices") ? 'text-secondary me-3' : 'me-3' } to="/devices">Devices</NavLink>
18-
<NavLink className={ location.pathname.includes("/types") ? 'text-secondary me-3' : 'me-3' } to="/types">Types</NavLink>
69+
<NavLink
70+
className={
71+
location.pathname.includes(`/${params.appId}/console`)
72+
? "text-secondary me-3"
73+
: "me-3"
74+
}
75+
to={`/${params.appId}/console`}
76+
>
77+
Debug Console
78+
</NavLink>
79+
<NavLink
80+
className={
81+
location.pathname.includes(`/${params.appId}/versions`)
82+
? "text-secondary me-3"
83+
: "me-3"
84+
}
85+
to={`/${params.appId}/versions`}
86+
>
87+
Versions
88+
</NavLink>
89+
<NavLink
90+
className={
91+
location.pathname.includes(`/${params.appId}/config`)
92+
? "text-secondary me-3"
93+
: "me-3"
94+
}
95+
to={`/${params.appId}/config`}
96+
>
97+
Config File
98+
</NavLink>
99+
<NavLink
100+
className={
101+
location.pathname.includes(`/${params.appId}/devices`)
102+
? "text-secondary me-3"
103+
: "me-3"
104+
}
105+
to={`/${params.appId}/devices`}
106+
>
107+
Devices
108+
</NavLink>
109+
<NavLink
110+
className={
111+
location.pathname.includes(`/${params.appId}/types`)
112+
? "text-secondary me-3"
113+
: "me-3"
114+
}
115+
to={`/${params.appId}/types`}
116+
>
117+
Types
118+
</NavLink>
19119
</Nav>
20-
<div className='d-flex align-items-center'>
21-
<IconDarkEllipse className={isConnected ? 'text-success' : 'text-danger'} />
22-
<span className='ms-2'>{isConnected ? 'Connected' : 'Disconnected'}</span>
120+
<div className="d-flex align-items-center">
121+
<IconDarkEllipse
122+
className={isConnected ? "text-success" : "text-danger"}
123+
/>
124+
<span className="ms-2">
125+
{isConnected ? "Connected" : "Disconnected"}
126+
</span>
23127
</div>
24-
</Container>
128+
</div>
25129
</Navbar>
26130
);
27131
};
28132

29133
export default TopNav;
30134

135+
type appIds =
136+
| "app01"
137+
| "app02"
138+
| "app03"
139+
| "app04"
140+
| "app05"
141+
| "app06"
142+
| "app07"
143+
| "app08"
144+
| "app09"
145+
| "app10";
146+
31147

src/features/Versions.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import { skipToken } from '@reduxjs/toolkit/query';
12
import { Col, Container, Row } from 'react-bootstrap';
3+
import useAppParams from '../shared/hooks/useAppParams';
24
import { Version, useGetVersionsQuery } from "../store/apiSlice";
35

46
const Versions = () => {
5-
const { data: versions } = useGetVersionsQuery();
7+
const { appId } = useAppParams();
8+
const { data: versions } = useGetVersionsQuery(appId ? { appId } : skipToken);
69

710
if (!versions) {
811
return <div>Loading...</div>;

src/index.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
import React from "react";
22
import ReactDOM from "react-dom/client";
33
import { Provider } from "react-redux";
4-
import { Navigate, RouterProvider, createBrowserRouter } from 'react-router-dom';
4+
import { BrowserRouter } from 'react-router-dom';
55
import App from "./App";
6-
import ErrorBox from './features/ErrorBox';
76
import reportWebVitals from "./reportWebVitals";
87
import { store } from "./store/store";
98
import "./styles.scss";
109

11-
const router = createBrowserRouter(
12-
[
13-
{ path: '/', element: <Navigate to="/home" replace /> },
14-
{ path: '*', Component: App, errorElement: <ErrorBox /> },
15-
],
16-
{
17-
basename: '/debug',
18-
}
19-
);
10+
// const router = createBrowserRouter(
11+
// [
12+
// { path: '/', element: <Navigate to="/app01/versions" replace /> },
13+
// { path: '*', Component: App, errorElement: <ErrorBox /> },
14+
// ],
15+
// {
16+
// basename: '/debug',
17+
// }
18+
// );
2019

2120
const root = ReactDOM.createRoot(
2221
document.getElementById("root") as HTMLElement
2322
);
2423
root.render(
2524
<React.StrictMode>
2625
<Provider store={store}>
27-
<RouterProvider router={router} />
28-
{/* <App /> */}
26+
<BrowserRouter basename='/debug'>
27+
<App />
28+
</BrowserRouter>
2929
</Provider>
3030
</React.StrictMode>
3131
);

src/shared/hooks/useAppParams.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { useParams } from 'react-router';
2+
3+
/** Wraps up useParams with a type */
4+
export default function useAppParams() {
5+
return useParams<AppParams>();
6+
}
7+
8+
type AppParams = {
9+
appId: string;
10+
};

0 commit comments

Comments
 (0)