diff --git a/AppImage/components/interface-details-modal.tsx b/AppImage/components/interface-details-modal.tsx new file mode 100644 index 0000000..206d342 --- /dev/null +++ b/AppImage/components/interface-details-modal.tsx @@ -0,0 +1,54 @@ +import { getUnitsSettings, formatNetworkTraffic, getNetworkLabel } from "@/lib/network-utils" + + +export function InterfaceDetailsModal({ interface_, onClose, timeframe }: InterfaceDetailsModalProps) { + const [metricsData, setMetricsData] = useState([]) + const [loading, setLoading] = useState(false) + const [networkUnit, setNetworkUnit] = useState<"Bytes" | "Bits">("Bytes") + + useEffect(() => { + const settings = getUnitsSettings() + setNetworkUnit(settings.networkUnit as "Bytes" | "Bits") + + const handleStorageChange = () => { + const settings = getUnitsSettings() + setNetworkUnit(settings.networkUnit as "Bytes" | "Bits") + } + + window.addEventListener('storage', handleStorageChange) + return () => window.removeEventListener('storage', handleStorageChange) + }, []) + + + const totalReceived = metricsData.length > 0 + ? Math.max(0, (metricsData[metricsData.length - 1].netin || 0) - (metricsData[0].netin || 0)) + : 0 + + const totalSent = metricsData.length > 0 + ? Math.max(0, (metricsData[metricsData.length - 1].netout || 0) - (metricsData[0].netout || 0)) + : 0 + + return ( + + + +
+

+ Network Traffic Statistics (Last 24 Hours) +

+
+
+

{getNetworkLabel(networkUnit, "received")}

+

{formatNetworkTraffic(totalReceived, networkUnit)}

+
+
+

{getNetworkLabel(networkUnit, "sent")}

+

{formatNetworkTraffic(totalSent, networkUnit)}

+
+
+ +
+
+
+ ) +} diff --git a/AppImage/components/metrics-dialog.tsx b/AppImage/components/metrics-dialog.tsx index ce201a6..90e7395 100644 --- a/AppImage/components/metrics-dialog.tsx +++ b/AppImage/components/metrics-dialog.tsx @@ -3,9 +3,10 @@ import { useState, useEffect } from "react" import { Button } from "@/components/ui/button" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" -import { ArrowLeft, Loader2 } from "lucide-react" +import { ArrowLeft, Loader2 } from 'lucide-react' import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from "recharts" import { fetchApi } from "@/lib/api-config" +import { getUnitsSettings, formatNetworkTraffic } from "@/lib/network-utils" interface MetricsViewProps { vmid: number @@ -84,6 +85,7 @@ const CustomDiskTooltip = ({ active, payload, label }: any) => { const CustomNetworkTooltip = ({ active, payload, label }: any) => { if (active && payload && payload.length) { + const unit = networkUnit === "Bits" ? "Mb" : "MB" return (

{label}

