mirror of
https://github.com/fankes/beszel.git
synced 2025-10-20 18:29:29 +08:00
updates
This commit is contained in:
@@ -6,7 +6,8 @@ import {
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { $chartTime } from '@/lib/stores'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { chartTimeData, cn } from '@/lib/utils'
|
||||
import { ChartTimes } from '@/types'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
@@ -19,16 +20,20 @@ export default function ChartTimeSelect({ className }: { className?: string }) {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Select defaultValue="1h" value={chartTime} onValueChange={(value) => $chartTime.set(value)}>
|
||||
<Select
|
||||
defaultValue="1h"
|
||||
value={chartTime}
|
||||
onValueChange={(value: ChartTimes) => $chartTime.set(value)}
|
||||
>
|
||||
<SelectTrigger className={cn(className, 'w-40 px-5')}>
|
||||
<SelectValue placeholder="1h" />
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1h">1 hour</SelectItem>
|
||||
<SelectItem value="12h">12 hours</SelectItem>
|
||||
<SelectItem value="24h">24 hours</SelectItem>
|
||||
<SelectItem value="1w">1 week</SelectItem>
|
||||
<SelectItem value="30d">30 days</SelectItem>
|
||||
{Object.entries(chartTimeData).map(([value, { label }]) => (
|
||||
<SelectItem key={label} value={value}>
|
||||
{label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)
|
||||
|
@@ -8,14 +8,20 @@ import {
|
||||
ChartTooltipContent,
|
||||
} from '@/components/ui/chart'
|
||||
import { useMemo } from 'react'
|
||||
import { calculateXaxisTicks, formatShortDate, formatShortTime } from '@/lib/utils'
|
||||
import { formatShortDate, hourWithMinutes } from '@/lib/utils'
|
||||
import Spinner from '../spinner'
|
||||
|
||||
export default function ContainerCpuChart({
|
||||
chartData,
|
||||
ticks,
|
||||
}: {
|
||||
chartData: Record<string, number | string>[]
|
||||
ticks: number[]
|
||||
}) {
|
||||
if (!chartData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
|
||||
const chartConfig = useMemo(() => {
|
||||
let config = {} as Record<
|
||||
string,
|
||||
@@ -45,18 +51,12 @@ export default function ContainerCpuChart({
|
||||
const hue = ((i * 360) / length) % 360
|
||||
config[key] = {
|
||||
label: key,
|
||||
color: `hsl(${hue}, 60%, 60%)`,
|
||||
color: `hsl(${hue}, 60%, 55%)`,
|
||||
}
|
||||
}
|
||||
return config satisfies ChartConfig
|
||||
}, [chartData])
|
||||
|
||||
const ticks = useMemo(() => calculateXaxisTicks(chartData), [chartData])
|
||||
|
||||
if (!chartData.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
|
||||
return (
|
||||
<ChartContainer config={chartConfig} className="h-full w-full absolute aspect-auto">
|
||||
<AreaChart
|
||||
@@ -86,7 +86,7 @@ export default function ContainerCpuChart({
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
minTickGap={30}
|
||||
tickFormatter={formatShortTime}
|
||||
tickFormatter={hourWithMinutes}
|
||||
/>
|
||||
<ChartTooltip
|
||||
// cursor={false}
|
||||
@@ -100,9 +100,10 @@ export default function ContainerCpuChart({
|
||||
{Object.keys(chartConfig).map((key) => (
|
||||
<Area
|
||||
key={key}
|
||||
// isAnimationActive={chartData.length < 20}
|
||||
animateNewValues={false}
|
||||
dataKey={key}
|
||||
type="monotone"
|
||||
type="monotoneX"
|
||||
fill={chartConfig[key].color}
|
||||
fillOpacity={0.4}
|
||||
stroke={chartConfig[key].color}
|
||||
|
@@ -8,11 +8,17 @@ import {
|
||||
ChartTooltipContent,
|
||||
} from '@/components/ui/chart'
|
||||
import { useMemo } from 'react'
|
||||
import { calculateXaxisTicks, formatShortDate, formatShortTime } from '@/lib/utils'
|
||||
import { formatShortDate, hourWithMinutes } from '@/lib/utils'
|
||||
import Spinner from '../spinner'
|
||||
|
||||
export default function ({ chartData }: { chartData: Record<string, number | string>[] }) {
|
||||
if (!chartData.length) {
|
||||
export default function ({
|
||||
chartData,
|
||||
ticks,
|
||||
}: {
|
||||
chartData: Record<string, number | string>[]
|
||||
ticks: number[]
|
||||
}) {
|
||||
if (!chartData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
|
||||
@@ -45,14 +51,12 @@ export default function ({ chartData }: { chartData: Record<string, number | str
|
||||
const hue = ((i * 360) / length) % 360
|
||||
config[key] = {
|
||||
label: key,
|
||||
color: `hsl(${hue}, 60%, 60%)`,
|
||||
color: `hsl(${hue}, 60%, 55%)`,
|
||||
}
|
||||
}
|
||||
return config satisfies ChartConfig
|
||||
}, [chartData])
|
||||
|
||||
const ticks = useMemo(() => calculateXaxisTicks(chartData), [chartData])
|
||||
|
||||
return (
|
||||
<ChartContainer config={chartConfig} className="h-full w-full absolute aspect-auto">
|
||||
<AreaChart
|
||||
@@ -72,7 +76,7 @@ export default function ({ chartData }: { chartData: Record<string, number | str
|
||||
allowDecimals={false}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
unit={' GiB'}
|
||||
unit={' GB'}
|
||||
tickFormatter={(x) => {
|
||||
x = x / 1024
|
||||
return x % 1 === 0 ? x : x.toFixed(1)
|
||||
@@ -88,7 +92,7 @@ export default function ({ chartData }: { chartData: Record<string, number | str
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
minTickGap={30}
|
||||
tickFormatter={formatShortTime}
|
||||
tickFormatter={hourWithMinutes}
|
||||
/>
|
||||
<ChartTooltip
|
||||
// cursor={false}
|
||||
@@ -102,10 +106,10 @@ export default function ({ chartData }: { chartData: Record<string, number | str
|
||||
{Object.keys(chartConfig).map((key) => (
|
||||
<Area
|
||||
key={key}
|
||||
// isAnimationActive={false}
|
||||
isAnimationActive={chartData.length < 20}
|
||||
animateNewValues={false}
|
||||
dataKey={key}
|
||||
type="monotone"
|
||||
type="monotoneX"
|
||||
fill={chartConfig[key].color}
|
||||
fillOpacity={0.4}
|
||||
stroke={chartConfig[key].color}
|
||||
|
@@ -6,9 +6,10 @@ import {
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from '@/components/ui/chart'
|
||||
import { calculateXaxisTicks, formatShortDate, formatShortTime } from '@/lib/utils'
|
||||
import { chartTimeData, formatShortDate } from '@/lib/utils'
|
||||
import Spinner from '../spinner'
|
||||
import { useMemo } from 'react'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $chartTime } from '@/lib/stores'
|
||||
|
||||
const chartConfig = {
|
||||
cpu: {
|
||||
@@ -17,12 +18,17 @@ const chartConfig = {
|
||||
},
|
||||
} satisfies ChartConfig
|
||||
|
||||
export default function CpuChart({ chartData }: { chartData: { time: number; cpu: number }[] }) {
|
||||
if (!chartData?.length) {
|
||||
export default function CpuChart({
|
||||
chartData,
|
||||
ticks,
|
||||
}: {
|
||||
chartData: { time: number; cpu: number }[]
|
||||
ticks: number[]
|
||||
}) {
|
||||
if (!chartData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
|
||||
const ticks = useMemo(() => calculateXaxisTicks(chartData), [chartData])
|
||||
const chartTime = useStore($chartTime)
|
||||
|
||||
return (
|
||||
<ChartContainer config={chartConfig} className="h-full w-full absolute aspect-auto">
|
||||
@@ -42,10 +48,10 @@ export default function CpuChart({ chartData }: { chartData: { time: number; cpu
|
||||
ticks={ticks}
|
||||
type="number"
|
||||
scale={'time'}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
minTickGap={35}
|
||||
tickFormatter={formatShortTime}
|
||||
tickMargin={8}
|
||||
axisLine={false}
|
||||
tickFormatter={chartTimeData[chartTime].format}
|
||||
/>
|
||||
<ChartTooltip
|
||||
animationEasing="ease-out"
|
||||
@@ -60,7 +66,7 @@ export default function CpuChart({ chartData }: { chartData: { time: number; cpu
|
||||
/>
|
||||
<Area
|
||||
dataKey="cpu"
|
||||
type="monotone"
|
||||
type="monotoneX"
|
||||
fill="var(--color-cpu)"
|
||||
fillOpacity={0.4}
|
||||
stroke="var(--color-cpu)"
|
||||
|
@@ -6,26 +6,25 @@ import {
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from '@/components/ui/chart'
|
||||
import { calculateXaxisTicks, formatShortDate, formatShortTime } from '@/lib/utils'
|
||||
import { formatShortDate, hourWithMinutes } from '@/lib/utils'
|
||||
import { useMemo } from 'react'
|
||||
import Spinner from '../spinner'
|
||||
// for (const data of chartData) {
|
||||
// data.month = formatDateShort(data.month)
|
||||
// }
|
||||
|
||||
const chartConfig = {
|
||||
diskUsed: {
|
||||
label: 'Disk Usage',
|
||||
color: 'hsl(var(--chart-3))',
|
||||
color: 'hsl(var(--chart-4))',
|
||||
},
|
||||
} satisfies ChartConfig
|
||||
|
||||
export default function DiskChart({
|
||||
chartData,
|
||||
ticks,
|
||||
}: {
|
||||
chartData: { time: number; disk: number; diskUsed: number }[]
|
||||
ticks: number[]
|
||||
}) {
|
||||
if (!chartData.length) {
|
||||
if (!chartData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
|
||||
@@ -33,8 +32,6 @@ export default function DiskChart({
|
||||
return Math.round(chartData[0]?.disk)
|
||||
}, [chartData])
|
||||
|
||||
const ticks = useMemo(() => calculateXaxisTicks(chartData), [chartData])
|
||||
|
||||
// const ticks = useMemo(() => {
|
||||
// let ticks = [0]
|
||||
// for (let i = 1; i < diskSize; i += diskSize / 5) {
|
||||
@@ -65,7 +62,7 @@ export default function DiskChart({
|
||||
minTickGap={8}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
unit={' GiB'}
|
||||
unit={' GB'}
|
||||
/>
|
||||
{/* todo: short time if first date is same day, otherwise short date */}
|
||||
<XAxis
|
||||
@@ -78,13 +75,13 @@ export default function DiskChart({
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
minTickGap={30}
|
||||
tickFormatter={formatShortTime}
|
||||
tickFormatter={hourWithMinutes}
|
||||
/>
|
||||
<ChartTooltip
|
||||
cursor={false}
|
||||
// cursor={false}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
unit=" GiB"
|
||||
unit=" GB"
|
||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.time)}
|
||||
indicator="line"
|
||||
/>
|
||||
@@ -92,7 +89,7 @@ export default function DiskChart({
|
||||
/>
|
||||
<Area
|
||||
dataKey="diskUsed"
|
||||
type="monotone"
|
||||
type="monotoneX"
|
||||
fill="var(--color-diskUsed)"
|
||||
fillOpacity={0.4}
|
||||
stroke="var(--color-diskUsed)"
|
||||
|
97
site/src/components/charts/disk-io-chart.tsx
Normal file
97
site/src/components/charts/disk-io-chart.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts'
|
||||
|
||||
import {
|
||||
ChartConfig,
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from '@/components/ui/chart'
|
||||
import { formatShortDate, hourWithMinutes } from '@/lib/utils'
|
||||
import Spinner from '../spinner'
|
||||
|
||||
const chartConfig = {
|
||||
read: {
|
||||
label: 'Read',
|
||||
color: 'hsl(var(--chart-5))',
|
||||
},
|
||||
write: {
|
||||
label: 'Write',
|
||||
color: 'hsl(var(--chart-3))',
|
||||
},
|
||||
} satisfies ChartConfig
|
||||
|
||||
export default function DiskIoChart({
|
||||
chartData,
|
||||
ticks,
|
||||
}: {
|
||||
chartData: { time: number; read: number; write: number }[]
|
||||
ticks: number[]
|
||||
}) {
|
||||
if (!chartData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
|
||||
return (
|
||||
<ChartContainer config={chartConfig} className="h-full w-full absolute aspect-auto">
|
||||
<AreaChart
|
||||
accessibilityLayer
|
||||
data={chartData}
|
||||
margin={{
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 10,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid vertical={false} />
|
||||
<YAxis
|
||||
width={75}
|
||||
domain={[0, 'auto']}
|
||||
// ticks={ticks}
|
||||
tickCount={9}
|
||||
minTickGap={8}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
unit={' MB/s'}
|
||||
/>
|
||||
{/* todo: short time if first date is same day, otherwise short date */}
|
||||
<XAxis
|
||||
dataKey="time"
|
||||
domain={[ticks[0], ticks.at(-1)!]}
|
||||
ticks={ticks}
|
||||
type="number"
|
||||
scale={'time'}
|
||||
tickLine={true}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
minTickGap={30}
|
||||
tickFormatter={hourWithMinutes}
|
||||
/>
|
||||
<ChartTooltip
|
||||
// cursor={false}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
unit=" MB/s"
|
||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.time)}
|
||||
indicator="line"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Area
|
||||
dataKey="read"
|
||||
type="monotoneX"
|
||||
fill="var(--color-read)"
|
||||
fillOpacity={0.4}
|
||||
stroke="var(--color-read)"
|
||||
/>
|
||||
<Area
|
||||
dataKey="write"
|
||||
type="monotoneX"
|
||||
fill="var(--color-write)"
|
||||
fillOpacity={0.4}
|
||||
stroke="var(--color-write)"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
)
|
||||
}
|
@@ -6,21 +6,21 @@ import {
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from '@/components/ui/chart'
|
||||
import { calculateXaxisTicks, formatShortDate, formatShortTime } from '@/lib/utils'
|
||||
import { formatShortDate, hourWithMinutes } from '@/lib/utils'
|
||||
import { useMemo } from 'react'
|
||||
import Spinner from '../spinner'
|
||||
|
||||
export default function MemChart({
|
||||
chartData,
|
||||
ticks,
|
||||
}: {
|
||||
chartData: { time: number; mem: number; memUsed: number; memCache: number }[]
|
||||
ticks: number[]
|
||||
}) {
|
||||
if (!chartData.length) {
|
||||
if (!chartData.length || !ticks.length) {
|
||||
return <Spinner />
|
||||
}
|
||||
|
||||
const ticks = useMemo(() => calculateXaxisTicks(chartData), [chartData])
|
||||
|
||||
const totalMem = useMemo(() => {
|
||||
return Math.ceil(chartData[0]?.mem)
|
||||
}, [chartData])
|
||||
@@ -56,7 +56,7 @@ export default function MemChart({
|
||||
tickLine={false}
|
||||
allowDecimals={false}
|
||||
axisLine={false}
|
||||
tickFormatter={(v) => `${v} GiB`}
|
||||
tickFormatter={(v) => `${v} GB`}
|
||||
/>
|
||||
{/* todo: short time if first date is same day, otherwise short date */}
|
||||
<XAxis
|
||||
@@ -69,7 +69,7 @@ export default function MemChart({
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
minTickGap={30}
|
||||
tickFormatter={formatShortTime}
|
||||
tickFormatter={hourWithMinutes}
|
||||
/>
|
||||
<ChartTooltip
|
||||
// cursor={false}
|
||||
@@ -77,7 +77,7 @@ export default function MemChart({
|
||||
animationDuration={150}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
unit="GiB"
|
||||
unit="GB"
|
||||
// @ts-ignore
|
||||
itemSorter={(a, b) => a.name.localeCompare(b.name)}
|
||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.time)}
|
||||
@@ -87,7 +87,7 @@ export default function MemChart({
|
||||
/>
|
||||
<Area
|
||||
dataKey="memUsed"
|
||||
type="monotone"
|
||||
type="monotoneX"
|
||||
fill="var(--color-memUsed)"
|
||||
fillOpacity={0.4}
|
||||
stroke="var(--color-memUsed)"
|
||||
@@ -95,7 +95,7 @@ export default function MemChart({
|
||||
/>
|
||||
<Area
|
||||
dataKey="memCache"
|
||||
type="monotone"
|
||||
type="monotoneX"
|
||||
fill="var(--color-memCache)"
|
||||
fillOpacity={0.2}
|
||||
strokeOpacity={0.3}
|
||||
|
Reference in New Issue
Block a user