-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace individual storage graphs with combined graph (#13438)
* Replace individual storage graphs with combined graph * replace underscores with spaces * fix bar height
- Loading branch information
1 parent
a8dcc87
commit 6a0b5c3
Showing
3 changed files
with
247 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
import { useTheme } from "@/context/theme-provider"; | ||
import { generateColors } from "@/utils/colorUtil"; | ||
import { useEffect, useMemo } from "react"; | ||
import Chart from "react-apexcharts"; | ||
import { | ||
Table, | ||
TableBody, | ||
TableCell, | ||
TableHead, | ||
TableHeader, | ||
TableRow, | ||
} from "@/components/ui/table"; | ||
import { getUnitSize } from "@/utils/storageUtil"; | ||
|
||
type CameraStorage = { | ||
[key: string]: { | ||
bandwidth: number; | ||
usage: number; | ||
usage_percent: number; | ||
}; | ||
}; | ||
|
||
type TotalStorage = { | ||
used: number; | ||
total: number; | ||
}; | ||
|
||
type CombinedStorageGraphProps = { | ||
graphId: string; | ||
cameraStorage: CameraStorage; | ||
totalStorage: TotalStorage; | ||
}; | ||
export function CombinedStorageGraph({ | ||
graphId, | ||
cameraStorage, | ||
totalStorage, | ||
}: CombinedStorageGraphProps) { | ||
const { theme, systemTheme } = useTheme(); | ||
|
||
const entities = Object.keys(cameraStorage); | ||
const colors = generateColors(entities.length); | ||
|
||
const series = entities.map((entity, index) => ({ | ||
name: entity, | ||
data: [(cameraStorage[entity].usage / totalStorage.total) * 100], | ||
usage: cameraStorage[entity].usage, | ||
bandwidth: cameraStorage[entity].bandwidth, | ||
color: colors[index], // Assign the corresponding color | ||
})); | ||
|
||
// Add the unused percentage to the series | ||
series.push({ | ||
name: "Unused Free Space", | ||
data: [ | ||
((totalStorage.total - totalStorage.used) / totalStorage.total) * 100, | ||
], | ||
usage: totalStorage.total - totalStorage.used, | ||
bandwidth: 0, | ||
color: (systemTheme || theme) == "dark" ? "#404040" : "#E5E5E5", | ||
}); | ||
|
||
const options = useMemo(() => { | ||
return { | ||
chart: { | ||
id: graphId, | ||
background: (systemTheme || theme) == "dark" ? "#404040" : "#E5E5E5", | ||
selection: { | ||
enabled: false, | ||
}, | ||
toolbar: { | ||
show: false, | ||
}, | ||
zoom: { | ||
enabled: false, | ||
}, | ||
stacked: true, | ||
stackType: "100%", | ||
}, | ||
grid: { | ||
show: false, | ||
padding: { | ||
bottom: -45, | ||
top: -40, | ||
left: -20, | ||
right: -20, | ||
}, | ||
}, | ||
legend: { | ||
show: false, | ||
}, | ||
dataLabels: { | ||
enabled: false, | ||
}, | ||
plotOptions: { | ||
bar: { | ||
horizontal: true, | ||
}, | ||
}, | ||
states: { | ||
active: { | ||
filter: { | ||
type: "none", | ||
}, | ||
}, | ||
hover: { | ||
filter: { | ||
type: "none", | ||
}, | ||
}, | ||
}, | ||
tooltip: { | ||
enabled: false, | ||
x: { | ||
show: false, | ||
}, | ||
y: { | ||
formatter: function (val, { seriesIndex }) { | ||
if (series[seriesIndex]) { | ||
const usage = series[seriesIndex].usage; | ||
return `${getUnitSize(usage)} (${val.toFixed(2)}%)`; | ||
} | ||
}, | ||
}, | ||
theme: systemTheme || theme, | ||
}, | ||
xaxis: { | ||
axisBorder: { | ||
show: false, | ||
}, | ||
axisTicks: { | ||
show: false, | ||
}, | ||
labels: { | ||
formatter: function (val) { | ||
return val + "%"; | ||
}, | ||
}, | ||
min: 0, | ||
max: 100, | ||
}, | ||
yaxis: { | ||
show: false, | ||
min: 0, | ||
max: 100, | ||
}, | ||
} as ApexCharts.ApexOptions; | ||
}, [graphId, systemTheme, theme, series]); | ||
|
||
useEffect(() => { | ||
ApexCharts.exec(graphId, "updateOptions", options, true, true); | ||
}, [graphId, options]); | ||
|
||
return ( | ||
<div className="flex w-full flex-col gap-2.5"> | ||
<div className="flex w-full items-center justify-between gap-1"> | ||
<div className="flex items-center gap-1"> | ||
<div className="text-xs text-primary"> | ||
{getUnitSize(totalStorage.used)} | ||
</div> | ||
<div className="text-xs text-primary">/</div> | ||
<div className="text-xs text-muted-foreground"> | ||
{getUnitSize(totalStorage.total)} | ||
</div> | ||
</div> | ||
</div> | ||
<div className="h-5 overflow-hidden rounded-md"> | ||
<Chart type="bar" options={options} series={series} height="100%" /> | ||
</div> | ||
<div className="custom-legend"> | ||
<Table> | ||
<TableHeader> | ||
<TableRow> | ||
<TableHead>Camera</TableHead> | ||
<TableHead>Storage Used</TableHead> | ||
<TableHead>Percentage of Total Used</TableHead> | ||
<TableHead>Bandwidth</TableHead> | ||
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{series.map((item) => ( | ||
<TableRow key={item.name}> | ||
<TableCell className="flex flex-row items-center gap-2 font-medium capitalize"> | ||
{" "} | ||
<div | ||
className="size-3 rounded-md" | ||
style={{ backgroundColor: item.color }} | ||
></div> | ||
{item.name.replaceAll("_", " ")} | ||
</TableCell> | ||
<TableCell>{getUnitSize(item.usage)}</TableCell> | ||
<TableCell>{item.data[0].toFixed(2)}%</TableCell> | ||
<TableCell> | ||
{item.name === "Unused Free Space" | ||
? "—" | ||
: `${getUnitSize(item.bandwidth)} / hour`} | ||
</TableCell> | ||
</TableRow> | ||
))} | ||
</TableBody> | ||
</Table> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Utility function to generate colors based on a predefined palette with slight variations | ||
export const generateColors = (numColors: number) => { | ||
const palette = [ | ||
"#008FFB", | ||
"#00E396", | ||
"#FEB019", | ||
"#FF4560", | ||
"#775DD0", | ||
"#3F51B5", | ||
"#03A9F4", | ||
"#4CAF50", | ||
"#F9CE1D", | ||
"#FF9800", | ||
]; | ||
|
||
const colors = [...palette]; // Start with the predefined palette | ||
|
||
for (let i = palette.length; i < numColors; i++) { | ||
const baseColor = palette[i % palette.length]; | ||
// Modify the base color slightly by adjusting the brightness for additional colors | ||
const factor = 1 + Math.floor(i / palette.length) * 0.1; | ||
const modifiedColor = adjustColorBrightness(baseColor, factor); | ||
colors.push(modifiedColor); | ||
} | ||
|
||
return colors.slice(0, numColors); | ||
}; | ||
|
||
const adjustColorBrightness = (color: string, factor: number) => { | ||
const rgb = parseInt(color.slice(1), 16); | ||
const r = Math.min(255, Math.floor(((rgb >> 16) & 0xff) * factor)); | ||
const g = Math.min(255, Math.floor(((rgb >> 8) & 0xff) * factor)); | ||
const b = Math.min(255, Math.floor((rgb & 0xff) * factor)); | ||
|
||
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters