Christian Mueller a50ef96eb7 feat(frontend): Task 15 - all React pages and components
Implements the full SPA UI:
- App.tsx with BrowserRouter, ProtectedRoute, and all page routes
- Sidebar with NavLink active state and logout
- Login page with JWT auth flow
- Dashboard with parallel data fetching and status tiles
- MqttBrokers: CRUD with test-connection button and Topics link
- MqttTopics: discovery, filter, json_path inline edit, ignore
- PgSources: CRUD form with all required fields
- Datapoints: table with inline label/unit editing via onBlur
- Displays: layout dropdown, refresh interval, last-seen timestamp
- Layouts: left panel list + right panel element editor
- EpaperPreview: scaled e-paper simulation with live data support
- StatusBadge: color-coded status chip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 13:06:40 +02:00

42 lines
1.7 KiB
TypeScript

import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { isLoggedIn } from './api/client';
import Sidebar from './components/Sidebar';
import Login from './pages/Login';
import Dashboard from './pages/Dashboard';
import MqttBrokers from './pages/MqttBrokers';
import MqttTopics from './pages/MqttTopics';
import PgSources from './pages/PgSources';
import Datapoints from './pages/Datapoints';
import Displays from './pages/Displays';
import Layouts from './pages/Layouts';
function ProtectedRoute({ children }: { children: React.ReactNode }) {
if (!isLoggedIn()) return <Navigate to="/login" replace />;
return (
<div className="flex min-h-screen bg-gray-100">
<Sidebar />
<main className="ml-56 flex-1 p-6 overflow-auto">
{children}
</main>
</div>
);
}
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
<Route path="/mqtt/brokers" element={<ProtectedRoute><MqttBrokers /></ProtectedRoute>} />
<Route path="/mqtt/topics/:brokerId" element={<ProtectedRoute><MqttTopics /></ProtectedRoute>} />
<Route path="/pg/sources" element={<ProtectedRoute><PgSources /></ProtectedRoute>} />
<Route path="/datapoints" element={<ProtectedRoute><Datapoints /></ProtectedRoute>} />
<Route path="/displays" element={<ProtectedRoute><Displays /></ProtectedRoute>} />
<Route path="/layouts" element={<ProtectedRoute><Layouts /></ProtectedRoute>} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</BrowserRouter>
);
}