import { useSessions, useCancelQuery, useTerminateBackend, } from "@/hooks/use-management"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { toast } from "sonner"; import { Loader2, XCircle, Skull, RefreshCw } from "lucide-react"; import { useQueryClient } from "@tanstack/react-query"; interface Props { connectionId: string; } function getStateBadge(state: string | null) { if (!state) return null; const colors: Record = { idle: "bg-green-500/15 text-green-600", active: "bg-yellow-500/15 text-yellow-600", "idle in transaction": "bg-orange-500/15 text-orange-600", disabled: "bg-red-500/15 text-red-600", }; return ( {state} ); } function formatDuration(queryStart: string | null): string { if (!queryStart) return "-"; const start = new Date(queryStart).getTime(); const now = Date.now(); const diffSec = Math.floor((now - start) / 1000); if (diffSec < 0) return "-"; if (diffSec < 60) return `${diffSec}s`; if (diffSec < 3600) return `${Math.floor(diffSec / 60)}m ${diffSec % 60}s`; return `${Math.floor(diffSec / 3600)}h ${Math.floor((diffSec % 3600) / 60)}m`; } function getDurationColor(queryStart: string | null, state: string | null): string { if (state !== "active" || !queryStart) return ""; const diffSec = (Date.now() - new Date(queryStart).getTime()) / 1000; if (diffSec > 30) return "text-red-500 font-semibold"; if (diffSec > 5) return "text-yellow-500 font-semibold"; return ""; } export function SessionsView({ connectionId }: Props) { const { data: sessions, isLoading } = useSessions(connectionId); const cancelMutation = useCancelQuery(); const terminateMutation = useTerminateBackend(); const queryClient = useQueryClient(); const handleCancel = (pid: number) => { cancelMutation.mutate( { connectionId, pid }, { onSuccess: () => toast.success(`Cancel signal sent to PID ${pid}`), onError: (err) => toast.error("Cancel failed", { description: String(err) }), } ); }; const handleTerminate = (pid: number) => { if (!confirm(`Terminate backend PID ${pid}? This will kill the session.`)) return; terminateMutation.mutate( { connectionId, pid }, { onSuccess: () => toast.success(`Terminate signal sent to PID ${pid}`), onError: (err) => toast.error("Terminate failed", { description: String(err) }), } ); }; if (isLoading) { return (
Loading sessions...
); } return (
Active Sessions {sessions?.length ?? 0} Auto-refresh: 5s
{sessions?.map((s) => ( ))}
PID User Database State Duration Wait Query Client Actions
{s.pid} {s.usename ?? "-"} {s.datname ?? "-"} {getStateBadge(s.state)} {formatDuration(s.query_start)} {s.wait_event ? `${s.wait_event_type}: ${s.wait_event}` : "-"} {s.query ? (s.query.length > 60 ? s.query.slice(0, 60) + "..." : s.query) : "-"} {s.client_addr ?? "-"}
{(!sessions || sessions.length === 0) && (
No active sessions
)}
); }