This commit is contained in:
Hugh Kaznowski
2026-09-05 12:23:46 +01:00
parent 818f8d09ea
commit 68548911bc
34 changed files with 3262 additions and 4 deletions
+60
View File
@@ -0,0 +1,60 @@
import { useEffect, useState } from 'react'
import { NavMenu } from './components/NavMenu'
import { FilterBar } from './components/FilterBar'
import { LogTimeline, type TimeRange } from './components/LogTimeline'
import { LogPreview } from './components/LogPreview'
import { ComingSoon } from './components/ComingSoon'
import { fetchLogs, fetchStats, fetchTimeline, type LogEntry, type Stats, type TimelineBucket } from './lib/api'
const POLL_MS = 2000
export default function App() {
const [stats, setStats] = useState<Stats | null>(null)
const [logs, setLogs] = useState<LogEntry[]>([])
const [buckets, setBuckets] = useState<TimelineBucket[]>([])
const [range, setRange] = useState<TimeRange | null>(null)
const [filter, setFilter] = useState('')
useEffect(() => {
let cancelled = false
async function poll() {
try {
const [nextStats, nextBuckets, nextLogs] = await Promise.all([
fetchStats(),
fetchTimeline({ startMs: range?.startMs, endMs: range?.endMs }),
fetchLogs({ startMs: range?.startMs, endMs: range?.endMs, limit: 1000 }),
])
if (!cancelled) {
setStats(nextStats)
setBuckets(nextBuckets)
setLogs(nextLogs)
}
} catch {
// engine not reachable yet; retry on next tick
}
}
poll()
const id = setInterval(poll, POLL_MS)
return () => {
cancelled = true
clearInterval(id)
}
}, [range])
const visibleLogs = filter ? logs.filter((l) => l.text.includes(filter)) : logs
return (
<div className="flex h-screen w-screen overflow-hidden">
<NavMenu stats={stats} />
<div className="flex min-w-0 flex-1 flex-col">
<FilterBar value={filter} onChange={setFilter} />
<LogTimeline buckets={buckets} range={range} onSelectRange={setRange} />
<LogPreview logs={visibleLogs} />
<ComingSoon title="Metrics" />
<ComingSoon title="Dashboards" />
</div>
</div>
)
}