"use client" import { Card, CardContent } from "./ui/card" import { Badge } from "./ui/badge" import { Wifi, Zap } from "lucide-react" import { useState, useEffect } from "react" import { fetchApi } from "../lib/api-config" interface NetworkCardProps { interface_: { name: string type: string status: string speed: number duplex?: string mtu?: number mac_address: string | null addresses: Array<{ ip: string netmask: string }> bytes_sent?: number bytes_recv?: number bridge_physical_interface?: string bridge_bond_slaves?: string[] vmid?: number vm_name?: string vm_type?: string } timeframe: "hour" | "day" | "week" | "month" | "year" onClick?: () => void } const getInterfaceTypeBadge = (type: string) => { switch (type) { case "physical": return { color: "bg-blue-500/10 text-blue-500 border-blue-500/20", label: "Physical" } case "bridge": return { color: "bg-green-500/10 text-green-500 border-green-500/20", label: "Bridge" } case "bond": return { color: "bg-purple-500/10 text-purple-500 border-purple-500/20", label: "Bond" } case "vlan": return { color: "bg-cyan-500/10 text-cyan-500 border-cyan-500/20", label: "VLAN" } case "vm_lxc": return { color: "bg-orange-500/10 text-orange-500 border-orange-500/20", label: "Virtual" } case "virtual": return { color: "bg-orange-500/10 text-orange-500 border-orange-500/20", label: "Virtual" } default: return { color: "bg-gray-500/10 text-gray-500 border-gray-500/20", label: "Unknown" } } } const getVMTypeBadge = (vmType: string | undefined) => { if (vmType === "lxc") { return { color: "bg-cyan-500/10 text-cyan-500 border-cyan-500/20", label: "LXC" } } else if (vmType === "vm") { return { color: "bg-purple-500/10 text-purple-500 border-purple-500/20", label: "VM" } } return { color: "bg-gray-500/10 text-gray-500 border-gray-500/20", label: "Unknown" } } const formatBytes = (bytes: number | undefined): string => { if (!bytes || bytes === 0) return "0 B" const k = 1024 const sizes = ["B", "KB", "MB", "GB", "TB"] const i = Math.floor(Math.log(bytes) / Math.log(k)) return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}` } const formatSpeed = (speed: number): string => { if (speed === 0) return "N/A" if (speed >= 1000) return `${(speed / 1000).toFixed(1)} Gbps` return `${speed} Mbps` } const formatStorage = (bytes: number): string => { if (bytes === 0) return "0 B" const k = 1024 const sizes = ["B", "KB", "MB", "GB", "TB", "PB"] const i = Math.floor(Math.log(bytes) / Math.log(k)) const value = bytes / Math.pow(k, i) const decimals = value >= 10 ? 1 : 2 return `${value.toFixed(decimals)} ${sizes[i]}` } export function NetworkCard({ interface_, timeframe, onClick }: NetworkCardProps) { const typeBadge = getInterfaceTypeBadge(interface_.type) const vmTypeBadge = interface_.vm_type ? getVMTypeBadge(interface_.vm_type) : null const [trafficData, setTrafficData] = useState<{ received: number; sent: number }>({ received: 0, sent: 0, }) useEffect(() => { const fetchTrafficData = async () => { try { const data = await fetchApi(`/api/network/${interface_.name}/metrics?timeframe=${timeframe}`) if (data.data && data.data.length > 0) { const lastPoint = data.data[data.data.length - 1] const firstPoint = data.data[0] const receivedGB = Math.max(0, (lastPoint.netin || 0) - (firstPoint.netin || 0)) const sentGB = Math.max(0, (lastPoint.netout || 0) - (firstPoint.netout || 0)) setTrafficData({ received: receivedGB, sent: sentGB, }) } } catch (error) { console.error("[v0] Failed to fetch traffic data for card:", error) setTrafficData({ received: 0, sent: 0 }) } } if (interface_.status.toLowerCase() === "up" && interface_.vm_type !== "vm") { fetchTrafficData() const interval = setInterval(fetchTrafficData, 60000) return () => clearInterval(interval) } }, [interface_.name, interface_.status, interface_.vm_type, timeframe]) const getTimeframeLabel = () => { switch (timeframe) { case "hour": return "Last Hour" case "day": return "Last 24 Hours" case "week": return "Last 7 Days" case "month": return "Last 30 Days" case "year": return "Last Year" default: return "Last 24 Hours" } } return (
{/* First row: Icon, Name, Type Badge, Status */}
{interface_.name}
{vmTypeBadge ? ( {vmTypeBadge.label} ) : ( {typeBadge.label} )} {interface_.vm_name && (
→ {interface_.vm_name}
)} {interface_.type === "bridge" && interface_.bridge_physical_interface && (
→ {interface_.bridge_physical_interface}
)}
{interface_.status.toUpperCase()}
{/* Second row: Details - Responsive layout */}
{interface_.type === "vm_lxc" ? "VMID" : "IP Address"}
{interface_.type === "vm_lxc" ? (interface_.vmid ?? "N/A") : interface_.addresses.length > 0 ? interface_.addresses[0].ip : "N/A"}
Speed
{formatSpeed(interface_.speed)}
{getTimeframeLabel()}
{interface_.status.toLowerCase() === "up" && interface_.vm_type !== "vm" ? ( <> ↓ {formatStorage(trafficData.received * 1024 * 1024 * 1024)} {" / "} ↑ {formatStorage(trafficData.sent * 1024 * 1024 * 1024)} ) : ( <> ↓ {formatBytes(interface_.bytes_recv)} {" / "} ↑ {formatBytes(interface_.bytes_sent)} )}
{interface_.mac_address && (
MAC
{interface_.mac_address}
)}
) }