import React, { useCallback, useEffect, useState } from "react"; import { stopInstance, deleteInstance, checkInstanceHealth, recreateInstanceTunnel, } from "@/api/sessions"; import type { Session } from "@/types/session"; import { SessionCard } from "./SessionCard"; interface SessionListProps { sessions: Session[]; variant: "active" | "recent"; onSessionChange?: () => void; onOpen?: (session: Session) => void; } export const SessionList: React.FC = ({ sessions, variant, onSessionChange, onOpen, }) => { const [deleteConfirmId, setDeleteConfirmId] = useState(null); const [stopConfirmId, setStopConfirmId] = useState(null); const [tunnelHealth, setTunnelHealth] = useState< Record< string, { healthy: boolean; status_code: number | null; error?: string } > >({}); const [recreatingId, setRecreatingId] = useState(null); // Poll tunnel health every 30 seconds for running instances (active only) useEffect(() => { if (variant !== "active") return; const checkHealth = async () => { const runningSessions = sessions.filter( (s) => s.status === "running" && s.url, ); for (const session of runningSessions) { try { const health = await checkInstanceHealth( session.project_id, session.repository_id, session.id, ); setTunnelHealth((prev) => ({ ...prev, [session.id]: health })); } catch { setTunnelHealth((prev) => ({ ...prev, [session.id]: { healthy: false, status_code: null, error: "check failed", }, })); } } }; void checkHealth(); const interval = setInterval(() => void checkHealth(), 30000); return () => clearInterval(interval); }, [sessions, variant]); const handleStop = useCallback( async (session: Session) => { try { await stopInstance( session.project_id, session.repository_id, session.id, ); setStopConfirmId(null); onSessionChange?.(); } catch { setStopConfirmId(null); } }, [onSessionChange], ); const handleDelete = useCallback( async (session: Session) => { try { await deleteInstance( session.project_id, session.repository_id, session.id, ); setDeleteConfirmId(null); onSessionChange?.(); } catch { setDeleteConfirmId(null); } }, [onSessionChange], ); const handleRecreateTunnel = useCallback( async (session: Session) => { setRecreatingId(session.id); try { await recreateInstanceTunnel( session.project_id, session.repository_id, session.id, ); onSessionChange?.(); } catch { // ignore } finally { setRecreatingId(null); } }, [onSessionChange], ); const handleOpen = useCallback( (session: Session) => { if (session.url) { window.open(session.url, "_blank", "noopener,noreferrer"); } else { onOpen?.(session); } }, [onOpen], ); if (sessions.length === 0) { return (

{variant === "active" ? "No active sessions" : "No recent sessions"}

); } return ( <> {variant === "active" ? (
{sessions.map((session) => ( handleOpen(session)} onStop={() => stopConfirmId === session.id ? handleStop(session) : setStopConfirmId(session.id) } onDelete={() => deleteConfirmId === session.id ? handleDelete(session) : setDeleteConfirmId(session.id) } onRecreateTunnel={() => handleRecreateTunnel(session)} onCancelStop={() => setStopConfirmId(null)} onCancelDelete={() => setDeleteConfirmId(null)} /> ))}
) : (
{sessions.map((session) => ( handleOpen(session)} onStop={() => {}} onDelete={() => deleteConfirmId === session.id ? handleDelete(session) : setDeleteConfirmId(session.id) } onRecreateTunnel={() => {}} onCancelStop={() => {}} onCancelDelete={() => setDeleteConfirmId(null)} /> ))}
)} ); };