import { useState } from "react"; import { Power } from "lucide-react"; import { useTranslation } from "@termix/plugin-sdk/frontend"; import { useTailscaleData, useTailscaleAction } from "./useTailscaleManager"; import { ManagerCardShell, ManagerSearch } from "@termix/plugin-sdk/ui"; interface TailscalePeer { hostname: string; tailscaleIPs: string[]; online: boolean; isExitNode: boolean; } interface TailscaleData { installed: boolean; running: boolean; tailscaleIPs: string[]; hostname: string | null; peers: TailscalePeer[]; exitNodeInUse: boolean; } export function TailscaleManagerCard({ hostId }: { hostId: number | null }) { const { t } = useTranslation(); const { data, loading, error, refresh } = useTailscaleData(hostId); const { busy, run } = useTailscaleAction(hostId); const [query, setQuery] = useState(""); const handleToggle = async () => { if (!data) return; const action = data.running ? "down" : "up"; await run(action, { loadingMsg: data.running ? t("manager.tsDisabling") : t("manager.tsEnabling"), successMsg: data.running ? t("manager.tsDisabled") : t("manager.tsEnabled"), onDone: refresh, }); }; const filteredPeers = (data?.peers ?? []).filter( (p) => !query || p.hostname.toLowerCase().includes(query.toLowerCase()) || p.tailscaleIPs.some((ip) => ip.includes(query)), ); const isNotInstalled = data && !data.installed; return ( } loading={loading} error={error} onRefresh={refresh} empty={!!isNotInstalled} emptyMessage={t("manager.tsNotInstalled")} > {data && (
{/* Status + toggle row */}
{data.running ? t("manager.tsRunning") : t("manager.tsStopped")} {data.hostname && ( {data.hostname} )} {data.tailscaleIPs.map((ip) => ( {ip} ))}
{/* Exit node badge */} {data.exitNodeInUse && (
{t("manager.tsExitNodeActive")}
)} {/* Peers */} {(data.peers?.length ?? 0) > 5 && ( )} {filteredPeers.map((peer) => (
{peer.hostname} {peer.tailscaleIPs.map((ip) => ( {ip} ))}
{peer.isExitNode && ( {t("manager.tsExitNode")} )}
))}
)}
); }