Add temperature chart filtering (#430)

- Refactored ContainerFilterBar to accept a dynamic store prop.
- Updated filtering logic in ContainerChart to be case-insensitive.
This commit is contained in:
henrygd
2025-04-25 19:19:19 -04:00
parent 38f2ba3984
commit bda06f30b3
5 changed files with 43 additions and 21 deletions

View File

@@ -170,7 +170,7 @@ export default memo(function ContainerChart({
content={<ChartTooltipContent filter={filter} contentFormatter={toolTipFormatter} />} content={<ChartTooltipContent filter={filter} contentFormatter={toolTipFormatter} />}
/> />
{Object.keys(chartConfig).map((key) => { {Object.keys(chartConfig).map((key) => {
const filtered = filter && !key.includes(filter) const filtered = filter && !key.toLowerCase().includes(filter.toLowerCase())
let fillOpacity = filtered ? 0.05 : 0.4 let fillOpacity = filtered ? 0.05 : 0.4
let strokeOpacity = filtered ? 0.1 : 1 let strokeOpacity = filtered ? 0.1 : 1
return ( return (

View File

@@ -18,8 +18,11 @@ import {
} from "@/lib/utils" } from "@/lib/utils"
import { ChartData } from "@/types" import { ChartData } from "@/types"
import { memo, useMemo } from "react" import { memo, useMemo } from "react"
import { $temperatureFilter } from "@/lib/stores"
import { useStore } from "@nanostores/react"
export default memo(function TemperatureChart({ chartData }: { chartData: ChartData }) { export default memo(function TemperatureChart({ chartData }: { chartData: ChartData }) {
const filter = useStore($temperatureFilter)
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth() const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
if (chartData.systemStats.length === 0) { if (chartData.systemStats.length === 0) {
@@ -86,11 +89,14 @@ export default memo(function TemperatureChart({ chartData }: { chartData: ChartD
<ChartTooltipContent <ChartTooltipContent
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)} labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
contentFormatter={(item) => decimalString(item.value) + " °C"} contentFormatter={(item) => decimalString(item.value) + " °C"}
// indicator="line" filter={filter}
/> />
} }
/> />
{colors.map((key) => ( {colors.map((key) => {
const filtered = filter && !key.toLowerCase().includes(filter.toLowerCase())
let strokeOpacity = filtered ? 0.1 : 1
return (
<Line <Line
key={key} key={key}
dataKey={key} dataKey={key}
@@ -99,9 +105,12 @@ export default memo(function TemperatureChart({ chartData }: { chartData: ChartD
dot={false} dot={false}
strokeWidth={1.5} strokeWidth={1.5}
stroke={newChartData.colors[key]} stroke={newChartData.colors[key]}
strokeOpacity={strokeOpacity}
activeDot={{ opacity: filtered ? 0 : 1 }}
isAnimationActive={false} isAnimationActive={false}
/> />
))} )
})}
{colors.length < 12 && <ChartLegend content={<ChartLegendContent />} />} {colors.length < 12 && <ChartLegend content={<ChartLegendContent />} />}
</LineChart> </LineChart>
</ChartContainer> </ChartContainer>

View File

@@ -1,6 +1,15 @@
import { t } from "@lingui/core/macro" import { t } from "@lingui/core/macro"
import { Plural, Trans } from "@lingui/react/macro" import { Plural, Trans } from "@lingui/react/macro"
import { $systems, pb, $chartTime, $containerFilter, $userSettings, $direction, $maxValues } from "@/lib/stores" import {
$systems,
pb,
$chartTime,
$containerFilter,
$userSettings,
$direction,
$maxValues,
$temperatureFilter,
} from "@/lib/stores"
import { ChartData, ChartTimes, ContainerStatsRecord, GPUData, SystemRecord, SystemStatsRecord } from "@/types" import { ChartData, ChartTimes, ContainerStatsRecord, GPUData, SystemRecord, SystemStatsRecord } from "@/types"
import { ChartType, Os } from "@/lib/enums" import { ChartType, Os } from "@/lib/enums"
import React, { lazy, memo, useCallback, useEffect, useMemo, useRef, useState } from "react" import React, { lazy, memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
@@ -219,7 +228,7 @@ export default function SystemDetail({ name }: { name: string }) {
cache.set(cs_cache_key, containerData) cache.set(cs_cache_key, containerData)
} }
if (containerData.length) { if (containerData.length) {
!containerFilterBar && setContainerFilterBar(<ContainerFilterBar />) !containerFilterBar && setContainerFilterBar(<FilterBar />)
} else if (containerFilterBar) { } else if (containerFilterBar) {
setContainerFilterBar(null) setContainerFilterBar(null)
} }
@@ -557,6 +566,7 @@ export default function SystemDetail({ name }: { name: string }) {
grid={grid} grid={grid}
title={t`Temperature`} title={t`Temperature`}
description={t`Temperatures of system sensors`} description={t`Temperatures of system sensors`}
cornerEl={<FilterBar store={$temperatureFilter} />}
> >
<TemperatureChart chartData={chartData} /> <TemperatureChart chartData={chartData} />
</ChartCard> </ChartCard>
@@ -654,12 +664,12 @@ export default function SystemDetail({ name }: { name: string }) {
) )
} }
function ContainerFilterBar() { function FilterBar({ store = $containerFilter }: { store?: typeof $containerFilter }) {
const containerFilter = useStore($containerFilter) const containerFilter = useStore(store)
const { t } = useLingui() const { t } = useLingui()
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
$containerFilter.set(e.target.value) store.set(e.target.value)
}, []) }, [])
return ( return (
@@ -672,7 +682,7 @@ function ContainerFilterBar() {
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={() => $containerFilter.set("")} onClick={() => store.set("")}
> >
<XIcon className="h-4 w-4" /> <XIcon className="h-4 w-4" />
</Button> </Button>

View File

@@ -129,7 +129,7 @@ const ChartTooltipContent = React.forwardRef<
React.useMemo(() => { React.useMemo(() => {
if (filter) { if (filter) {
payload = payload?.filter((item) => (item.name as string)?.includes(filter)) payload = payload?.filter((item) => (item.name as string)?.toLowerCase().includes(filter.toLowerCase()))
} }
if (itemSorter) { if (itemSorter) {
// @ts-ignore // @ts-ignore

View File

@@ -38,6 +38,9 @@ $userSettings.subscribe((value) => {
/** Container chart filter */ /** Container chart filter */
export const $containerFilter = atom("") export const $containerFilter = atom("")
/** Temperature chart filter */
export const $temperatureFilter = atom("")
/** Fallback copy to clipboard dialog content */ /** Fallback copy to clipboard dialog content */
export const $copyContent = atom("") export const $copyContent = atom("")