import { getErrorMessage } from "../error-message"; import { Button } from "@termix/plugin-sdk/ui"; import type { DockerContainer } from "../types"; import React from "react"; import { Box, List, Play, RefreshCw, Square, Terminal } from "lucide-react"; import { toast } from "sonner"; import { useTranslation } from "@termix/plugin-sdk/frontend"; import { useDockerApi } from "../docker-api"; import { DockerBadge } from "./ContainerCard.tsx"; type DetailTab = "logs" | "stats" | "console"; interface ContainerTableProps { containers: DockerContainer[]; sessionId: string; onSelectContainer: (containerId: string, tab?: DetailTab) => void; selectedContainerId?: string | null; onRefresh?: () => void; search?: string; statusFilter?: string; } export function ContainerTable({ containers, sessionId, onSelectContainer, selectedContainerId = null, onRefresh, search = "", statusFilter = "all", }: ContainerTableProps): React.ReactElement { const { t } = useTranslation(); const docker = useDockerApi(); const [pendingId, setPendingId] = React.useState(null); const filtered = React.useMemo(() => { return containers.filter((c) => { const name = c.name.startsWith("/") ? c.name.slice(1) : c.name; const matchesSearch = name.toLowerCase().includes(search.toLowerCase()) || c.image.toLowerCase().includes(search.toLowerCase()) || c.id.toLowerCase().includes(search.toLowerCase()); return ( matchesSearch && (statusFilter === "all" || c.state === statusFilter) ); }); }, [containers, search, statusFilter]); const handleToggleRun = async ( e: React.MouseEvent, container: DockerContainer, ) => { e.stopPropagation(); const containerName = container.name.startsWith("/") ? container.name.slice(1) : container.name; setPendingId(container.id); try { if (container.state === "running") { await docker.containerAction(sessionId, container.id, "stop"); toast.success(t("docker.containerStopped", { name: containerName })); } else { await docker.containerAction(sessionId, container.id, "start"); toast.success(t("docker.containerStarted", { name: containerName })); } onRefresh?.(); } catch (err) { toast.error( container.state === "running" ? t("docker.failedToStopContainer", { error: getErrorMessage(err) }) : t("docker.failedToStartContainer", { error: getErrorMessage(err), }), ); } finally { setPendingId(null); } }; if (containers.length === 0) { return (
{t("docker.noContainersFound")} {t("docker.noContainersFoundHint")}
); } if (filtered.length === 0) { return (
{t("docker.noContainersMatchFilters")} {t("docker.noContainersMatchFiltersHint")}
); } return (
{filtered.map((container) => { const containerName = container.name.startsWith("/") ? container.name.slice(1) : container.name; const portsList = (container.ports ?? "") .split(",") .map((p) => p.trim()) .filter(Boolean); const isSelected = selectedContainerId === container.id; const isPending = pendingId === container.id; return ( onSelectContainer(container.id, "logs")} className={`cursor-pointer transition-colors hover:bg-muted/40 ${ isSelected ? "bg-accent-brand/5" : "" }`} > ); })}
{t("docker.name")} {t("docker.image")} {t("docker.state")} {t("docker.ports")} {t("docker.status")} {t("docker.actions")}
{containerName}
{container.image} {portsList.length > 0 ? (
{portsList.slice(0, 2).map((p) => ( {p} ))} {portsList.length > 2 && ( +{portsList.length - 2} )}
) : ( {t("docker.noPorts")} )}
{container.status}
e.stopPropagation()} >
); }