import { useState } from "react"; import type { Session } from "../api/sessions"; import { Icon } from "./icon"; export interface SessionCardProps { session: Session; onOpen?: (session: Session) => void; onStart?: (session: Session) => void; onStop?: (session: Session) => void; onDelete?: (session: Session) => void; onRecreateTunnel?: (session: Session) => void; isBusy?: boolean; tunnelHealth?: { healthy: boolean; container_status: string; container_health: string | null; tunnel_status: string; tunnel_status_code: number | null; probe_status: string; last_probe_output: string | null; error: string | null; } | null; } const statusConfig: Record = { running: { color: "green", label: "Running" }, building: { color: "yellow", label: "Building" }, starting: { color: "yellow", label: "Starting" }, probing: { color: "yellow", label: "Probing" }, pending: { color: "yellow", label: "Pending" }, stopped: { color: "gray", label: "Stopped" }, error: { color: "red", label: "Error" }, unhealthy: { color: "orange", label: "Unhealthy" }, }; export function SessionCard({ session, onOpen, onStart, onStop, onDelete, onRecreateTunnel, isBusy = false, tunnelHealth = null, }: SessionCardProps) { const [showStopConfirm, setShowStopConfirm] = useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const status = statusConfig[session.status] || { color: "gray", label: session.status }; const isTerminalOnly = session.tool_type_interfaces?.includes("terminal") && !session.tool_type_interfaces?.includes("web"); const hasTunnelError = !isTerminalOnly && tunnelHealth?.tunnel_status === "unreachable"; const hasAppError = !isTerminalOnly && tunnelHealth?.tunnel_status === "error_response"; const handleStop = () => { if (showStopConfirm) { setShowStopConfirm(false); onStop?.(session); } else { setShowStopConfirm(true); } }; const handleDelete = () => { if (showDeleteConfirm) { setShowDeleteConfirm(false); onDelete?.(session); } else { setShowDeleteConfirm(true); } }; const handleCancelStop = () => setShowStopConfirm(false); const handleCancelDelete = () => setShowDeleteConfirm(false); const isActive = ["running", "building", "starting", "probing", "pending", "unhealthy"].includes(session.status); return (

{session.display_name}

{status.label} {hasTunnelError && ( Tunnel Error )} {hasAppError && ( App Error {tunnelHealth?.tunnel_status_code} )}

{session.tool_type_name} {session.project_name && ` · ${session.project_name}`} {session.repository_name && ` · ${session.repository_name}`}

{session.clone_mode && (

{session.clone_mode === "clone" ? `Clone${session.branch ? ` (${session.branch})` : ""}` : "Mount"}

)} {session.url && (

{session.url}

)} {session.created_at && (

Created: {new Date(session.created_at).toLocaleDateString()}

)}
{isActive && ( <> {session.url ? ( Open ) : ( )} {hasTunnelError && onRecreateTunnel && ( )} {showStopConfirm ? (
Stop?
) : ( )} )} {!isActive && onStart && ( )} {showDeleteConfirm ? (
Delete?
) : ( )}
); }