diff --git a/.gitignore b/.gitignore
index 9202ae2..a703f29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
/node_modules/
/tse_events.*
-/.env
\ No newline at end of file
+/.env
+monitor.db*
\ No newline at end of file
diff --git a/dashboard/src/App.jsx b/dashboard/src/App.jsx
index 65ba4c0..84b9110 100644
--- a/dashboard/src/App.jsx
+++ b/dashboard/src/App.jsx
@@ -2,8 +2,7 @@ import { useState, useEffect, useCallback } from 'react';
import {
ThemeProvider, CssBaseline, Box, Container, Grid,
Typography, ToggleButton, ToggleButtonGroup,
- Chip, CircularProgress, Alert, IconButton, Tooltip,
- Select, MenuItem, FormControl
+ Chip, CircularProgress, Alert, IconButton, Tooltip
} from '@mui/material';
import RefreshIcon from '@mui/icons-material/Refresh';
import { theme } from './theme.js';
@@ -16,34 +15,23 @@ import { fetchSummary, fetchHourly, fetchOutages, fetchLive, fetchEndpoints } fr
const RANGE_DAYS = { '24h': 1, '7d': 7, '30d': 30 };
const REFRESH_MS = 60_000;
-export default function App() {
- const [endpoints, setEndpoints] = useState([]);
- const [selectedEndpoint, setSelectedEndpoint] = useState('');
+function EndpointDashboard({ endpoint, range }) {
const [summary, setSummary] = useState(null);
const [hourly, setHourly] = useState([]);
const [outages, setOutages] = useState([]);
const [live, setLive] = useState(null);
- const [range, setRange] = useState('7d');
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [lastRefresh, setLastRefresh] = useState(null);
- useEffect(() => {
- fetchEndpoints().then(eps => {
- setEndpoints(eps);
- if (eps.length > 0) setSelectedEndpoint(eps[0].id);
- }).catch(e => setError(e.message));
- }, []);
-
const refresh = useCallback(async () => {
- if (!selectedEndpoint) return;
const days = RANGE_DAYS[range] ?? 7;
try {
const [s, h, o, l] = await Promise.all([
- fetchSummary(selectedEndpoint),
- fetchHourly(selectedEndpoint, days),
- fetchOutages(selectedEndpoint, 50),
- fetchLive(selectedEndpoint),
+ fetchSummary(endpoint.id),
+ fetchHourly(endpoint.id, days),
+ fetchOutages(endpoint.id, 50),
+ fetchLive(endpoint.id),
]);
setSummary(s); setHourly(h); setOutages(o); setLive(l);
setError(null);
@@ -53,7 +41,7 @@ export default function App() {
} finally {
setLoading(false);
}
- }, [range, selectedEndpoint]);
+ }, [range, endpoint.id]);
useEffect(() => {
setLoading(true);
@@ -64,6 +52,76 @@ export default function App() {
const isOnline = live && (Date.now() - new Date(live.ts).getTime()) < 180_000;
+ return (
+
+
+
+ {endpoint.id}
+
+ ({endpoint.url})
+
+
+
+ {live && (
+
+ )}
+
+
+
+ {lastRefresh && (
+
+ updated {lastRefresh.toLocaleTimeString()}
+
+ )}
+
+
+
+
+
+
+
+ {error && API error: {error}}
+
+ {loading && !summary ? (
+
+
+
+ ) : (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ );
+}
+
+export default function App() {
+ const [endpoints, setEndpoints] = useState([]);
+ const [range, setRange] = useState('7d');
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ fetchEndpoints()
+ .then(setEndpoints)
+ .catch(e => setError(e.message));
+ }, []);
+
return (
@@ -78,94 +136,34 @@ export default function App() {
}}>
-
- ⚡ TSE Monitor
+
+ ⚡ Monitor
- {endpoints.length > 0 && (
-
-
-
- )}
-
-
-
- {live && (
- <>
-
- >
- )}
-
- {lastRefresh && (
-
- updated {lastRefresh.toLocaleTimeString()}
-
- )}
-
-
-
-
-
-
+ v && setRange(v)}
+ >
+ {['24h','7d','30d'].map(v => (
+ {v}
+ ))}
+
- {error && API error: {error}}
-
- {loading && !summary ? (
+ {error && Failed to load endpoints: {error}}
+
+ {!error && endpoints.length === 0 && (
- ) : (
-
- {/* Summary cards */}
-
-
-
-
- {/* Time-range toggle */}
-
- v && setRange(v)}
- >
- {['24h','7d','30d'].map(v => (
- {v}
- ))}
-
-
-
- {/* Availability chart */}
-
-
-
-
- {/* Response-time chart */}
-
-
-
-
- {/* Outage table */}
-
-
-
-
)}
+
+ {endpoints.map(ep => (
+
+ ))}