mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 01:39:34 +08:00
improve container filtering performance
- also a few instances of autoformat
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
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 { memo, useMemo } from "react"
|
||||||
import { cn, formatShortDate, chartMargin, toFixedFloat, formatBytes, decimalString } from "@/lib/utils"
|
import { cn, formatShortDate, chartMargin, toFixedFloat, formatBytes, decimalString } from "@/lib/utils"
|
||||||
// import Spinner from '../spinner'
|
// import Spinner from '../spinner'
|
||||||
import { useStore } from "@nanostores/react"
|
import { useStore } from "@nanostores/react"
|
||||||
import { $containerFilter, $userSettings } from "@/lib/stores"
|
import { $containerFilter, $userSettings } from "@/lib/stores"
|
||||||
import { ChartData } from "@/types"
|
import type { ChartData } from "@/types"
|
||||||
import { Separator } from "../ui/separator"
|
import { Separator } from "../ui/separator"
|
||||||
import { ChartType, Unit } from "@/lib/enums"
|
import { ChartType, Unit } from "@/lib/enums"
|
||||||
import { useYAxisWidth } from "./hooks"
|
import { useYAxisWidth } from "./hooks"
|
||||||
@@ -31,6 +31,7 @@ export default memo(function ContainerChart({
|
|||||||
|
|
||||||
const isNetChart = chartType === ChartType.Network
|
const isNetChart = chartType === ChartType.Network
|
||||||
|
|
||||||
|
// biome-ignore lint/correctness/useExhaustiveDependencies: not necessary
|
||||||
const { toolTipFormatter, dataFunction, tickFormatter } = useMemo(() => {
|
const { toolTipFormatter, dataFunction, tickFormatter } = useMemo(() => {
|
||||||
const obj = {} as {
|
const obj = {} as {
|
||||||
toolTipFormatter: (item: any, key: string) => React.ReactNode | string
|
toolTipFormatter: (item: any, key: string) => React.ReactNode | string
|
||||||
@@ -47,7 +48,7 @@ export default memo(function ContainerChart({
|
|||||||
const chartUnit = isNetChart ? userSettings.unitNet : Unit.Bytes
|
const chartUnit = isNetChart ? userSettings.unitNet : Unit.Bytes
|
||||||
obj.tickFormatter = (val) => {
|
obj.tickFormatter = (val) => {
|
||||||
const { value, unit } = formatBytes(val, isNetChart, chartUnit, true)
|
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
|
// tooltip formatter
|
||||||
@@ -74,10 +75,10 @@ export default memo(function ContainerChart({
|
|||||||
} else if (chartType === ChartType.Memory) {
|
} else if (chartType === ChartType.Memory) {
|
||||||
obj.toolTipFormatter = (item: any) => {
|
obj.toolTipFormatter = (item: any) => {
|
||||||
const { value, unit } = formatBytes(item.value, false, Unit.Bytes, true)
|
const { value, unit } = formatBytes(item.value, false, Unit.Bytes, true)
|
||||||
return decimalString(value) + " " + unit
|
return `${decimalString(value)} ${unit}`
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
obj.toolTipFormatter = (item: any) => decimalString(item.value) + unit
|
obj.toolTipFormatter = (item: any) => `${decimalString(item.value)} ${unit}`
|
||||||
}
|
}
|
||||||
// data function
|
// data function
|
||||||
if (isNetChart) {
|
if (isNetChart) {
|
||||||
@@ -133,7 +134,7 @@ export default memo(function ContainerChart({
|
|||||||
animationDuration={150}
|
animationDuration={150}
|
||||||
truncate={true}
|
truncate={true}
|
||||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
itemSorter={(a, b) => b.value - a.value}
|
itemSorter={(a, b) => b.value - a.value}
|
||||||
content={<ChartTooltipContent filter={filter} contentFormatter={toolTipFormatter} />}
|
content={<ChartTooltipContent filter={filter} contentFormatter={toolTipFormatter} />}
|
||||||
/>
|
/>
|
||||||
|
@@ -32,6 +32,7 @@ import { useIntersectionObserver } from "@/lib/use-intersection-observer"
|
|||||||
import {
|
import {
|
||||||
chartTimeData,
|
chartTimeData,
|
||||||
cn,
|
cn,
|
||||||
|
debounce,
|
||||||
decimalString,
|
decimalString,
|
||||||
formatBytes,
|
formatBytes,
|
||||||
getHostDisplayValue,
|
getHostDisplayValue,
|
||||||
@@ -61,7 +62,6 @@ type ChartTimeData = {
|
|||||||
chartTime: ChartTimes
|
chartTime: ChartTimes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const cache = new Map<string, ChartTimeData | SystemStatsRecord[] | ContainerStatsRecord[]>()
|
const cache = new Map<string, ChartTimeData | SystemStatsRecord[] | ContainerStatsRecord[]>()
|
||||||
|
|
||||||
// create ticks and domain for charts
|
// create ticks and domain for charts
|
||||||
@@ -88,7 +88,7 @@ function getTimeData(chartTime: ChartTimes, lastCreated: number) {
|
|||||||
function addEmptyValues<T extends SystemStatsRecord | ContainerStatsRecord>(
|
function addEmptyValues<T extends SystemStatsRecord | ContainerStatsRecord>(
|
||||||
prevRecords: T[],
|
prevRecords: T[],
|
||||||
newRecords: T[],
|
newRecords: T[],
|
||||||
expectedInterval: number,
|
expectedInterval: number
|
||||||
) {
|
) {
|
||||||
const modifiedRecords: T[] = []
|
const modifiedRecords: T[] = []
|
||||||
let prevTime = (prevRecords.at(-1)?.created ?? 0) as number
|
let prevTime = (prevRecords.at(-1)?.created ?? 0) as number
|
||||||
@@ -109,7 +109,11 @@ function addEmptyValues<T extends SystemStatsRecord | ContainerStatsRecord>(
|
|||||||
return modifiedRecords
|
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 cachedStats = cache.get(`${system.id}_${chartTime}_${collection}`) as T[] | undefined
|
||||||
const lastCached = cachedStats?.at(-1)?.created as number
|
const lastCached = cachedStats?.at(-1)?.created as number
|
||||||
return await pb.collection<T>(collection).getFullList({
|
return await pb.collection<T>(collection).getFullList({
|
||||||
@@ -175,7 +179,7 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
const chartData: ChartData = useMemo(() => {
|
const chartData: ChartData = useMemo(() => {
|
||||||
const lastCreated = Math.max(
|
const lastCreated = Math.max(
|
||||||
(systemStats.at(-1)?.created as number) ?? 0,
|
(systemStats.at(-1)?.created as number) ?? 0,
|
||||||
(containerData.at(-1)?.created as number) ?? 0,
|
(containerData.at(-1)?.created as number) ?? 0
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
systemStats,
|
systemStats,
|
||||||
@@ -256,7 +260,6 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
})
|
})
|
||||||
}, [system, chartTime])
|
}, [system, chartTime])
|
||||||
|
|
||||||
|
|
||||||
// values for system info bar
|
// values for system info bar
|
||||||
const systemInfo = useMemo(() => {
|
const systemInfo = useMemo(() => {
|
||||||
if (!system.info) {
|
if (!system.info) {
|
||||||
@@ -859,14 +862,24 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
function FilterBar({ store = $containerFilter }: { store?: typeof $containerFilter }) {
|
function FilterBar({ store = $containerFilter }: { store?: typeof $containerFilter }) {
|
||||||
const containerFilter = useStore(store)
|
const containerFilter = useStore(store)
|
||||||
const { t } = useLingui()
|
const { t } = useLingui()
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
const debouncedStoreSet = useMemo(() => debounce((value: string) => store.set(value), 150), [store])
|
||||||
store.set(e.target.value)
|
|
||||||
}, [store])
|
const handleChange = useCallback(
|
||||||
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const value = e.target.value
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.value = value
|
||||||
|
}
|
||||||
|
debouncedStoreSet(value)
|
||||||
|
},
|
||||||
|
[debouncedStoreSet]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
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 && (
|
{containerFilter && (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -874,7 +887,12 @@ function FilterBar({ store = $containerFilter }: { store?: typeof $containerFilt
|
|||||||
size="icon"
|
size="icon"
|
||||||
aria-label="Clear"
|
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"
|
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" />
|
<XIcon className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
Reference in New Issue
Block a user