diff --git a/AppImage/components/network-traffic-chart.tsx b/AppImage/components/network-traffic-chart.tsx
new file mode 100644
index 0000000..48afdc9
--- /dev/null
+++ b/AppImage/components/network-traffic-chart.tsx
@@ -0,0 +1,227 @@
+"use client"
+
+import { useState, useEffect } from "react"
+import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from "recharts"
+import { Loader2 } from "lucide-react"
+
+interface NetworkMetricsData {
+ time: string
+ timestamp: number
+ netIn: number
+ netOut: number
+}
+
+interface NetworkTrafficChartProps {
+ timeframe: string
+}
+
+const CustomNetworkTooltip = ({ active, payload, label }: any) => {
+ if (active && payload && payload.length) {
+ return (
+
+
{label}
+
+ {payload.map((entry: any, index: number) => (
+
+
+
{entry.name}:
+
{entry.value} GB
+
+ ))}
+
+
+ )
+ }
+ return null
+}
+
+export function NetworkTrafficChart({ timeframe }: NetworkTrafficChartProps) {
+ const [data, setData] = useState([])
+ const [loading, setLoading] = useState(true)
+ const [error, setError] = useState(null)
+ const [visibleLines, setVisibleLines] = useState({
+ netIn: true,
+ netOut: true,
+ })
+
+ useEffect(() => {
+ fetchMetrics()
+ }, [timeframe])
+
+ const fetchMetrics = async () => {
+ setLoading(true)
+ setError(null)
+
+ try {
+ const baseUrl =
+ typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
+ const apiUrl = `${baseUrl}/api/node/metrics?timeframe=${timeframe}`
+
+ const response = await fetch(apiUrl)
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch node metrics: ${response.status}`)
+ }
+
+ const result = await response.json()
+
+ if (!result.data || !Array.isArray(result.data)) {
+ throw new Error("Invalid data format received from server")
+ }
+
+ if (result.data.length === 0) {
+ setData([])
+ setLoading(false)
+ return
+ }
+
+ const transformedData = result.data.map((item: any) => {
+ const date = new Date(item.time * 1000)
+ let timeLabel = ""
+
+ if (timeframe === "hour") {
+ timeLabel = date.toLocaleString("en-US", {
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: false,
+ })
+ } else if (timeframe === "day") {
+ timeLabel = date.toLocaleString("en-US", {
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: false,
+ })
+ } else if (timeframe === "week") {
+ timeLabel = date.toLocaleString("en-US", {
+ month: "short",
+ day: "numeric",
+ hour: "2-digit",
+ hour12: false,
+ })
+ } else {
+ timeLabel = date.toLocaleString("en-US", {
+ month: "short",
+ day: "numeric",
+ })
+ }
+
+ return {
+ time: timeLabel,
+ timestamp: item.time,
+ netIn: item.netin ? Number((item.netin / 1024 / 1024 / 1024).toFixed(2)) : 0,
+ netOut: item.netout ? Number((item.netout / 1024 / 1024 / 1024).toFixed(2)) : 0,
+ }
+ })
+
+ setData(transformedData)
+ } catch (err: any) {
+ console.error("[v0] Error fetching network metrics:", err)
+ setError(err.message || "Error loading metrics")
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const tickInterval = Math.ceil(data.length / 8)
+
+ const handleLegendClick = (dataKey: string) => {
+ setVisibleLines((prev) => ({
+ ...prev,
+ [dataKey]: !prev[dataKey as keyof typeof prev],
+ }))
+ }
+
+ const renderLegend = (props: any) => {
+ const { payload } = props
+ return (
+
+ {payload.map((entry: any, index: number) => {
+ const isVisible = visibleLines[entry.dataKey as keyof typeof visibleLines]
+ return (
+
handleLegendClick(entry.dataKey)}
+ style={{ opacity: isVisible ? 1 : 0.4 }}
+ >
+
+
{entry.value}
+
+ )
+ })}
+
+ )
+ }
+
+ if (loading) {
+ return (
+
+
+
+ )
+ }
+
+ if (error) {
+ return (
+
+
Network metrics not available yet
+
{error}
+
+ )
+ }
+
+ if (data.length === 0) {
+ return (
+
+
No network metrics available
+
+ )
+ }
+
+ return (
+
+
+
+
+
+ } />
+
+
+
+
+
+ )
+}
diff --git a/AppImage/components/system-overview.tsx b/AppImage/components/system-overview.tsx
index 4f8ade5..387b146 100644
--- a/AppImage/components/system-overview.tsx
+++ b/AppImage/components/system-overview.tsx
@@ -6,6 +6,8 @@ import { Progress } from "./ui/progress"
import { Badge } from "./ui/badge"
import { Cpu, MemoryStick, Thermometer, Server, Zap, AlertCircle, HardDrive, Network } from "lucide-react"
import { NodeMetricsCharts } from "./node-metrics-charts"
+import { NetworkTrafficChart } from "./network-traffic-chart"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"
interface SystemData {
cpu_usage: number
@@ -229,6 +231,7 @@ export function SystemOverview() {
const [networkData, setNetworkData] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
+ const [networkTimeframe, setNetworkTimeframe] = useState("day")
useEffect(() => {
const fetchData = async () => {
@@ -624,9 +627,22 @@ export function SystemOverview() {
{/* Network Summary */}
-
-
- Network Overview
+
+
+
+ Network Overview
+
+
@@ -687,6 +703,10 @@ export function SystemOverview() {
+
+
+
+
) : (
Network data not available