import { Area, AreaChart, CartesianGrid, YAxis } from "recharts" import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart" import { memo, useMemo } from "react" import { useYAxisWidth, cn, formatShortDate, chartMargin, toFixedWithoutTrailingZeros, formatBytes, decimalString, } from "@/lib/utils" // import Spinner from '../spinner' import { useStore } from "@nanostores/react" import { $containerFilter, $userSettings } from "@/lib/stores" import { ChartData } from "@/types" import { Separator } from "../ui/separator" import { ChartType, Unit } from "@/lib/enums" export default memo(function ContainerChart({ dataKey, chartData, chartType, unit = "%", }: { dataKey: string chartData: ChartData chartType: ChartType unit?: string }) { const filter = useStore($containerFilter) const userSettings = $userSettings.get() const { yAxisWidth, updateYAxisWidth } = useYAxisWidth() const { containerData } = chartData const isNetChart = chartType === ChartType.Network const chartConfig = useMemo(() => { let config = {} as Record< string, { label: string color: string } > const totalUsage = {} as Record for (let stats of containerData) { for (let key in stats) { if (!key || key === "created") { continue } if (!(key in totalUsage)) { totalUsage[key] = 0 } if (isNetChart) { totalUsage[key] += (stats[key]?.nr ?? 0) + (stats[key]?.ns ?? 0) } else { // @ts-ignore totalUsage[key] += stats[key]?.[dataKey] ?? 0 } } } let keys = Object.keys(totalUsage) keys.sort((a, b) => (totalUsage[a] > totalUsage[b] ? -1 : 1)) const length = keys.length for (let i = 0; i < length; i++) { const key = keys[i] const hue = ((i * 360) / length) % 360 config[key] = { label: key, color: `hsl(${hue}, 60%, 55%)`, } } return config satisfies ChartConfig }, [chartData]) const { toolTipFormatter, dataFunction, tickFormatter } = useMemo(() => { const obj = {} as { toolTipFormatter: (item: any, key: string) => React.ReactNode | string dataFunction: (key: string, data: any) => number | null tickFormatter: (value: any) => string } // tick formatter if (chartType === ChartType.CPU) { obj.tickFormatter = (value) => { const val = toFixedWithoutTrailingZeros(value, 2) + unit return updateYAxisWidth(val) } } else { const chartUnit = isNetChart ? userSettings.unitNet : Unit.Bytes obj.tickFormatter = (val) => { const { value, unit } = formatBytes(val, isNetChart, chartUnit, true) return updateYAxisWidth(decimalString(value, value >= 10 ? 0 : 1) + " " + unit) } } // tooltip formatter if (isNetChart) { obj.toolTipFormatter = (item: any, key: string) => { try { const sent = item?.payload?.[key]?.ns ?? 0 const received = item?.payload?.[key]?.nr ?? 0 const { value: receivedValue, unit: receivedUnit } = formatBytes(received, true, userSettings.unitNet, true) const { value: sentValue, unit: sentUnit } = formatBytes(sent, true, userSettings.unitNet, true) return ( {decimalString(receivedValue)} {receivedUnit} rx {decimalString(sentValue)} {sentUnit} tx ) } catch (e) { return null } } } else if (chartType === ChartType.Memory) { obj.toolTipFormatter = (item: any) => { const { value, unit } = formatBytes(item.value, false, Unit.Bytes, true) return decimalString(value) + " " + unit } } else { obj.toolTipFormatter = (item: any) => decimalString(item.value) + unit } // data function if (isNetChart) { obj.dataFunction = (key: string, data: any) => (data[key] ? data[key].nr + data[key].ns : null) } else { obj.dataFunction = (key: string, data: any) => data[key]?.[dataKey] ?? null } return obj }, []) // console.log('rendered at', new Date()) if (containerData.length === 0) { return null } return (
{xAxis(chartData)} formatShortDate(data[0].payload.created)} // @ts-ignore itemSorter={(a, b) => b.value - a.value} content={} /> {Object.keys(chartConfig).map((key) => { const filtered = filter && !key.toLowerCase().includes(filter.toLowerCase()) let fillOpacity = filtered ? 0.05 : 0.4 let strokeOpacity = filtered ? 0.1 : 1 return ( ) })}
) })