mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 01:39:34 +08:00
lazy load charts and disable chart animations
This commit is contained in:
Binary file not shown.
@@ -37,6 +37,7 @@
|
||||
"recharts": "^2.13.0-alpha.4",
|
||||
"tailwind-merge": "^2.4.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"use-is-in-viewport": "^1.0.9",
|
||||
"valibot": "^0.36.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts'
|
||||
|
||||
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'
|
||||
import { chartTimeData, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
import Spinner from '../spinner'
|
||||
import { chartTimeData, cn, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
// import Spinner from '../spinner'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $chartTime } from '@/lib/stores'
|
||||
import { SystemStatsRecord } from '@/types'
|
||||
import { useRef } from 'react'
|
||||
import { useMemo, useRef } from 'react'
|
||||
|
||||
export default function BandwidthChart({
|
||||
ticks,
|
||||
@@ -19,13 +19,17 @@ export default function BandwidthChart({
|
||||
const yAxisWidth = useYaxisWidth(chartRef)
|
||||
const chartTime = useStore($chartTime)
|
||||
|
||||
if (!systemData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
const yAxisSet = useMemo(() => yAxisWidth !== 180, [yAxisWidth])
|
||||
|
||||
return (
|
||||
<div ref={chartRef}>
|
||||
<ChartContainer config={{}} className="h-full w-full absolute aspect-auto">
|
||||
{/* {!yAxisSet && <Spinner />} */}
|
||||
<ChartContainer
|
||||
config={{}}
|
||||
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
|
||||
'opacity-100': yAxisSet,
|
||||
})}
|
||||
>
|
||||
<AreaChart
|
||||
accessibilityLayer
|
||||
data={systemData}
|
||||
@@ -80,7 +84,8 @@ export default function BandwidthChart({
|
||||
fill="hsl(var(--chart-5))"
|
||||
fillOpacity={0.4}
|
||||
stroke="hsl(var(--chart-5))"
|
||||
animationDuration={1200}
|
||||
// animationDuration={1200}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<Area
|
||||
dataKey="stats.nr"
|
||||
@@ -89,7 +94,8 @@ export default function BandwidthChart({
|
||||
fill="hsl(var(--chart-2))"
|
||||
fillOpacity={0.4}
|
||||
stroke="hsl(var(--chart-2))"
|
||||
animationDuration={1200}
|
||||
// animationDuration={1200}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
|
@@ -6,8 +6,8 @@ import {
|
||||
ChartTooltipContent,
|
||||
} from '@/components/ui/chart'
|
||||
import { useMemo, useRef } from 'react'
|
||||
import { chartTimeData, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
import Spinner from '../spinner'
|
||||
import { chartTimeData, cn, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
// import Spinner from '../spinner'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $chartTime } from '@/lib/stores'
|
||||
|
||||
@@ -22,6 +22,8 @@ export default function ContainerCpuChart({
|
||||
const yAxisWidth = useYaxisWidth(chartRef)
|
||||
const chartTime = useStore($chartTime)
|
||||
|
||||
const yAxisSet = useMemo(() => yAxisWidth !== 180, [yAxisWidth])
|
||||
|
||||
const chartConfig = useMemo(() => {
|
||||
let config = {} as Record<
|
||||
string,
|
||||
@@ -57,13 +59,19 @@ export default function ContainerCpuChart({
|
||||
return config satisfies ChartConfig
|
||||
}, [chartData])
|
||||
|
||||
if (!chartData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
// if (!chartData.length || !ticks.length) {
|
||||
// return <Spinner />
|
||||
// }
|
||||
|
||||
return (
|
||||
<div ref={chartRef}>
|
||||
<ChartContainer config={chartConfig} className="h-full w-full absolute aspect-auto">
|
||||
{/* {!yAxisSet && <Spinner />} */}
|
||||
<ChartContainer
|
||||
config={{}}
|
||||
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
|
||||
'opacity-100': yAxisSet,
|
||||
})}
|
||||
>
|
||||
<AreaChart
|
||||
accessibilityLayer
|
||||
data={chartData}
|
||||
@@ -105,8 +113,9 @@ export default function ContainerCpuChart({
|
||||
<Area
|
||||
key={key}
|
||||
// isAnimationActive={chartData.length < 20}
|
||||
animateNewValues={false}
|
||||
animationDuration={1200}
|
||||
isAnimationActive={false}
|
||||
// animateNewValues={false}
|
||||
// animationDuration={1200}
|
||||
dataKey={key}
|
||||
type="monotoneX"
|
||||
fill={chartConfig[key].color}
|
||||
|
@@ -6,8 +6,8 @@ import {
|
||||
ChartTooltipContent,
|
||||
} from '@/components/ui/chart'
|
||||
import { useMemo, useRef } from 'react'
|
||||
import { chartTimeData, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
import Spinner from '../spinner'
|
||||
import { chartTimeData, cn, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
// import Spinner from '../spinner'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $chartTime } from '@/lib/stores'
|
||||
|
||||
@@ -18,9 +18,11 @@ export default function ContainerMemChart({
|
||||
chartData: Record<string, number | string>[]
|
||||
ticks: number[]
|
||||
}) {
|
||||
const chartTime = useStore($chartTime)
|
||||
const chartRef = useRef<HTMLDivElement>(null)
|
||||
const yAxisWidth = useYaxisWidth(chartRef)
|
||||
const chartTime = useStore($chartTime)
|
||||
|
||||
const yAxisSet = useMemo(() => yAxisWidth !== 180, [yAxisWidth])
|
||||
|
||||
const chartConfig = useMemo(() => {
|
||||
let config = {} as Record<
|
||||
@@ -57,13 +59,19 @@ export default function ContainerMemChart({
|
||||
return config satisfies ChartConfig
|
||||
}, [chartData])
|
||||
|
||||
if (!chartData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
// if (!chartData.length || !ticks.length) {
|
||||
// return <Spinner />
|
||||
// }
|
||||
|
||||
return (
|
||||
<div ref={chartRef}>
|
||||
<ChartContainer config={chartConfig} className="h-full w-full absolute aspect-auto">
|
||||
{/* {!yAxisSet && <Spinner />} */}
|
||||
<ChartContainer
|
||||
config={{}}
|
||||
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
|
||||
'opacity-100': yAxisSet,
|
||||
})}
|
||||
>
|
||||
<AreaChart
|
||||
accessibilityLayer
|
||||
data={chartData}
|
||||
@@ -71,8 +79,6 @@ export default function ContainerMemChart({
|
||||
margin={{
|
||||
top: 10,
|
||||
}}
|
||||
|
||||
// reverseStackOrder={true}
|
||||
>
|
||||
<CartesianGrid vertical={false} />
|
||||
<YAxis
|
||||
@@ -109,9 +115,8 @@ export default function ContainerMemChart({
|
||||
{Object.keys(chartConfig).map((key) => (
|
||||
<Area
|
||||
key={key}
|
||||
isAnimationActive={chartData.length < 20}
|
||||
animateNewValues={false}
|
||||
animationDuration={1200}
|
||||
// animationDuration={1200}
|
||||
isAnimationActive={false}
|
||||
dataKey={key}
|
||||
type="monotoneX"
|
||||
fill={chartConfig[key].color}
|
||||
|
@@ -6,8 +6,8 @@ import {
|
||||
ChartTooltipContent,
|
||||
} from '@/components/ui/chart'
|
||||
import { useMemo, useRef } from 'react'
|
||||
import { chartTimeData, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
import Spinner from '../spinner'
|
||||
import { chartTimeData, cn, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
// import Spinner from '../spinner'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $chartTime } from '@/lib/stores'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
@@ -19,9 +19,11 @@ export default function ContainerCpuChart({
|
||||
chartData: Record<string, number | number[]>[]
|
||||
ticks: number[]
|
||||
}) {
|
||||
const chartTime = useStore($chartTime)
|
||||
const chartRef = useRef<HTMLDivElement>(null)
|
||||
const yAxisWidth = useYaxisWidth(chartRef)
|
||||
const chartTime = useStore($chartTime)
|
||||
|
||||
const yAxisSet = useMemo(() => yAxisWidth !== 180, [yAxisWidth])
|
||||
|
||||
const chartConfig = useMemo(() => {
|
||||
let config = {} as Record<
|
||||
@@ -57,13 +59,19 @@ export default function ContainerCpuChart({
|
||||
return config satisfies ChartConfig
|
||||
}, [chartData])
|
||||
|
||||
if (!chartData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
// if (!chartData.length || !ticks.length) {
|
||||
// return <Spinner />
|
||||
// }
|
||||
|
||||
return (
|
||||
<div ref={chartRef}>
|
||||
<ChartContainer config={{}} className="h-full w-full absolute aspect-auto">
|
||||
{/* {!yAxisSet && <Spinner />} */}
|
||||
<ChartContainer
|
||||
config={{}}
|
||||
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
|
||||
'opacity-100': yAxisSet,
|
||||
})}
|
||||
>
|
||||
<AreaChart
|
||||
accessibilityLayer
|
||||
data={chartData}
|
||||
@@ -134,9 +142,8 @@ export default function ContainerCpuChart({
|
||||
<Area
|
||||
key={key}
|
||||
name={key}
|
||||
// isAnimationActive={chartData.length < 20}
|
||||
animateNewValues={false}
|
||||
animationDuration={1200}
|
||||
// animationDuration={1200}
|
||||
isAnimationActive={false}
|
||||
dataKey={(data) => data?.[key]?.[2] ?? 0}
|
||||
type="monotoneX"
|
||||
fill={chartConfig[key].color}
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts'
|
||||
|
||||
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'
|
||||
import { chartTimeData, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
import Spinner from '../spinner'
|
||||
import { chartTimeData, cn, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
// import Spinner from '../spinner'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $chartTime } from '@/lib/stores'
|
||||
import { SystemStatsRecord } from '@/types'
|
||||
import { useRef } from 'react'
|
||||
import { useMemo, useRef } from 'react'
|
||||
|
||||
export default function CpuChart({
|
||||
ticks,
|
||||
@@ -15,17 +15,24 @@ export default function CpuChart({
|
||||
ticks: number[]
|
||||
systemData: SystemStatsRecord[]
|
||||
}) {
|
||||
const chartTime = useStore($chartTime)
|
||||
const chartRef = useRef<HTMLDivElement>(null)
|
||||
const yAxisWidth = useYaxisWidth(chartRef)
|
||||
const chartTime = useStore($chartTime)
|
||||
|
||||
if (!systemData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
const yAxisSet = useMemo(() => yAxisWidth !== 180, [yAxisWidth])
|
||||
|
||||
// if (!systemData.length || !ticks.length) {
|
||||
// return <Spinner />
|
||||
// }
|
||||
|
||||
return (
|
||||
<div ref={chartRef}>
|
||||
<ChartContainer config={{}} className="h-full w-full absolute aspect-auto">
|
||||
<ChartContainer
|
||||
config={{}}
|
||||
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
|
||||
'opacity-100': yAxisSet,
|
||||
})}
|
||||
>
|
||||
<AreaChart accessibilityLayer data={systemData} margin={{ top: 10 }}>
|
||||
<CartesianGrid vertical={false} />
|
||||
<YAxis
|
||||
@@ -64,9 +71,10 @@ export default function CpuChart({
|
||||
fill="hsl(var(--chart-1))"
|
||||
fillOpacity={0.4}
|
||||
stroke="hsl(var(--chart-1))"
|
||||
animationDuration={1200}
|
||||
isAnimationActive={false}
|
||||
// animationEasing="ease-out"
|
||||
// animateNewValues={false}
|
||||
animationDuration={700}
|
||||
animateNewValues={true}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts'
|
||||
|
||||
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'
|
||||
import { chartTimeData, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
import { chartTimeData, cn, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
import { useMemo, useRef } from 'react'
|
||||
import Spinner from '../spinner'
|
||||
// import Spinner from '../spinner'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $chartTime } from '@/lib/stores'
|
||||
import { SystemStatsRecord } from '@/types'
|
||||
@@ -15,9 +15,11 @@ export default function DiskChart({
|
||||
ticks: number[]
|
||||
systemData: SystemStatsRecord[]
|
||||
}) {
|
||||
const chartTime = useStore($chartTime)
|
||||
const chartRef = useRef<HTMLDivElement>(null)
|
||||
const yAxisWidth = useYaxisWidth(chartRef)
|
||||
const chartTime = useStore($chartTime)
|
||||
|
||||
const yAxisSet = useMemo(() => yAxisWidth !== 180, [yAxisWidth])
|
||||
|
||||
const diskSize = useMemo(() => {
|
||||
return Math.round(systemData[0]?.stats.d)
|
||||
@@ -32,13 +34,19 @@ export default function DiskChart({
|
||||
// return ticks
|
||||
// }, [diskSize])
|
||||
|
||||
if (!systemData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
// if (!systemData.length || !ticks.length) {
|
||||
// return <Spinner />
|
||||
// }
|
||||
|
||||
return (
|
||||
<div ref={chartRef}>
|
||||
<ChartContainer config={{}} className="h-full w-full absolute aspect-auto">
|
||||
{/* {!yAxisSet && <Spinner />} */}
|
||||
<ChartContainer
|
||||
config={{}}
|
||||
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
|
||||
'opacity-100': yAxisSet,
|
||||
})}
|
||||
>
|
||||
<AreaChart
|
||||
accessibilityLayer
|
||||
data={systemData}
|
||||
@@ -88,7 +96,8 @@ export default function DiskChart({
|
||||
fill="hsl(var(--chart-4))"
|
||||
fillOpacity={0.4}
|
||||
stroke="hsl(var(--chart-4))"
|
||||
animationDuration={1200}
|
||||
// animationDuration={1200}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts'
|
||||
|
||||
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'
|
||||
import { chartTimeData, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
import Spinner from '../spinner'
|
||||
import { chartTimeData, cn, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
// import Spinner from '../spinner'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $chartTime } from '@/lib/stores'
|
||||
import { SystemStatsRecord } from '@/types'
|
||||
import { useRef } from 'react'
|
||||
import { useMemo, useRef } from 'react'
|
||||
|
||||
export default function DiskIoChart({
|
||||
ticks,
|
||||
@@ -15,17 +15,25 @@ export default function DiskIoChart({
|
||||
ticks: number[]
|
||||
systemData: SystemStatsRecord[]
|
||||
}) {
|
||||
const chartTime = useStore($chartTime)
|
||||
const chartRef = useRef<HTMLDivElement>(null)
|
||||
const yAxisWidth = useYaxisWidth(chartRef)
|
||||
const chartTime = useStore($chartTime)
|
||||
|
||||
if (!systemData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
const yAxisSet = useMemo(() => yAxisWidth !== 180, [yAxisWidth])
|
||||
|
||||
// if (!systemData.length || !ticks.length) {
|
||||
// return <Spinner />
|
||||
// }
|
||||
|
||||
return (
|
||||
<div ref={chartRef}>
|
||||
<ChartContainer config={{}} className="h-full w-full absolute aspect-auto">
|
||||
{/* {!yAxisSet && <Spinner />} */}
|
||||
<ChartContainer
|
||||
config={{}}
|
||||
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
|
||||
'opacity-100': yAxisSet,
|
||||
})}
|
||||
>
|
||||
<AreaChart
|
||||
accessibilityLayer
|
||||
data={systemData}
|
||||
@@ -80,7 +88,8 @@ export default function DiskIoChart({
|
||||
fill="hsl(var(--chart-3))"
|
||||
fillOpacity={0.4}
|
||||
stroke="hsl(var(--chart-3))"
|
||||
animationDuration={1200}
|
||||
// animationDuration={1200}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<Area
|
||||
dataKey="stats.dr"
|
||||
@@ -89,7 +98,8 @@ export default function DiskIoChart({
|
||||
fill="hsl(var(--chart-1))"
|
||||
fillOpacity={0.4}
|
||||
stroke="hsl(var(--chart-1))"
|
||||
animationDuration={1200}
|
||||
// animationDuration={1200}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts'
|
||||
|
||||
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'
|
||||
import { chartTimeData, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
import { chartTimeData, cn, formatShortDate, useYaxisWidth } from '@/lib/utils'
|
||||
import { useMemo, useRef } from 'react'
|
||||
import Spinner from '../spinner'
|
||||
// import Spinner from '../spinner'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $chartTime } from '@/lib/stores'
|
||||
import { SystemStatsRecord } from '@/types'
|
||||
@@ -19,18 +19,26 @@ export default function MemChart({
|
||||
const chartRef = useRef<HTMLDivElement>(null)
|
||||
const yAxisWidth = useYaxisWidth(chartRef)
|
||||
|
||||
const yAxisSet = useMemo(() => yAxisWidth !== 180, [yAxisWidth])
|
||||
|
||||
const totalMem = useMemo(() => {
|
||||
const maxMem = Math.ceil(systemData[0]?.stats.m)
|
||||
return maxMem > 2 && maxMem % 2 !== 0 ? maxMem + 1 : maxMem
|
||||
}, [systemData])
|
||||
|
||||
if (!systemData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
// if (!systemData.length || !ticks.length) {
|
||||
// return <Spinner />
|
||||
// }
|
||||
|
||||
return (
|
||||
<div ref={chartRef}>
|
||||
<ChartContainer config={{}} className="h-full w-full absolute aspect-auto">
|
||||
{/* {!yAxisSet && <Spinner />} */}
|
||||
<ChartContainer
|
||||
config={{}}
|
||||
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
|
||||
'opacity-100': yAxisSet,
|
||||
})}
|
||||
>
|
||||
<AreaChart
|
||||
accessibilityLayer
|
||||
data={systemData}
|
||||
@@ -80,7 +88,8 @@ export default function MemChart({
|
||||
fillOpacity={0.4}
|
||||
stroke="hsl(var(--chart-2))"
|
||||
stackId="a"
|
||||
animationDuration={1200}
|
||||
// animationDuration={1200}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<Area
|
||||
dataKey="stats.mb"
|
||||
@@ -91,7 +100,8 @@ export default function MemChart({
|
||||
strokeOpacity={0.3}
|
||||
stroke="hsl(var(--chart-2))"
|
||||
stackId="a"
|
||||
animationDuration={1200}
|
||||
// animationDuration={1200}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import { $updatedSystem, $systems, pb, $chartTime } from '@/lib/stores'
|
||||
import { ContainerStatsRecord, SystemRecord, SystemStatsRecord } from '@/types'
|
||||
import { Suspense, lazy, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '../ui/card'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import Spinner from '../spinner'
|
||||
import { ClockArrowUp, CpuIcon, GlobeIcon } from 'lucide-react'
|
||||
import ChartTimeSelect from '../charts/chart-time-select'
|
||||
import { chartTimeData, cn, getPbTimestamp } from '@/lib/utils'
|
||||
import { chartTimeData, cn, getPbTimestamp, useClampedIsInViewport } from '@/lib/utils'
|
||||
import { Separator } from '../ui/separator'
|
||||
import { scaleTime } from 'd3-scale'
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'
|
||||
@@ -27,15 +27,10 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
const [ticks, setTicks] = useState([] as number[])
|
||||
const [server, setServer] = useState({} as SystemRecord)
|
||||
const [systemStats, setSystemStats] = useState([] as SystemStatsRecord[])
|
||||
const [dockerCpuChartData, setDockerCpuChartData] = useState(
|
||||
[] as Record<string, number | string>[]
|
||||
)
|
||||
const [dockerMemChartData, setDockerMemChartData] = useState(
|
||||
[] as Record<string, number | string>[]
|
||||
)
|
||||
const [dockerNetChartData, setDockerNetChartData] = useState(
|
||||
[] as Record<string, number | number[]>[]
|
||||
)
|
||||
const [dockerCpuChartData, setDockerCpuChartData] = useState<Record<string, number | string>[]>()
|
||||
const [dockerMemChartData, setDockerMemChartData] = useState<Record<string, number | string>[]>()
|
||||
const [dockerNetChartData, setDockerNetChartData] =
|
||||
useState<Record<string, number | number[]>[]>()
|
||||
|
||||
useEffect(() => {
|
||||
document.title = `${name} / Beszel`
|
||||
@@ -47,9 +42,9 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
|
||||
const resetCharts = useCallback(() => {
|
||||
setSystemStats([])
|
||||
setDockerCpuChartData([])
|
||||
setDockerMemChartData([])
|
||||
setDockerNetChartData([])
|
||||
setDockerCpuChartData(undefined)
|
||||
setDockerMemChartData(undefined)
|
||||
setDockerNetChartData(undefined)
|
||||
}, [])
|
||||
|
||||
useEffect(resetCharts, [chartTime])
|
||||
@@ -121,7 +116,9 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
sort: 'created',
|
||||
})
|
||||
.then((records) => {
|
||||
makeContainerData(records)
|
||||
if (records.length) {
|
||||
makeContainerData(records)
|
||||
}
|
||||
// setContainers(records)
|
||||
})
|
||||
}, [server, chartTime])
|
||||
@@ -129,15 +126,14 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
// container stats for charts
|
||||
const makeContainerData = useCallback((containers: ContainerStatsRecord[]) => {
|
||||
// console.log('containers', containers)
|
||||
const dockerCpuData = [] as typeof dockerCpuChartData
|
||||
const dockerMemData = [] as typeof dockerMemChartData
|
||||
const dockerNetData = [] as typeof dockerNetChartData
|
||||
|
||||
const dockerCpuData = []
|
||||
const dockerMemData = []
|
||||
const dockerNetData = []
|
||||
for (let { created, stats } of containers) {
|
||||
const time = new Date(created).getTime()
|
||||
let cpuData = { time } as (typeof dockerCpuChartData)[0]
|
||||
let memData = { time } as (typeof dockerMemChartData)[0]
|
||||
let netData = { time } as (typeof dockerNetChartData)[0]
|
||||
let cpuData = { time } as Record<string, number | string>
|
||||
let memData = { time } as Record<string, number | string>
|
||||
let netData = { time } as Record<string, number | number[]>
|
||||
for (let container of stats) {
|
||||
cpuData[container.n] = container.c
|
||||
memData[container.n] = container.m
|
||||
@@ -225,7 +221,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
<CpuChart ticks={ticks} systemData={systemStats} />
|
||||
</ChartCard>
|
||||
|
||||
{dockerCpuChartData.length > 0 && (
|
||||
{dockerCpuChartData && (
|
||||
<ChartCard title="Docker CPU Usage" description="CPU utilization of docker containers">
|
||||
<ContainerCpuChart chartData={dockerCpuChartData} ticks={ticks} />
|
||||
</ChartCard>
|
||||
@@ -235,7 +231,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
<MemChart ticks={ticks} systemData={systemStats} />
|
||||
</ChartCard>
|
||||
|
||||
{dockerMemChartData.length > 0 && (
|
||||
{dockerMemChartData && (
|
||||
<ChartCard title="Docker Memory Usage" description="Memory usage of docker containers">
|
||||
<ContainerMemChart chartData={dockerMemChartData} ticks={ticks} />
|
||||
</ChartCard>
|
||||
@@ -256,7 +252,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
<BandwidthChart ticks={ticks} systemData={systemStats} />
|
||||
</ChartCard>
|
||||
|
||||
{dockerNetChartData.length > 0 && (
|
||||
{dockerNetChartData && (
|
||||
<ChartCard
|
||||
title="Docker Network I/O"
|
||||
description="Includes traffic between internal services"
|
||||
@@ -277,8 +273,10 @@ function ChartCard({
|
||||
description: string
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const target = useRef<HTMLDivElement>(null)
|
||||
const [isInViewport, wrappedTargetRef] = useClampedIsInViewport({ target: target })
|
||||
return (
|
||||
<Card className="pb-2 sm:pb-4 col-span-full">
|
||||
<Card className="pb-2 sm:pb-4 col-span-full" ref={wrappedTargetRef}>
|
||||
<CardHeader className="pb-5 pt-4 relative space-y-1 max-sm:py-3 max-sm:px-4">
|
||||
<CardTitle className="text-xl sm:text-2xl">{title}</CardTitle>
|
||||
<CardDescription>{description}</CardDescription>
|
||||
@@ -287,7 +285,8 @@ function ChartCard({
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="pl-1 w-[calc(100%-1.6em)] h-52 relative">
|
||||
<Suspense fallback={<Spinner />}>{children}</Suspense>
|
||||
{<Spinner />}
|
||||
{isInViewport && <Suspense>{children}</Suspense>}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
|
@@ -7,6 +7,7 @@ import { RecordModel, RecordSubscription } from 'pocketbase'
|
||||
import { WritableAtom } from 'nanostores'
|
||||
import { timeDay, timeHour } from 'd3-time'
|
||||
import { useEffect, useState } from 'react'
|
||||
import useIsInViewport, { CallbackRef, HookOptions } from 'use-is-in-viewport'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
@@ -183,17 +184,36 @@ export const chartTimeData: ChartTimeData = {
|
||||
|
||||
/** Hacky solution to set the correct width of the yAxis in recharts */
|
||||
export function useYaxisWidth(chartRef: React.RefObject<HTMLDivElement>) {
|
||||
const [yAxisWidth, setYAxisWidth] = useState(90)
|
||||
const [yAxisWidth, setYAxisWidth] = useState(180)
|
||||
useEffect(() => {
|
||||
let interval = setInterval(() => {
|
||||
// console.log('chartRef', chartRef.current)
|
||||
const yAxisElement = chartRef?.current?.querySelector('.yAxis')
|
||||
if (yAxisElement) {
|
||||
console.log('yAxisElement', yAxisElement)
|
||||
// console.log('yAxisElement', yAxisElement)
|
||||
setYAxisWidth(yAxisElement.getBoundingClientRect().width + 22)
|
||||
clearInterval(interval)
|
||||
}
|
||||
}, 16)
|
||||
}, 0)
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
return yAxisWidth
|
||||
}
|
||||
|
||||
export function useClampedIsInViewport(options: HookOptions): [boolean | null, CallbackRef] {
|
||||
const [isInViewport, wrappedTargetRef] = useIsInViewport(options)
|
||||
const [wasInViewportAtleastOnce, setWasInViewportAtleastOnce] = useState(isInViewport)
|
||||
|
||||
useEffect(() => {
|
||||
setWasInViewportAtleastOnce((prev) => {
|
||||
// this will clamp it to the first true
|
||||
// received from useIsInViewport
|
||||
if (!prev) {
|
||||
return isInViewport
|
||||
}
|
||||
return prev
|
||||
})
|
||||
}, [isInViewport])
|
||||
|
||||
return [wasInViewportAtleastOnce, wrappedTargetRef]
|
||||
}
|
||||
|
Reference in New Issue
Block a user