mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-10 21:25:24 +03:00
Get storage graph formatted
This commit is contained in:
parent
b93031649f
commit
76b805eba1
@ -125,6 +125,108 @@ export function ThresholdBarGraph({
|
||||
);
|
||||
}
|
||||
|
||||
export function StorageGraph() {
|
||||
|
||||
const getUnitSize = (MB: number) => {
|
||||
if (isNaN(MB) || MB < 0) return "Invalid number";
|
||||
if (MB < 1024) return `${MB} MiB`;
|
||||
if (MB < 1048576) return `${(MB / 1024).toFixed(2)} GiB`;
|
||||
|
||||
return `${(MB / 1048576).toFixed(2)} TiB`;
|
||||
};
|
||||
|
||||
type StorageGraphProps = {
|
||||
graphId: string;
|
||||
used: number;
|
||||
total: number;
|
||||
data: ApexAxisChartSeries;
|
||||
};
|
||||
export function StorageGraph({
|
||||
graphId,
|
||||
used,
|
||||
total,
|
||||
data,
|
||||
}: StorageGraphProps) {
|
||||
const { theme, systemTheme } = useTheme();
|
||||
|
||||
const options = useMemo(() => {
|
||||
return {
|
||||
chart: {
|
||||
id: graphId,
|
||||
background: (systemTheme || theme) == "dark" ? "#404040" : "#E5E5E5",
|
||||
selection: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
zoom: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
show: false,
|
||||
padding: {
|
||||
bottom: -40,
|
||||
top: -60,
|
||||
left: -20,
|
||||
right: 0,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: true,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
theme: systemTheme || theme,
|
||||
},
|
||||
xaxis: {
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
labels: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
show: false,
|
||||
min: 0,
|
||||
max: 100,
|
||||
},
|
||||
};
|
||||
}, [graphId, systemTheme, theme]);
|
||||
|
||||
useEffect(() => {
|
||||
ApexCharts.exec(graphId, "updateOptions", options, true, true);
|
||||
}, [graphId, options]);
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-2.5">
|
||||
<div className="w-full flex justify-between items-center gap-1">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="text-xs text-primary-foreground">
|
||||
{getUnitSize(used)}
|
||||
</div>
|
||||
<div className="text-xs text-primary-foreground">/</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{getUnitSize(total)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-primary-foreground">
|
||||
{Math.round((used / total) * 100)}%
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-5 rounded-md overflow-hidden">
|
||||
<Chart type="bar" options={options} series={data} height="100%" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import TimeAgo from "@/components/dynamic/TimeAgo";
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
||||
import { isDesktop } from "react-device-detect";
|
||||
import GeneralMetrics from "@/views/system/GeneralMetrics";
|
||||
import StorageMetrics from "@/views/system/StorageMetrics";
|
||||
|
||||
const metrics = ["general", "storage", "cameras"] as const;
|
||||
type SystemMetric = (typeof metrics)[number];
|
||||
@ -70,6 +71,12 @@ function System() {
|
||||
setLastUpdated={setLastUpdated}
|
||||
/>
|
||||
)}
|
||||
{page == "storage" && (
|
||||
<StorageMetrics
|
||||
lastUpdated={lastUpdated}
|
||||
setLastUpdated={setLastUpdated}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
59
web/src/views/system/StorageMetrics.tsx
Normal file
59
web/src/views/system/StorageMetrics.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import { StorageGraph } from "@/components/graph/SystemGraph";
|
||||
import { useMemo } from "react";
|
||||
import useSWR from "swr";
|
||||
|
||||
type CameraStorage = {
|
||||
[key: string]: {
|
||||
bandwidth: number;
|
||||
usage: number;
|
||||
usage_percent: number;
|
||||
};
|
||||
};
|
||||
|
||||
type StorageMetricsProps = {
|
||||
lastUpdated: number;
|
||||
setLastUpdated: (last: number) => void;
|
||||
};
|
||||
export default function StorageMetrics({
|
||||
lastUpdated,
|
||||
setLastUpdated,
|
||||
}: StorageMetricsProps) {
|
||||
const { data: storage } = useSWR<CameraStorage>("recordings/storage");
|
||||
|
||||
const totalStorage = useMemo(() => {
|
||||
if (!storage) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const totalStorage = {
|
||||
total: 0,
|
||||
};
|
||||
|
||||
Object.values(storage).forEach((cam) => (totalStorage.total += cam.usage));
|
||||
|
||||
return totalStorage;
|
||||
}, [storage]);
|
||||
|
||||
if (!totalStorage) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="size-full mt-4 flex flex-col overflow-y-auto">
|
||||
<div className="text-muted-foreground text-sm font-medium">
|
||||
General Storage
|
||||
</div>
|
||||
<div className="mt-4 grid grid-cols-1 sm:grid-cols-3 gap-2">
|
||||
<div className="p-2.5 bg-primary rounded-2xl flex-col">
|
||||
<div className="mb-5">Recordings</div>
|
||||
<StorageGraph
|
||||
graphId="general-recordings"
|
||||
used={1000000}
|
||||
total={5000000}
|
||||
data={[{ name: "Recordings", data: [{ x: "Recordings", y: 25 }] }]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user