import { useMemo } from "react"
import { t } from "@lingui/core/macro"
import AreaChartDefault from "@/components/charts/area-chart"
import { useContainerDataPoints } from "@/components/charts/hooks"
import { $userSettings } from "@/lib/stores"
import { decimalString, formatBytes, toFixedFloat } from "@/lib/utils"
import type { ChartConfig } from "@/components/ui/chart"
import { pinnedAxisDomain } from "@/components/ui/chart"
import type { ChartData, SystemStatsRecord } from "@/types"
import { Separator } from "@/components/ui/separator"
import NetworkSheet from "../network-sheet"
import { ChartCard, FilterBar, SelectAvgMax } from "../chart-card"
import { dockerOrPodman } from "../chart-data"
export function BandwidthChart({
chartData,
grid,
dataEmpty,
showMax,
isLongerChart,
maxValues,
systemStats,
}: {
chartData: ChartData
grid: boolean
dataEmpty: boolean
showMax: boolean
isLongerChart: boolean
maxValues: boolean
systemStats: SystemStatsRecord[]
}) {
const maxValSelect = isLongerChart ? : null
const userSettings = $userSettings.get()
return (
{maxValSelect}
}
description={t`Network traffic of public interfaces`}
>
(systemStats.at(-1)?.stats.b?.[1] ?? 0) - (systemStats.at(-1)?.stats.b?.[0] ?? 0))}
tickFormatter={(val) => {
const { value, unit } = formatBytes(val, true, userSettings.unitNet, false)
return `${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`
}}
contentFormatter={(data) => {
const { value, unit } = formatBytes(data.value, true, userSettings.unitNet, false)
return `${decimalString(value, value >= 100 ? 1 : 2)} ${unit}`
}}
showTotal={true}
/>
)
}
export function ContainerNetworkChart({
chartData,
grid,
dataEmpty,
isPodman,
networkConfig,
}: {
chartData: ChartData
grid: boolean
dataEmpty: boolean
isPodman: boolean
networkConfig: ChartConfig
}) {
const userSettings = $userSettings.get()
const { filter, dataPoints, filteredKeys } = useContainerDataPoints(networkConfig, (key, data) => {
const payload = data[key]
if (!payload) return null
const sent = payload?.b?.[0] ?? (payload?.ns ?? 0) * 1024 * 1024
const recv = payload?.b?.[1] ?? (payload?.nr ?? 0) * 1024 * 1024
return sent + recv
})
const contentFormatter = useMemo(() => {
const getRxTxBytes = (record?: { b?: [number, number]; ns?: number; nr?: number }) => {
if (record?.b?.length && record.b.length >= 2) {
return [Number(record.b[0]) || 0, Number(record.b[1]) || 0]
}
return [(record?.ns ?? 0) * 1024 * 1024, (record?.nr ?? 0) * 1024 * 1024]
}
const formatRxTx = (recv: number, sent: number) => {
const { value: receivedValue, unit: receivedUnit } = formatBytes(recv, true, userSettings.unitNet, false)
const { value: sentValue, unit: sentUnit } = formatBytes(sent, true, userSettings.unitNet, false)
return (
{decimalString(receivedValue)} {receivedUnit}
rx
{decimalString(sentValue)} {sentUnit}
tx
)
}
// biome-ignore lint/suspicious/noExplicitAny: recharts tooltip item
return (item: any, key: string) => {
try {
if (key === "__total__") {
let totalSent = 0
let totalRecv = 0
const payloadData = item?.payload && typeof item.payload === "object" ? item.payload : {}
for (const [containerKey, value] of Object.entries(payloadData)) {
if (!value || typeof value !== "object") continue
if (filteredKeys.has(containerKey)) continue
const [sent, recv] = getRxTxBytes(value as { b?: [number, number]; ns?: number; nr?: number })
totalSent += sent
totalRecv += recv
}
return formatRxTx(totalRecv, totalSent)
}
const [sent, recv] = getRxTxBytes(item?.payload?.[key])
return formatRxTx(recv, sent)
} catch {
return null
}
}
}, [filteredKeys, userSettings.unitNet])
return (
}
>
{
const { value, unit } = formatBytes(val, true, userSettings.unitNet, false)
return `${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`
}}
contentFormatter={contentFormatter}
domain={pinnedAxisDomain()}
showTotal={true}
reverseStackOrder={true}
filter={filter}
truncate={true}
itemSorter={(a, b) => b.value - a.value}
/>
)
}