64 lines
2.0 KiB
TypeScript
64 lines
2.0 KiB
TypeScript
import { useEffect, useRef, useState } from 'react'
|
|
|
|
type LogRecord = {
|
|
ts?: string
|
|
level?: string
|
|
logger?: string
|
|
msg?: string
|
|
}
|
|
|
|
export function Logs() {
|
|
const [lines, setLines] = useState<LogRecord[]>([])
|
|
const bottomRef = useRef<HTMLDivElement>(null)
|
|
|
|
useEffect(() => {
|
|
const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
const ws = new WebSocket(`${proto}//${window.location.host}/ws/logs`)
|
|
ws.onmessage = (ev) => {
|
|
try {
|
|
const rec = JSON.parse(ev.data as string) as LogRecord
|
|
setLines((prev) => {
|
|
const next = [...prev, rec]
|
|
return next.length > 500 ? next.slice(-500) : next
|
|
})
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
return () => ws.close()
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
|
|
}, [lines.length])
|
|
|
|
return (
|
|
<div className="min-h-screen bg-slate-950 p-4 text-slate-100 md:p-8">
|
|
<div className="mx-auto max-w-5xl">
|
|
<h1 className="text-xl font-bold text-white">Logy EMS</h1>
|
|
<p className="mt-1 text-sm text-slate-500">Stream z backendu (WebSocket)</p>
|
|
<pre className="mt-6 max-h-[calc(100vh-8rem)] overflow-auto rounded-xl border border-slate-800 bg-slate-900/80 p-4 font-mono text-xs leading-relaxed">
|
|
{lines.map((r, i) => (
|
|
<div
|
|
key={`${i}-${r.ts}-${r.msg}`}
|
|
className={
|
|
r.level === 'ERROR'
|
|
? 'text-red-300'
|
|
: r.level === 'WARNING'
|
|
? 'text-amber-200'
|
|
: 'text-slate-300'
|
|
}
|
|
>
|
|
<span className="text-slate-600">{r.ts ?? '—'} </span>
|
|
<span className="text-slate-500">[{r.level ?? '?'}] </span>
|
|
<span className="text-slate-500">{r.logger ?? ''}: </span>
|
|
{r.msg ?? ''}
|
|
</div>
|
|
))}
|
|
<div ref={bottomRef} />
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|