import { Loader2, RefreshCw } from "lucide-react"; import { useMemo } from "react"; import { Cell, Label, Pie, PieChart } from "recharts"; import { Button } from "@/components/ui/button"; import { type ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart"; import { api } from "@/utils/api"; const TYPE_TO_KEY: Record = { Images: "images", Containers: "containers", "Local Volumes": "volumes", "Build Cache": "buildCache", }; const chartConfig = { value: { label: "Size", }, images: { label: "Images", color: "hsl(var(--chart-1))", }, containers: { label: "Containers", color: "hsl(var(--chart-2))", }, volumes: { label: "Volumes", color: "hsl(var(--chart-3))", }, buildCache: { label: "Build Cache", color: "hsl(var(--chart-4))", }, } satisfies ChartConfig; const formatSize = (bytes: number): string => { if (bytes >= 1024 ** 3) return `${(bytes / 1024 ** 3).toFixed(2)} GB`; if (bytes >= 1024 ** 2) return `${(bytes / 1024 ** 2).toFixed(1)} MB`; if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${bytes} B`; }; export const DockerDiskUsageChart = () => { const { data, isLoading, refetch, isRefetching } = api.settings.getDockerDiskUsage.useQuery(undefined, { refetchOnWindowFocus: false, }); const { chartData, totalBytes } = useMemo(() => { const items = data ?.filter((item) => item.sizeBytes > 0) .map((item) => { const key = TYPE_TO_KEY[item.type] ?? item.type; return { name: key, value: item.sizeBytes, size: item.size, active: item.active, totalCount: item.totalCount, reclaimable: item.reclaimable, fill: `var(--color-${key})`, }; }) ?? []; return { chartData: items, totalBytes: items.reduce((sum, item) => sum + item.value, 0), }; }, [data]); if (isLoading) { return (
); } if (chartData.length === 0) { return (

No Docker disk usage data available.

); } return (
Total: {formatSize(totalBytes)}
{ const item = chartData.find((d) => d.name === name); if (!item) return [formatSize(value as number), name]; return [ `${item.size} — ${item.active} active / ${item.totalCount} total — Reclaimable: ${item.reclaimable}`, chartConfig[name as keyof typeof chartConfig]?.label ?? name, ]; }} /> } /> {chartData.map((entry) => ( ))} } />
); };