mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-07 08:27:12 +00:00
feat(web): live-update runs board via SSE
Subscribe to the global event stream (GET /api/v1/attach) on the runs board page. When a status-changing event arrives (run.submitted, run.starting, run.running, run.paused, run.completed, run.failed), debounce 500ms then revalidate the loader to refresh the board. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
84f3c80566
commit
3e8a1f7cdc
1 changed files with 34 additions and 2 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useCallback, useRef } from "react";
|
||||
import { Link } from "react-router";
|
||||
import { useState, useCallback, useEffect, useRef } from "react";
|
||||
import { Link, useRevalidator } from "react-router";
|
||||
import { ChevronDownIcon, ChevronRightIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||
import {
|
||||
DndContext,
|
||||
|
|
@ -523,6 +523,38 @@ export default function Runs({ loaderData }: any) {
|
|||
const [collapsed, setCollapsed] = useState<Set<string>>(new Set());
|
||||
const [columns, setColumns] = useState(initialColumns);
|
||||
const lowerQuery = query.toLowerCase();
|
||||
const revalidator = useRevalidator();
|
||||
|
||||
const STATUS_EVENTS = new Set([
|
||||
"run.submitted", "run.starting", "run.running",
|
||||
"run.paused", "run.completed", "run.failed",
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const source = new EventSource("/api/v1/attach");
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
source.onmessage = (msg) => {
|
||||
try {
|
||||
const payload = JSON.parse(msg.data);
|
||||
if (STATUS_EVENTS.has(payload.event)) {
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(() => revalidator.revalidate(), 500);
|
||||
}
|
||||
} catch {
|
||||
// ignore malformed events
|
||||
}
|
||||
};
|
||||
|
||||
return () => {
|
||||
clearTimeout(debounceTimer);
|
||||
source.close();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setColumns(initialColumns);
|
||||
}, [initialColumns]);
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue