feat(operations-ui): BURST toggle + deadline dialog for the halacha drain (#133)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 7s

Implements the chair-approved Claude Design mockup (02b-operations-burst): a single
gold " הפעל BURST" button on the legal-halacha-drain row that opens a deadline
dialog (datetime-local, default the upcoming Saturday 18:00, quick chips for Sat
18:00 / +5h / midnight). While a burst is active the row shows a gold pill with the
deadline + an "עצור BURST" stop button. Manual only.

- operations.ts: burst_until on OpsService + useDrainBurst hook (POST .../burst).
- page.tsx: BurstControl component, gated to legal-halacha-drain.
- types.ts: regenerated (npm run api:types) — the new burst route.

Active-state is derived from burst_until presence (the supervisor NULLs it at the
deadline, snapshot refetches every 5s) — no impure Date.now() in render.

Invariants: G1/G2 — single DB-backed control (drain_controls.burst_until), shared
with the host supervisor; no parallel path. UI passed the Claude Design gate.
This commit is contained in:
2026-06-12 11:46:41 +00:00
parent 75a1b23972
commit 9154a1d817
3 changed files with 223 additions and 0 deletions

View File

@@ -12,6 +12,7 @@ export type OpsService = {
cron: string;
autorestart: boolean;
disabled?: boolean; // cron drain switched off via the dashboard
burst_until?: string | null; // manual "run continuously now" window (ISO) — see useDrainBurst
};
export type CourtFetchJob = {
@@ -104,6 +105,28 @@ export function useDrainToggle() {
});
}
/**
* Start/stop a drain's MANUAL burst window ("run continuously now until X").
* `until` is a tz-naive local datetime string (e.g. "2026-06-13T18:00") — the
* backend interprets it as Israel time. Omit it to let the server default to the
* upcoming Saturday 18:00. The host supervisor enforces it within ≤15 min.
*/
export function useDrainBurst() {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ name, action, until }: { name: string; action: "on" | "off"; until?: string }) =>
apiRequest(`/api/operations/drains/${name}/burst`, {
method: "POST",
body: action === "on" ? { action, ...(until ? { until } : {}) } : { action },
}),
onSuccess: (_d, { action }) => {
toast.success(action === "on" ? "BURST הופעל" : "BURST כובה");
qc.invalidateQueries({ queryKey: ["operations"] });
},
onError: (e) => toast.error(`הפעולה נכשלה: ${String(e)}`),
});
}
// ── Live agents — which agent is working now + its output + controls ───────
export type AgentRun = {