@@ -92,7 +94,7 @@ const CustomNetworkTooltip = ({ active, payload, label }: any) => {
{entry.name}: - {entry.value} MB + {entry.value} {unit}
))}
@@ -109,10 +111,20 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps) const [error, setError] = useState(null) const [hiddenDiskLines, setHiddenDiskLines] = useState([]) const [hiddenNetworkLines, setHiddenNetworkLines] = useState([]) + const [networkUnit, setNetworkUnit] = useState<"Bytes" | "Bits">("Bytes") useEffect(() => { - fetchMetrics() - }, [vmid, timeframe]) + const settings = getUnitsSettings() + setNetworkUnit(settings.networkUnit as "Bytes" | "Bits") + + const handleStorageChange = () => { + const settings = getUnitsSettings() + setNetworkUnit(settings.networkUnit as "Bytes" | "Bits") + } + + window.addEventListener('storage', handleStorageChange) + return () => window.removeEventListener('storage', handleStorageChange) + }, []) const fetchMetrics = async () => { setLoading(true) @@ -157,6 +169,9 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps) }) } + const netinValue = item.netin ? (item.netin / 1024 / 1024) : 0 + const netoutValue = item.netout ? (item.netout / 1024 / 1024) : 0 + return { time: timeLabel, timestamp: item.time, @@ -164,8 +179,8 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps) memory: item.mem ? Number(((item.mem / item.maxmem) * 100).toFixed(2)) : 0, memoryGB: item.mem ? Number((item.mem / 1024 / 1024 / 1024).toFixed(2)) : 0, maxMemoryGB: item.maxmem ? Number((item.maxmem / 1024 / 1024 / 1024).toFixed(2)) : 0, - netin: item.netin ? Number((item.netin / 1024 / 1024).toFixed(2)) : 0, - netout: item.netout ? Number((item.netout / 1024 / 1024).toFixed(2)) : 0, + netin: networkUnit === "Bits" ? Number((netinValue * 8).toFixed(2)) : Number(netinValue.toFixed(2)), + netout: networkUnit === "Bits" ? Number((netoutValue * 8).toFixed(2)) : Number(netoutValue.toFixed(2)), diskread: item.diskread ? Number((item.diskread / 1024 / 1024).toFixed(2)) : 0, diskwrite: item.diskwrite ? Number((item.diskwrite / 1024 / 1024).toFixed(2)) : 0, } @@ -179,6 +194,10 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps) } } + useEffect(() => { + fetchMetrics() + }, [vmid, timeframe]) + const formatXAxisTick = (tick: any) => { return tick } @@ -359,7 +378,7 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps) stroke="currentColor" className="text-foreground" tick={{ fill: "currentColor" }} - label={{ value: "MB", angle: -90, position: "insideLeft", fill: "currentColor" }} + label={{ value: networkUnit === "Bits" ? "Mb" : "MB", angle: -90, position: "insideLeft", fill: "currentColor" }} domain={[0, "dataMax"]} /> } /> diff --git a/AppImage/components/network-card.tsx b/AppImage/components/network-card.tsx index eb0680b..bd4215d 100644 --- a/AppImage/components/network-card.tsx +++ b/AppImage/components/network-card.tsx @@ -5,6 +5,7 @@ import { Badge } from "./ui/badge" import { Wifi, Zap } from 'lucide-react' import { useState, useEffect } from "react" import { fetchApi } from "../lib/api-config" +import { getUnitsSettings, formatNetworkTraffic, getNetworkLabel } from "../lib/network-utils" interface NetworkCardProps { interface_: { @@ -83,55 +84,6 @@ const formatStorage = (bytes: number): string => { return `${value.toFixed(decimals)} ${sizes[i]}` } -const getUnitsSettings = () => { - if (typeof window === 'undefined') return { networkUnit: 'Bytes' as const } - - try { - const settings = localStorage.getItem('unitsSettings') - if (settings) { - const parsed = JSON.parse(settings) - return { networkUnit: parsed.networkUnit || 'Bytes' } - } - } catch (e) { - console.error('[v0] Error reading units settings:', e) - } - - return { networkUnit: 'Bytes' as const } -} - -const formatNetworkTraffic = (sizeInGB: number, unit: "Bytes" | "Bits" = "Bytes"): string => { - if (unit === "Bits") { - // Convert GB to Gb (Gigabits) - const sizeInGb = sizeInGB * 8 - - if (sizeInGb < 0.001) { - // Less than 0.001 Gb, show in Mb - return `${(sizeInGb * 1024).toFixed(1)} Mb` - } else if (sizeInGb < 1) { - // Less than 1 Gb, show in Mb - return `${(sizeInGb * 1024).toFixed(1)} Mb` - } else if (sizeInGb < 1024) { - // Less than 1024 Gb, show in Gb - return `${sizeInGb.toFixed(1)} Gb` - } else { - // 1024 Gb or more, show in Tb - return `${(sizeInGb / 1024).toFixed(1)} Tb` - } - } else { - // Bytes mode (existing behavior) - if (sizeInGB < 1) { - // Less than 1 GB, show in MB - return `${(sizeInGB * 1024).toFixed(1)} MB` - } else if (sizeInGB < 1024) { - // Less than 1024 GB, show in GB - return `${sizeInGB.toFixed(1)} GB` - } else { - // 1024 GB or more, show in TB - return `${(sizeInGB / 1024).toFixed(1)} TB` - } - } -} - export function NetworkCard({ interface_, timeframe, onClick }: NetworkCardProps) { const typeBadge = getInterfaceTypeBadge(interface_.type) const vmTypeBadge = interface_.vm_type ? getVMTypeBadge(interface_.vm_type) : null diff --git a/AppImage/lib/network-utils.ts b/AppImage/lib/network-utils.ts new file mode 100644 index 0000000..7b7ec0f --- /dev/null +++ b/AppImage/lib/network-utils.ts @@ -0,0 +1,46 @@ +export const getUnitsSettings = () => { + if (typeof window === 'undefined') return { networkUnit: 'Bytes' as const } + + try { + const settings = localStorage.getItem('unitsSettings') + if (settings) { + const parsed = JSON.parse(settings) + return { networkUnit: parsed.networkUnit || 'Bytes' } + } + } catch (e) { + console.error('[v0] Error reading units settings:', e) + } + + return { networkUnit: 'Bytes' as const } +} + +export const formatNetworkTraffic = (sizeInGB: number, unit: "Bytes" | "Bits" = "Bytes"): string => { + if (unit === "Bits") { + const sizeInGb = sizeInGB * 8 + + if (sizeInGb < 0.001) { + return `${(sizeInGb * 1024).toFixed(1)} Mb` + } else if (sizeInGb < 1) { + return `${(sizeInGb * 1024).toFixed(1)} Mb` + } else if (sizeInGb < 1024) { + return `${sizeInGb.toFixed(1)} Gb` + } else { + return `${(sizeInGb / 1024).toFixed(1)} Tb` + } + } else { + if (sizeInGB < 1) { + return `${(sizeInGB * 1024).toFixed(1)} MB` + } else if (sizeInGB < 1024) { + return `${sizeInGB.toFixed(1)} GB` + } else { + return `${(sizeInGB / 1024).toFixed(1)} TB` + } + } +} + +export const getNetworkLabel = (unit: "Bytes" | "Bits", type: "received" | "sent"): string => { + if (unit === "Bits") { + return type === "received" ? "Bits Received" : "Bits Sent" + } + return type === "received" ? "Bytes Received" : "Bytes Sent" +}