"use client"; import { useEffect, useState } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { Loader2 } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { apiPost } from "@/lib/api/client"; const SESSIONS_QUERY_KEY = ["mission-control", "sessions"] as const; type PanelAction = "pause" | "resume" | "graceful-kill" | "force-kill"; type KillConfirmationState = "graceful" | "force" | null; interface PanelActionResult { nextStatus: string; } export interface PanelControlsProps { sessionId: string; // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents status: "active" | "paused" | "killed" | string; onStatusChange?: (newStatus: string) => void; } function getErrorMessage(error: unknown): string { if (error instanceof Error && error.message.trim().length > 0) { return error.message; } return "Failed to update agent session."; } export function PanelControls({ sessionId, status, onStatusChange, }: PanelControlsProps): React.JSX.Element { const queryClient = useQueryClient(); const [errorMessage, setErrorMessage] = useState(null); const [confirmingKill, setConfirmingKill] = useState(null); useEffect(() => { setErrorMessage(null); setConfirmingKill(null); }, [sessionId]); const controlMutation = useMutation({ mutationFn: async (action: PanelAction): Promise => { switch (action) { case "pause": await apiPost<{ message: string }>( `/api/mission-control/sessions/${encodeURIComponent(sessionId)}/pause` ); return { nextStatus: "paused" }; case "resume": await apiPost<{ message: string }>( `/api/mission-control/sessions/${encodeURIComponent(sessionId)}/resume` ); return { nextStatus: "active" }; case "graceful-kill": await apiPost<{ message: string }>( `/api/mission-control/sessions/${encodeURIComponent(sessionId)}/kill`, { force: false } ); return { nextStatus: "killed" }; case "force-kill": await apiPost<{ message: string }>( `/api/mission-control/sessions/${encodeURIComponent(sessionId)}/kill`, { force: true } ); return { nextStatus: "killed" }; } }, onSuccess: ({ nextStatus }): void => { setErrorMessage(null); setConfirmingKill(null); onStatusChange?.(nextStatus); void queryClient.invalidateQueries({ queryKey: SESSIONS_QUERY_KEY }); }, onError: (error: unknown): void => { setConfirmingKill(null); setErrorMessage(getErrorMessage(error)); }, }); const normalizedStatus = status.toLowerCase(); const isKilled = normalizedStatus === "killed"; const isBusy = controlMutation.isPending; const pendingAction = isBusy ? controlMutation.variables : undefined; const submitAction = (action: PanelAction): void => { setErrorMessage(null); controlMutation.mutate(action); }; const pauseDisabled = isBusy || normalizedStatus === "paused" || isKilled; const resumeDisabled = isBusy || normalizedStatus === "active" || isKilled; const gracefulKillDisabled = isBusy || isKilled; const forceKillDisabled = isBusy || isKilled; return (
{confirmingKill === "graceful" ? (

Gracefully stop this agent after it finishes the current step?

) : null}
{confirmingKill === "force" ? (

This will hard-kill the agent immediately.

) : null}
{errorMessage ? ( {errorMessage} ) : null}
); }