mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 09:49:28 +08:00
chart tooltip sorting
This commit is contained in:
@@ -11,13 +11,7 @@ import { useMemo } from 'react'
|
|||||||
import { formatShortDate, formatShortTime } from '@/lib/utils'
|
import { formatShortDate, formatShortTime } from '@/lib/utils'
|
||||||
import Spinner from '../spinner'
|
import Spinner from '../spinner'
|
||||||
|
|
||||||
export default function ({
|
export default function ({ chartData }: { chartData: Record<string, number | string>[] }) {
|
||||||
chartData,
|
|
||||||
max,
|
|
||||||
}: {
|
|
||||||
chartData: Record<string, number | string>[]
|
|
||||||
max: number
|
|
||||||
}) {
|
|
||||||
const chartConfig = useMemo(() => {
|
const chartConfig = useMemo(() => {
|
||||||
let config = {} as Record<
|
let config = {} as Record<
|
||||||
string,
|
string,
|
||||||
@@ -65,8 +59,7 @@ export default function ({
|
|||||||
margin={{
|
margin={{
|
||||||
top: 10,
|
top: 10,
|
||||||
}}
|
}}
|
||||||
|
reverseStackOrder={true}
|
||||||
// reverseStackOrder={true}
|
|
||||||
>
|
>
|
||||||
<CartesianGrid vertical={false} />
|
<CartesianGrid vertical={false} />
|
||||||
<YAxis
|
<YAxis
|
||||||
@@ -88,10 +81,8 @@ export default function ({
|
|||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
cursor={false}
|
cursor={false}
|
||||||
labelFormatter={formatShortDate}
|
labelFormatter={formatShortDate}
|
||||||
// itemSorter={(item) => {
|
// @ts-ignore
|
||||||
// console.log('itemSorter', item)
|
itemSorter={(a, b) => b.value - a.value}
|
||||||
// return -item.value
|
|
||||||
// }}
|
|
||||||
content={<ChartTooltipContent unit="%" indicator="line" />}
|
content={<ChartTooltipContent unit="%" indicator="line" />}
|
||||||
/>
|
/>
|
||||||
{Object.keys(chartConfig).map((key) => (
|
{Object.keys(chartConfig).map((key) => (
|
||||||
@@ -100,7 +91,7 @@ export default function ({
|
|||||||
// isAnimationActive={false}
|
// isAnimationActive={false}
|
||||||
animateNewValues={false}
|
animateNewValues={false}
|
||||||
dataKey={key}
|
dataKey={key}
|
||||||
type="bump"
|
type="monotone"
|
||||||
fill={chartConfig[key].color}
|
fill={chartConfig[key].color}
|
||||||
fillOpacity={0.4}
|
fillOpacity={0.4}
|
||||||
stroke={chartConfig[key].color}
|
stroke={chartConfig[key].color}
|
||||||
|
@@ -11,14 +11,7 @@ import { useMemo } from 'react'
|
|||||||
import { formatShortDate, formatShortTime } from '@/lib/utils'
|
import { formatShortDate, formatShortTime } from '@/lib/utils'
|
||||||
import Spinner from '../spinner'
|
import Spinner from '../spinner'
|
||||||
|
|
||||||
export default function ({
|
export default function ({ chartData }: { chartData: Record<string, number | string>[] }) {
|
||||||
chartData,
|
|
||||||
max,
|
|
||||||
}: {
|
|
||||||
chartData: Record<string, number | string>[]
|
|
||||||
max: number
|
|
||||||
}) {
|
|
||||||
console.log('max', max)
|
|
||||||
const chartConfig = useMemo(() => {
|
const chartConfig = useMemo(() => {
|
||||||
let config = {} as Record<
|
let config = {} as Record<
|
||||||
string,
|
string,
|
||||||
@@ -63,6 +56,7 @@ export default function ({
|
|||||||
<AreaChart
|
<AreaChart
|
||||||
accessibilityLayer
|
accessibilityLayer
|
||||||
data={chartData}
|
data={chartData}
|
||||||
|
reverseStackOrder={true}
|
||||||
margin={{
|
margin={{
|
||||||
top: 10,
|
top: 10,
|
||||||
}}
|
}}
|
||||||
@@ -71,12 +65,16 @@ export default function ({
|
|||||||
>
|
>
|
||||||
<CartesianGrid vertical={false} />
|
<CartesianGrid vertical={false} />
|
||||||
<YAxis
|
<YAxis
|
||||||
domain={[0, max]}
|
domain={[0, (max: number) => Math.ceil(max)]}
|
||||||
tickCount={9}
|
// tickCount={9}
|
||||||
allowDecimals={false}
|
allowDecimals={false}
|
||||||
tickLine={false}
|
tickLine={false}
|
||||||
axisLine={false}
|
axisLine={false}
|
||||||
tickFormatter={(v) => `${Math.ceil(v / 1024)} GiB`}
|
unit={' GiB'}
|
||||||
|
tickFormatter={(x) => {
|
||||||
|
x = x / 1024
|
||||||
|
return x % 1 === 0 ? x : x.toFixed(1)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<XAxis
|
<XAxis
|
||||||
dataKey="time"
|
dataKey="time"
|
||||||
@@ -89,10 +87,8 @@ export default function ({
|
|||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
cursor={false}
|
cursor={false}
|
||||||
labelFormatter={formatShortDate}
|
labelFormatter={formatShortDate}
|
||||||
// itemSorter={(item) => {
|
// @ts-ignore
|
||||||
// console.log('itemSorter', item)
|
itemSorter={(a, b) => b.value - a.value}
|
||||||
// return -item.value
|
|
||||||
// }}
|
|
||||||
content={<ChartTooltipContent unit=" MiB" indicator="line" />}
|
content={<ChartTooltipContent unit=" MiB" indicator="line" />}
|
||||||
/>
|
/>
|
||||||
{Object.keys(chartConfig).map((key) => (
|
{Object.keys(chartConfig).map((key) => (
|
||||||
@@ -101,7 +97,7 @@ export default function ({
|
|||||||
// isAnimationActive={false}
|
// isAnimationActive={false}
|
||||||
animateNewValues={false}
|
animateNewValues={false}
|
||||||
dataKey={key}
|
dataKey={key}
|
||||||
type="natural"
|
type="monotone"
|
||||||
fill={chartConfig[key].color}
|
fill={chartConfig[key].color}
|
||||||
fillOpacity={0.4}
|
fillOpacity={0.4}
|
||||||
stroke={chartConfig[key].color}
|
stroke={chartConfig[key].color}
|
||||||
|
@@ -19,13 +19,7 @@ const chartConfig = {
|
|||||||
},
|
},
|
||||||
} satisfies ChartConfig
|
} satisfies ChartConfig
|
||||||
|
|
||||||
export default function ({
|
export default function ({ chartData }: { chartData: { time: string; cpu: number }[] }) {
|
||||||
chartData,
|
|
||||||
max,
|
|
||||||
}: {
|
|
||||||
chartData: { time: string; cpu: number }[]
|
|
||||||
max: number
|
|
||||||
}) {
|
|
||||||
if (!chartData?.length) {
|
if (!chartData?.length) {
|
||||||
return <Spinner />
|
return <Spinner />
|
||||||
}
|
}
|
||||||
@@ -35,7 +29,7 @@ export default function ({
|
|||||||
<AreaChart accessibilityLayer data={chartData} margin={{ top: 10 }}>
|
<AreaChart accessibilityLayer data={chartData} margin={{ top: 10 }}>
|
||||||
<CartesianGrid vertical={false} />
|
<CartesianGrid vertical={false} />
|
||||||
<YAxis
|
<YAxis
|
||||||
domain={[0, max]}
|
domain={[0, (max: number) => Math.ceil(max)]}
|
||||||
width={47}
|
width={47}
|
||||||
// tickCount={5}
|
// tickCount={5}
|
||||||
tickLine={false}
|
tickLine={false}
|
||||||
|
@@ -82,7 +82,7 @@ export default function ({
|
|||||||
/>
|
/>
|
||||||
<Area
|
<Area
|
||||||
dataKey="diskUsed"
|
dataKey="diskUsed"
|
||||||
type="bump"
|
type="monotone"
|
||||||
fill="var(--color-diskUsed)"
|
fill="var(--color-diskUsed)"
|
||||||
fillOpacity={0.4}
|
fillOpacity={0.4}
|
||||||
stroke="var(--color-diskUsed)"
|
stroke="var(--color-diskUsed)"
|
||||||
|
@@ -66,7 +66,7 @@ export default function ({
|
|||||||
/>
|
/>
|
||||||
<Area
|
<Area
|
||||||
dataKey="memUsed"
|
dataKey="memUsed"
|
||||||
type="bump"
|
type="monotone"
|
||||||
fill="var(--color-memUsed)"
|
fill="var(--color-memUsed)"
|
||||||
fillOpacity={0.4}
|
fillOpacity={0.4}
|
||||||
stroke="var(--color-memUsed)"
|
stroke="var(--color-memUsed)"
|
||||||
|
@@ -31,14 +31,12 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
const [containers, setContainers] = useState([] as ContainerStatsRecord[])
|
const [containers, setContainers] = useState([] as ContainerStatsRecord[])
|
||||||
|
|
||||||
const [serverStats, setServerStats] = useState([] as SystemStatsRecord[])
|
const [serverStats, setServerStats] = useState([] as SystemStatsRecord[])
|
||||||
const [cpuChartData, setCpuChartData] = useState(
|
const [cpuChartData, setCpuChartData] = useState([] as { time: string; cpu: number }[])
|
||||||
{} as { max: number; data: { time: string; cpu: number }[] }
|
|
||||||
)
|
|
||||||
const [memChartData, setMemChartData] = useState(
|
const [memChartData, setMemChartData] = useState(
|
||||||
{} as { time: string; mem: number; memUsed: number }[]
|
[] as { time: string; mem: number; memUsed: number }[]
|
||||||
)
|
)
|
||||||
const [diskChartData, setDiskChartData] = useState(
|
const [diskChartData, setDiskChartData] = useState(
|
||||||
{} as { time: string; disk: number; diskUsed: number }[]
|
[] as { time: string; disk: number; diskUsed: number }[]
|
||||||
)
|
)
|
||||||
const [containerCpuChartData, setContainerCpuChartData] = useState(
|
const [containerCpuChartData, setContainerCpuChartData] = useState(
|
||||||
[] as Record<string, number | string>[]
|
[] as Record<string, number | string>[]
|
||||||
@@ -51,7 +49,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
document.title = name
|
document.title = name
|
||||||
return () => {
|
return () => {
|
||||||
setContainerCpuChartData([])
|
setContainerCpuChartData([])
|
||||||
setCpuChartData({} as { max: number; data: { time: string; cpu: number }[] })
|
setCpuChartData([])
|
||||||
setMemChartData([])
|
setMemChartData([])
|
||||||
setDiskChartData([])
|
setDiskChartData([])
|
||||||
}
|
}
|
||||||
@@ -70,7 +68,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
sort: '-created',
|
sort: '-created',
|
||||||
})
|
})
|
||||||
.then((records) => {
|
.then((records) => {
|
||||||
console.log('stats', records)
|
// console.log('sctats', records)
|
||||||
setServerStats(records.items)
|
setServerStats(records.items)
|
||||||
})
|
})
|
||||||
}, [server])
|
}, [server])
|
||||||
@@ -80,35 +78,32 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
if (!serverStats.length) {
|
if (!serverStats.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let maxCpu = 0
|
// let maxCpu = 0
|
||||||
const cpuData = [] as { time: string; cpu: number }[]
|
const cpuData = [] as { time: string; cpu: number }[]
|
||||||
const memData = [] as { time: string; mem: number; memUsed: number }[]
|
const memData = [] as { time: string; mem: number; memUsed: number }[]
|
||||||
const diskData = [] as { time: string; disk: number; diskUsed: number }[]
|
const diskData = [] as { time: string; disk: number; diskUsed: number }[]
|
||||||
for (let { created, stats } of serverStats) {
|
for (let { created, stats } of serverStats) {
|
||||||
cpuData.push({ time: created, cpu: stats.c })
|
cpuData.push({ time: created, cpu: stats.c })
|
||||||
maxCpu = Math.max(maxCpu, stats.c)
|
// maxCpu = Math.max(maxCpu, stats.c)
|
||||||
memData.push({ time: created, mem: stats.m, memUsed: stats.mu })
|
memData.push({ time: created, mem: stats.m, memUsed: stats.mu })
|
||||||
diskData.push({ time: created, disk: stats.d, diskUsed: stats.du })
|
diskData.push({ time: created, disk: stats.d, diskUsed: stats.du })
|
||||||
}
|
}
|
||||||
setCpuChartData({
|
setCpuChartData(cpuData.reverse())
|
||||||
max: Math.ceil(maxCpu),
|
|
||||||
data: cpuData.reverse(),
|
|
||||||
})
|
|
||||||
setMemChartData(memData.reverse())
|
setMemChartData(memData.reverse())
|
||||||
setDiskChartData(diskData.reverse())
|
setDiskChartData(diskData.reverse())
|
||||||
}, [serverStats])
|
}, [serverStats])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if ($servers.get().length === 0) {
|
if ($servers.get().length === 0) {
|
||||||
console.log('skipping')
|
// console.log('skipping')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log('running')
|
// console.log('running')
|
||||||
const matchingServer = servers.find((s) => s.name === name) as SystemRecord
|
const matchingServer = servers.find((s) => s.name === name) as SystemRecord
|
||||||
|
|
||||||
setServer(matchingServer)
|
setServer(matchingServer)
|
||||||
|
|
||||||
console.log('matchingServer', matchingServer)
|
console.log('found server', matchingServer)
|
||||||
// pb.collection<SystemRecord>('systems')
|
// pb.collection<SystemRecord>('systems')
|
||||||
// .getOne(serverId)
|
// .getOne(serverId)
|
||||||
// .then((record) => {
|
// .then((record) => {
|
||||||
@@ -129,7 +124,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
|
|
||||||
// container stats for charts
|
// container stats for charts
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('containers', containers)
|
// console.log('containers', containers)
|
||||||
const containerCpuData = [] as Record<string, number | string>[]
|
const containerCpuData = [] as Record<string, number | string>[]
|
||||||
const containerMemData = [] as Record<string, number | string>[]
|
const containerMemData = [] as Record<string, number | string>[]
|
||||||
|
|
||||||
@@ -153,7 +148,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
<Card className="pb-2">
|
<Card className="pb-2">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex gap-2 justify-between">
|
<CardTitle className="flex gap-2 justify-between">
|
||||||
<span>CPU Usage</span>
|
<span>Total CPU Usage</span>
|
||||||
<CpuIcon className="opacity-70" />
|
<CpuIcon className="opacity-70" />
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
@@ -162,7 +157,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className={'pl-1 w-[calc(100%-2em)] h-52 relative'}>
|
<CardContent className={'pl-1 w-[calc(100%-2em)] h-52 relative'}>
|
||||||
<Suspense fallback={<Spinner />}>
|
<Suspense fallback={<Spinner />}>
|
||||||
<CpuChart chartData={cpuChartData.data} max={cpuChartData.max} />
|
<CpuChart chartData={cpuChartData} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -177,15 +172,15 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className={'pl-1 w-[calc(100%-2em)] h-52 relative'}>
|
<CardContent className={'pl-1 w-[calc(100%-2em)] h-52 relative'}>
|
||||||
<Suspense fallback={<Spinner />}>
|
<Suspense fallback={<Spinner />}>
|
||||||
<ContainerCpuChart chartData={containerCpuChartData} max={cpuChartData.max} />
|
<ContainerCpuChart chartData={containerCpuChartData} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
<Card className="pb-2">
|
<Card className="pb-2">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Memory Usage</CardTitle>
|
<CardTitle>Total Memory Usage</CardTitle>
|
||||||
<CardDescription>Precise usage at the recorded time</CardDescription>
|
<CardDescription>Precise utilization at the recorded time</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className={'pl-1 w-[calc(100%-2em)] h-52 relative'}>
|
<CardContent className={'pl-1 w-[calc(100%-2em)] h-52 relative'}>
|
||||||
<Suspense fallback={<Spinner />}>
|
<Suspense fallback={<Spinner />}>
|
||||||
@@ -204,12 +199,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className={'pl-1 w-[calc(100%-2em)] h-52 relative'}>
|
<CardContent className={'pl-1 w-[calc(100%-2em)] h-52 relative'}>
|
||||||
<Suspense fallback={<Spinner />}>
|
<Suspense fallback={<Spinner />}>
|
||||||
{server?.stats?.m && (
|
{server?.stats?.m && <ContainerMemChart chartData={containerMemChartData} />}
|
||||||
<ContainerMemChart
|
|
||||||
chartData={containerMemChartData}
|
|
||||||
max={server.stats.m * 1024}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
@@ -118,11 +118,21 @@ const ChartTooltipContent = React.forwardRef<
|
|||||||
nameKey,
|
nameKey,
|
||||||
labelKey,
|
labelKey,
|
||||||
unit,
|
unit,
|
||||||
|
itemSorter,
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const { config } = useChart()
|
const { config } = useChart()
|
||||||
|
|
||||||
|
payload = React.useMemo(() => {
|
||||||
|
if (itemSorter) {
|
||||||
|
return payload.sort(itemSorter)
|
||||||
|
}
|
||||||
|
return payload
|
||||||
|
}, [itemSorter, payload])
|
||||||
|
|
||||||
|
// console.log('iiiiii', itemSorter)
|
||||||
|
|
||||||
const tooltipLabel = React.useMemo(() => {
|
const tooltipLabel = React.useMemo(() => {
|
||||||
if (hideLabel || !payload?.length) {
|
if (hideLabel || !payload?.length) {
|
||||||
return null
|
return null
|
||||||
|
Reference in New Issue
Block a user