live-tail-toggle.tsx49 lines · main
1'use client';
2
3import { useEffect, useState } from 'react';
4import { useRouter } from 'next/navigation';
5
6/**
7 * Toggleable poll-driven auto-refresh for the logs page. When on, we
8 * call router.refresh() every 5s so the server-rendered list re-fetches
9 * with the same filters. Off by default — polling wastes a request per
10 * tick and most users don't need it.
11 *
12 * Polling beats SSE here because the logs page is server-rendered and
13 * already does the right query with cookies + filters; piggybacking on
14 * router.refresh() keeps the surface area tiny.
15 */
16const INTERVAL_MS = 5000;
17
18export function LiveTailToggle() {
19 const router = useRouter();
20 const [on, setOn] = useState(false);
21 const [tickAt, setTickAt] = useState<number | null>(null);
22
23 useEffect(() => {
24 if (!on) return;
25 const id = setInterval(() => {
26 router.refresh();
27 setTickAt(Date.now());
28 }, INTERVAL_MS);
29 return () => clearInterval(id);
30 }, [on, router]);
31
32 return (
33 <label className="flex items-center gap-2 font-mono text-[10px] text-[var(--color-text-muted)]">
34 <input
35 type="checkbox"
36 checked={on}
37 onChange={(e) => setOn(e.target.checked)}
38 className="accent-[var(--color-primary)]"
39 />
40 live tail
41 {on ? (
42 <span className="text-[var(--color-text-subtle)]">
43 · {Math.round(INTERVAL_MS / 1000)}s
44 {tickAt ? ` · last ${new Date(tickAt).toISOString().slice(11, 19)}` : ''}
45 </span>
46 ) : null}
47 </label>
48 );
49}