import { useMemo, useState } from "react"; import { Unplug, Search } from "lucide-react"; import { useTranslation } from "react-i18next"; import type { ServerMetrics, ListeningPort } from "@/main-axios"; import { MetricCard } from "@/components/metric-card"; import { Select2 } from "@/components/select2"; function formatAddress(addr: string) { return addr === "0.0.0.0" || addr === "*" || addr === "::" ? "*" : addr; } function PortRow({ port }: { port: ListeningPort }) { return (
{port.localPort} {port.protocol.toUpperCase()} {port.process ?? (port.pid ? `PID:${port.pid}` : "—")} {formatAddress(port.localAddress)}
); } export function PortsCard({ metrics }: { metrics: ServerMetrics | null }) { const { t } = useTranslation(); const [query, setQuery] = useState(""); const [proto, setProto] = useState<"all" | "tcp" | "udp">("all"); const ports = useMemo(() => metrics?.ports?.ports ?? [], [metrics]); const filtered = useMemo(() => { const q = query.toLowerCase(); return ports.filter((p) => { if (proto !== "all" && p.protocol.toLowerCase() !== proto) return false; if (!q) return true; return ( String(p.localPort).includes(q) || (p.process ?? "").toLowerCase().includes(q) || String(p.pid ?? "").includes(q) || p.localAddress.toLowerCase().includes(q) ); }); }, [ports, query, proto]); return ( } scroll >
setQuery(e.target.value)} placeholder={t("hostMetrics.ports.search")} className="h-7 w-full border border-border bg-background pl-7 pr-2 text-xs outline-none focus:ring-1 focus:ring-ring" />
setProto(e.target.value as "all" | "tcp" | "udp")} className="h-7 border border-border bg-background px-1 text-xs" > {filtered.length}
{t("hostMetrics.ports.port")} {t("hostMetrics.ports.protocol")} {t("hostMetrics.ports.process")} {t("hostMetrics.ports.address")}
{filtered.length === 0 ? ( {t("hostMetrics.ports.noData")} ) : ( filtered.map((port, i) => ( )) )}
); }