improve container filtering performance

- also a few instances of autoformat
This commit is contained in:
henrygd
2025-09-09 16:59:16 -04:00
parent b2b54db409
commit 956880aa59
2 changed files with 37 additions and 18 deletions

View File

@@ -1,11 +1,11 @@
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
import { memo, useMemo } from "react"
import { cn, formatShortDate, chartMargin, toFixedFloat, 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 type { ChartData } from "@/types"
import { Separator } from "../ui/separator"
import { ChartType, Unit } from "@/lib/enums"
import { useYAxisWidth } from "./hooks"
@@ -31,6 +31,7 @@ export default memo(function ContainerChart({
const isNetChart = chartType === ChartType.Network
// biome-ignore lint/correctness/useExhaustiveDependencies: not necessary
const { toolTipFormatter, dataFunction, tickFormatter } = useMemo(() => {
const obj = {} as {
toolTipFormatter: (item: any, key: string) => React.ReactNode | string
@@ -47,7 +48,7 @@ export default memo(function ContainerChart({
const chartUnit = isNetChart ? userSettings.unitNet : Unit.Bytes
obj.tickFormatter = (val) => {
const { value, unit } = formatBytes(val, isNetChart, chartUnit, true)
return updateYAxisWidth(toFixedFloat(value, value >= 10 ? 0 : 1) + " " + unit)
return updateYAxisWidth(`${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`)
}
}
// tooltip formatter
@@ -74,10 +75,10 @@ export default memo(function ContainerChart({
} else if (chartType === ChartType.Memory) {
obj.toolTipFormatter = (item: any) => {
const { value, unit } = formatBytes(item.value, false, Unit.Bytes, true)
return decimalString(value) + " " + unit
return `${decimalString(value)} ${unit}`
}
} else {
obj.toolTipFormatter = (item: any) => decimalString(item.value) + unit
obj.toolTipFormatter = (item: any) => `${decimalString(item.value)} ${unit}`
}
// data function
if (isNetChart) {
@@ -133,7 +134,7 @@ export default memo(function ContainerChart({
animationDuration={150}
truncate={true}
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
// @ts-ignore
// @ts-expect-error
itemSorter={(a, b) => b.value - a.value}
content={<ChartTooltipContent filter={filter} contentFormatter={toolTipFormatter} />}
/>

View File

@@ -32,6 +32,7 @@ import { useIntersectionObserver } from "@/lib/use-intersection-observer"
import {
chartTimeData,
cn,
debounce,
decimalString,
formatBytes,
getHostDisplayValue,
@@ -61,7 +62,6 @@ type ChartTimeData = {
chartTime: ChartTimes
}
const cache = new Map<string, ChartTimeData | SystemStatsRecord[] | ContainerStatsRecord[]>()
// create ticks and domain for charts
@@ -88,7 +88,7 @@ function getTimeData(chartTime: ChartTimes, lastCreated: number) {
function addEmptyValues<T extends SystemStatsRecord | ContainerStatsRecord>(
prevRecords: T[],
newRecords: T[],
expectedInterval: number,
expectedInterval: number
) {
const modifiedRecords: T[] = []
let prevTime = (prevRecords.at(-1)?.created ?? 0) as number
@@ -109,7 +109,11 @@ function addEmptyValues<T extends SystemStatsRecord | ContainerStatsRecord>(
return modifiedRecords
}
async function getStats<T extends SystemStatsRecord | ContainerStatsRecord>(collection: string, system: SystemRecord, chartTime: ChartTimes): Promise<T[]> {
async function getStats<T extends SystemStatsRecord | ContainerStatsRecord>(
collection: string,
system: SystemRecord,
chartTime: ChartTimes
): Promise<T[]> {
const cachedStats = cache.get(`${system.id}_${chartTime}_${collection}`) as T[] | undefined
const lastCached = cachedStats?.at(-1)?.created as number
return await pb.collection<T>(collection).getFullList({
@@ -175,7 +179,7 @@ export default memo(function SystemDetail({ name }: { name: string }) {
const chartData: ChartData = useMemo(() => {
const lastCreated = Math.max(
(systemStats.at(-1)?.created as number) ?? 0,
(containerData.at(-1)?.created as number) ?? 0,
(containerData.at(-1)?.created as number) ?? 0
)
return {
systemStats,
@@ -256,7 +260,6 @@ export default memo(function SystemDetail({ name }: { name: string }) {
})
}, [system, chartTime])
// values for system info bar
const systemInfo = useMemo(() => {
if (!system.info) {
@@ -859,14 +862,24 @@ export default memo(function SystemDetail({ name }: { name: string }) {
function FilterBar({ store = $containerFilter }: { store?: typeof $containerFilter }) {
const containerFilter = useStore(store)
const { t } = useLingui()
const inputRef = useRef<HTMLInputElement>(null)
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
store.set(e.target.value)
}, [store])
const debouncedStoreSet = useMemo(() => debounce((value: string) => store.set(value), 150), [store])
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
if (inputRef.current) {
inputRef.current.value = value
}
debouncedStoreSet(value)
},
[debouncedStoreSet]
)
return (
<>
<Input placeholder={t`Filter...`} className="ps-4 pe-8" value={containerFilter} onChange={handleChange} />
<Input placeholder={t`Filter...`} className="ps-4 pe-8" onChange={handleChange} ref={inputRef} />
{containerFilter && (
<Button
type="button"
@@ -874,7 +887,12 @@ function FilterBar({ store = $containerFilter }: { store?: typeof $containerFilt
size="icon"
aria-label="Clear"
className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100"
onClick={() => store.set("")}
onClick={() => {
if (inputRef.current) {
inputRef.current.value = ""
}
store.set("")
}}
>
<XIcon className="h-4 w-4" />
</Button>