import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Box, Search, Server as ServerIcon } from "lucide-react"; import { cn } from "@/lib/utils"; import { Input } from "@/components/input"; import type { ProxmoxGuestSummary } from "@/types/proxmox"; type StatusFilter = "all" | "running" | "stopped"; type TypeFilter = "all" | "qemu" | "lxc"; function formatUptime(seconds: number | null): string { if (seconds === null || seconds <= 0) return "-"; const days = Math.floor(seconds / 86400); const hours = Math.floor((seconds % 86400) / 3600); const minutes = Math.floor((seconds % 3600) / 60); if (days > 0) return `${days}d ${hours}h`; if (hours > 0) return `${hours}h ${minutes}m`; return `${minutes}m`; } function UsageCell({ percent, usedGiB, totalGiB, }: { percent: number | null; usedGiB?: number | null; totalGiB?: number | null; }) { if (percent === null) { return -; } const clamped = Math.min(100, Math.max(0, percent)); const barColor = clamped >= 90 ? "bg-red-500" : clamped >= 75 ? "bg-yellow-500" : "bg-accent-brand"; return (
{clamped.toFixed(0)}% {usedGiB != null && totalGiB != null && ( {usedGiB.toFixed(1)}/{totalGiB.toFixed(1)}G )}
); } export function GuestTable({ guests }: { guests: ProxmoxGuestSummary[] }) { const { t } = useTranslation(); const [query, setQuery] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); const [typeFilter, setTypeFilter] = useState("all"); const filtered = useMemo(() => { const q = query.trim().toLowerCase(); return guests .filter( (g) => statusFilter === "all" || (statusFilter === "running") === (g.status === "running"), ) .filter((g) => typeFilter === "all" || g.type === typeFilter) .filter( (g) => !q || g.name.toLowerCase().includes(q) || String(g.vmid).includes(q), ) .sort((a, b) => { if (a.status === b.status) return a.name.localeCompare(b.name); return a.status === "running" ? -1 : 1; }); }, [guests, query, statusFilter, typeFilter]); const running = guests.filter((g) => g.status === "running").length; return (
{t("proxmoxStats.guestsSummary")} {t("proxmoxStats.guestCounts", { running, total: guests.length })}
setQuery(e.target.value)} placeholder={t("proxmoxStats.searchGuests")} className="h-7 w-40 pl-6 text-xs" />
{(["all", "qemu", "lxc"] as TypeFilter[]).map((f) => ( ))}
{(["all", "running", "stopped"] as StatusFilter[]).map((f) => ( ))}
{guests.length === 0 ? (
{t("proxmoxStats.noGuests")}
) : filtered.length === 0 ? (
{t("proxmoxStats.noGuestsMatch")}
) : ( {filtered.map((guest) => ( ))}
{t("proxmoxStats.name")} {t("proxmoxStats.status")} ID {t("proxmoxStats.cpu")} {t("proxmoxStats.mem")} {t("proxmoxStats.disk")} {t("proxmoxStats.uptime")}
{guest.type === "lxc" ? ( ) : ( )} {guest.name} {guest.type === "lxc" ? "LXC" : "VM"}
{guest.status === "running" ? t("proxmoxStats.running") : t("proxmoxStats.stopped")}
{guest.vmid} {formatUptime(guest.uptimeSeconds)}
)}
); }