mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 09:49:28 +08:00
uniform x axis on charts
This commit is contained in:
@@ -6,8 +6,10 @@ import {
|
|||||||
ChartTooltip,
|
ChartTooltip,
|
||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
} from '@/components/ui/chart'
|
} from '@/components/ui/chart'
|
||||||
import { formatShortDate, hourWithMinutes } from '@/lib/utils'
|
import { chartTimeData, formatShortDate } from '@/lib/utils'
|
||||||
import Spinner from '../spinner'
|
import Spinner from '../spinner'
|
||||||
|
import { useStore } from '@nanostores/react'
|
||||||
|
import { $chartTime } from '@/lib/stores'
|
||||||
|
|
||||||
const chartConfig = {
|
const chartConfig = {
|
||||||
recv: {
|
recv: {
|
||||||
@@ -27,6 +29,8 @@ export default function BandwidthChart({
|
|||||||
chartData: { time: number; sent: number; recv: number }[]
|
chartData: { time: number; sent: number; recv: number }[]
|
||||||
ticks: number[]
|
ticks: number[]
|
||||||
}) {
|
}) {
|
||||||
|
const chartTime = useStore($chartTime)
|
||||||
|
|
||||||
if (!chartData.length || !ticks.length) {
|
if (!chartData.length || !ticks.length) {
|
||||||
return <Spinner />
|
return <Spinner />
|
||||||
}
|
}
|
||||||
@@ -58,18 +62,16 @@ export default function BandwidthChart({
|
|||||||
axisLine={false}
|
axisLine={false}
|
||||||
unit={' MB/s'}
|
unit={' MB/s'}
|
||||||
/>
|
/>
|
||||||
{/* todo: short time if first date is same day, otherwise short date */}
|
|
||||||
<XAxis
|
<XAxis
|
||||||
dataKey="time"
|
dataKey="time"
|
||||||
domain={[ticks[0], ticks.at(-1)!]}
|
domain={[ticks[0], ticks.at(-1)!]}
|
||||||
ticks={ticks}
|
ticks={ticks}
|
||||||
type="number"
|
type="number"
|
||||||
scale={'time'}
|
scale={'time'}
|
||||||
tickLine={true}
|
minTickGap={35}
|
||||||
axisLine={false}
|
|
||||||
tickMargin={8}
|
tickMargin={8}
|
||||||
minTickGap={30}
|
axisLine={false}
|
||||||
tickFormatter={hourWithMinutes}
|
tickFormatter={chartTimeData[chartTime].format}
|
||||||
/>
|
/>
|
||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
animationEasing="ease-out"
|
animationEasing="ease-out"
|
||||||
|
@@ -8,8 +8,10 @@ import {
|
|||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
} from '@/components/ui/chart'
|
} from '@/components/ui/chart'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { formatShortDate, hourWithMinutes } from '@/lib/utils'
|
import { chartTimeData, formatShortDate } from '@/lib/utils'
|
||||||
import Spinner from '../spinner'
|
import Spinner from '../spinner'
|
||||||
|
import { useStore } from '@nanostores/react'
|
||||||
|
import { $chartTime } from '@/lib/stores'
|
||||||
|
|
||||||
export default function ContainerCpuChart({
|
export default function ContainerCpuChart({
|
||||||
chartData,
|
chartData,
|
||||||
@@ -18,6 +20,8 @@ export default function ContainerCpuChart({
|
|||||||
chartData: Record<string, number | string>[]
|
chartData: Record<string, number | string>[]
|
||||||
ticks: number[]
|
ticks: number[]
|
||||||
}) {
|
}) {
|
||||||
|
const chartTime = useStore($chartTime)
|
||||||
|
|
||||||
const chartConfig = useMemo(() => {
|
const chartConfig = useMemo(() => {
|
||||||
let config = {} as Record<
|
let config = {} as Record<
|
||||||
string,
|
string,
|
||||||
@@ -82,11 +86,10 @@ export default function ContainerCpuChart({
|
|||||||
ticks={ticks}
|
ticks={ticks}
|
||||||
type="number"
|
type="number"
|
||||||
scale={'time'}
|
scale={'time'}
|
||||||
tickLine={true}
|
minTickGap={35}
|
||||||
axisLine={false}
|
|
||||||
tickMargin={8}
|
tickMargin={8}
|
||||||
minTickGap={30}
|
axisLine={false}
|
||||||
tickFormatter={hourWithMinutes}
|
tickFormatter={chartTimeData[chartTime].format}
|
||||||
/>
|
/>
|
||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
// cursor={false}
|
// cursor={false}
|
||||||
|
@@ -8,8 +8,10 @@ import {
|
|||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
} from '@/components/ui/chart'
|
} from '@/components/ui/chart'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { formatShortDate, hourWithMinutes } from '@/lib/utils'
|
import { chartTimeData, formatShortDate } from '@/lib/utils'
|
||||||
import Spinner from '../spinner'
|
import Spinner from '../spinner'
|
||||||
|
import { useStore } from '@nanostores/react'
|
||||||
|
import { $chartTime } from '@/lib/stores'
|
||||||
|
|
||||||
export default function ContainerMemChart({
|
export default function ContainerMemChart({
|
||||||
chartData,
|
chartData,
|
||||||
@@ -18,6 +20,8 @@ export default function ContainerMemChart({
|
|||||||
chartData: Record<string, number | string>[]
|
chartData: Record<string, number | string>[]
|
||||||
ticks: number[]
|
ticks: number[]
|
||||||
}) {
|
}) {
|
||||||
|
const chartTime = useStore($chartTime)
|
||||||
|
|
||||||
const chartConfig = useMemo(() => {
|
const chartConfig = useMemo(() => {
|
||||||
let config = {} as Record<
|
let config = {} as Record<
|
||||||
string,
|
string,
|
||||||
@@ -87,11 +91,10 @@ export default function ContainerMemChart({
|
|||||||
ticks={ticks}
|
ticks={ticks}
|
||||||
type="number"
|
type="number"
|
||||||
scale={'time'}
|
scale={'time'}
|
||||||
tickLine={true}
|
minTickGap={35}
|
||||||
axisLine={false}
|
|
||||||
tickMargin={8}
|
tickMargin={8}
|
||||||
minTickGap={30}
|
axisLine={false}
|
||||||
tickFormatter={hourWithMinutes}
|
tickFormatter={chartTimeData[chartTime].format}
|
||||||
/>
|
/>
|
||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
// cursor={false}
|
// cursor={false}
|
||||||
|
@@ -42,7 +42,6 @@ export default function CpuChart({
|
|||||||
axisLine={false}
|
axisLine={false}
|
||||||
unit={'%'}
|
unit={'%'}
|
||||||
/>
|
/>
|
||||||
{/* todo: short time if first date is same day, otherwise short date */}
|
|
||||||
<XAxis
|
<XAxis
|
||||||
dataKey="time"
|
dataKey="time"
|
||||||
domain={[ticks[0], ticks.at(-1)!]}
|
domain={[ticks[0], ticks.at(-1)!]}
|
||||||
|
@@ -6,9 +6,11 @@ import {
|
|||||||
ChartTooltip,
|
ChartTooltip,
|
||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
} from '@/components/ui/chart'
|
} from '@/components/ui/chart'
|
||||||
import { formatShortDate, hourWithMinutes } from '@/lib/utils'
|
import { chartTimeData, formatShortDate, hourWithMinutes } from '@/lib/utils'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import Spinner from '../spinner'
|
import Spinner from '../spinner'
|
||||||
|
import { useStore } from '@nanostores/react'
|
||||||
|
import { $chartTime } from '@/lib/stores'
|
||||||
|
|
||||||
const chartConfig = {
|
const chartConfig = {
|
||||||
diskUsed: {
|
diskUsed: {
|
||||||
@@ -24,6 +26,8 @@ export default function DiskChart({
|
|||||||
chartData: { time: number; disk: number; diskUsed: number }[]
|
chartData: { time: number; disk: number; diskUsed: number }[]
|
||||||
ticks: number[]
|
ticks: number[]
|
||||||
}) {
|
}) {
|
||||||
|
const chartTime = useStore($chartTime)
|
||||||
|
|
||||||
const diskSize = useMemo(() => {
|
const diskSize = useMemo(() => {
|
||||||
return Math.round(chartData[0]?.disk)
|
return Math.round(chartData[0]?.disk)
|
||||||
}, [chartData])
|
}, [chartData])
|
||||||
@@ -63,18 +67,16 @@ export default function DiskChart({
|
|||||||
axisLine={false}
|
axisLine={false}
|
||||||
unit={' GB'}
|
unit={' GB'}
|
||||||
/>
|
/>
|
||||||
{/* todo: short time if first date is same day, otherwise short date */}
|
|
||||||
<XAxis
|
<XAxis
|
||||||
dataKey="time"
|
dataKey="time"
|
||||||
domain={[ticks[0], ticks.at(-1)!]}
|
domain={[ticks[0], ticks.at(-1)!]}
|
||||||
ticks={ticks}
|
ticks={ticks}
|
||||||
type="number"
|
type="number"
|
||||||
scale={'time'}
|
scale={'time'}
|
||||||
tickLine={true}
|
minTickGap={35}
|
||||||
axisLine={false}
|
|
||||||
tickMargin={8}
|
tickMargin={8}
|
||||||
minTickGap={30}
|
axisLine={false}
|
||||||
tickFormatter={hourWithMinutes}
|
tickFormatter={chartTimeData[chartTime].format}
|
||||||
/>
|
/>
|
||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
animationEasing="ease-out"
|
animationEasing="ease-out"
|
||||||
|
@@ -6,8 +6,10 @@ import {
|
|||||||
ChartTooltip,
|
ChartTooltip,
|
||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
} from '@/components/ui/chart'
|
} from '@/components/ui/chart'
|
||||||
import { formatShortDate, hourWithMinutes } from '@/lib/utils'
|
import { chartTimeData, formatShortDate } from '@/lib/utils'
|
||||||
import Spinner from '../spinner'
|
import Spinner from '../spinner'
|
||||||
|
import { useStore } from '@nanostores/react'
|
||||||
|
import { $chartTime } from '@/lib/stores'
|
||||||
|
|
||||||
const chartConfig = {
|
const chartConfig = {
|
||||||
read: {
|
read: {
|
||||||
@@ -27,6 +29,8 @@ export default function DiskIoChart({
|
|||||||
chartData: { time: number; read: number; write: number }[]
|
chartData: { time: number; read: number; write: number }[]
|
||||||
ticks: number[]
|
ticks: number[]
|
||||||
}) {
|
}) {
|
||||||
|
const chartTime = useStore($chartTime)
|
||||||
|
|
||||||
if (!chartData.length || !ticks.length) {
|
if (!chartData.length || !ticks.length) {
|
||||||
return <Spinner />
|
return <Spinner />
|
||||||
}
|
}
|
||||||
@@ -58,18 +62,16 @@ export default function DiskIoChart({
|
|||||||
axisLine={false}
|
axisLine={false}
|
||||||
unit={' MB/s'}
|
unit={' MB/s'}
|
||||||
/>
|
/>
|
||||||
{/* todo: short time if first date is same day, otherwise short date */}
|
|
||||||
<XAxis
|
<XAxis
|
||||||
dataKey="time"
|
dataKey="time"
|
||||||
domain={[ticks[0], ticks.at(-1)!]}
|
domain={[ticks[0], ticks.at(-1)!]}
|
||||||
ticks={ticks}
|
ticks={ticks}
|
||||||
type="number"
|
type="number"
|
||||||
scale={'time'}
|
scale={'time'}
|
||||||
tickLine={true}
|
minTickGap={35}
|
||||||
axisLine={false}
|
|
||||||
tickMargin={8}
|
tickMargin={8}
|
||||||
minTickGap={30}
|
axisLine={false}
|
||||||
tickFormatter={hourWithMinutes}
|
tickFormatter={chartTimeData[chartTime].format}
|
||||||
/>
|
/>
|
||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
animationEasing="ease-out"
|
animationEasing="ease-out"
|
||||||
|
@@ -6,9 +6,11 @@ import {
|
|||||||
ChartTooltip,
|
ChartTooltip,
|
||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
} from '@/components/ui/chart'
|
} from '@/components/ui/chart'
|
||||||
import { formatShortDate, hourWithMinutes } from '@/lib/utils'
|
import { chartTimeData, formatShortDate } from '@/lib/utils'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import Spinner from '../spinner'
|
import Spinner from '../spinner'
|
||||||
|
import { useStore } from '@nanostores/react'
|
||||||
|
import { $chartTime } from '@/lib/stores'
|
||||||
|
|
||||||
export default function MemChart({
|
export default function MemChart({
|
||||||
chartData,
|
chartData,
|
||||||
@@ -17,6 +19,8 @@ export default function MemChart({
|
|||||||
chartData: { time: number; mem: number; memUsed: number; memCache: number }[]
|
chartData: { time: number; mem: number; memUsed: number; memCache: number }[]
|
||||||
ticks: number[]
|
ticks: number[]
|
||||||
}) {
|
}) {
|
||||||
|
const chartTime = useStore($chartTime)
|
||||||
|
|
||||||
const totalMem = useMemo(() => {
|
const totalMem = useMemo(() => {
|
||||||
const maxMem = Math.ceil(chartData[0]?.mem)
|
const maxMem = Math.ceil(chartData[0]?.mem)
|
||||||
return maxMem > 2 && maxMem % 2 !== 0 ? maxMem + 1 : maxMem
|
return maxMem > 2 && maxMem % 2 !== 0 ? maxMem + 1 : maxMem
|
||||||
@@ -59,18 +63,16 @@ export default function MemChart({
|
|||||||
axisLine={false}
|
axisLine={false}
|
||||||
unit={' GB'}
|
unit={' GB'}
|
||||||
/>
|
/>
|
||||||
{/* todo: short time if first date is same day, otherwise short date */}
|
|
||||||
<XAxis
|
<XAxis
|
||||||
dataKey="time"
|
dataKey="time"
|
||||||
domain={[ticks[0], ticks.at(-1)!]}
|
domain={[ticks[0], ticks.at(-1)!]}
|
||||||
ticks={ticks}
|
ticks={ticks}
|
||||||
type="number"
|
type="number"
|
||||||
scale={'time'}
|
scale={'time'}
|
||||||
tickLine={true}
|
minTickGap={35}
|
||||||
axisLine={false}
|
|
||||||
tickMargin={8}
|
tickMargin={8}
|
||||||
minTickGap={30}
|
axisLine={false}
|
||||||
tickFormatter={hourWithMinutes}
|
tickFormatter={chartTimeData[chartTime].format}
|
||||||
/>
|
/>
|
||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
// cursor={false}
|
// cursor={false}
|
||||||
|
@@ -142,7 +142,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
const now = new Date()
|
const now = new Date()
|
||||||
const startTime = chartTimeData[chartTime].getOffset(now)
|
const startTime = chartTimeData[chartTime].getOffset(now)
|
||||||
const scale = scaleTime([startTime.getTime(), now], [0, cpuChartData.length])
|
const scale = scaleTime([startTime.getTime(), now], [0, cpuChartData.length])
|
||||||
setTicks(scale.ticks().map((d) => d.getTime()))
|
setTicks(scale.ticks(chartTimeData[chartTime].ticks).map((d) => d.getTime()))
|
||||||
}, [chartTime, systemStats])
|
}, [chartTime, systemStats])
|
||||||
|
|
||||||
// get container stats
|
// get container stats
|
||||||
|
@@ -2,7 +2,7 @@ import { toast } from '@/components/ui/use-toast'
|
|||||||
import { type ClassValue, clsx } from 'clsx'
|
import { type ClassValue, clsx } from 'clsx'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
import { $alerts, $systems, pb } from './stores'
|
import { $alerts, $systems, pb } from './stores'
|
||||||
import { AlertRecord, ChartTimes, SystemRecord } from '@/types'
|
import { AlertRecord, ChartTimeData, ChartTimes, SystemRecord } from '@/types'
|
||||||
import { RecordModel, RecordSubscription } from 'pocketbase'
|
import { RecordModel, RecordSubscription } from 'pocketbase'
|
||||||
import { WritableAtom } from 'nanostores'
|
import { WritableAtom } from 'nanostores'
|
||||||
import { timeDay, timeHour } from 'd3-time'
|
import { timeDay, timeHour } from 'd3-time'
|
||||||
@@ -71,9 +71,22 @@ export const formatShortDate = (timestamp: string) => {
|
|||||||
return shortDateFormatter.format(new Date(timestamp))
|
return shortDateFormatter.format(new Date(timestamp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const dayTimeFormatter = new Intl.DateTimeFormat(undefined, {
|
||||||
|
// // day: 'numeric',
|
||||||
|
// // month: 'short',
|
||||||
|
// hour: 'numeric',
|
||||||
|
// weekday: 'short',
|
||||||
|
// minute: 'numeric',
|
||||||
|
// // dateStyle: 'short',
|
||||||
|
// })
|
||||||
|
// export const formatDayTime = (timestamp: string) => {
|
||||||
|
// // console.log('ts', timestamp)
|
||||||
|
// return dayTimeFormatter.format(new Date(timestamp))
|
||||||
|
// }
|
||||||
|
|
||||||
const dayFormatter = new Intl.DateTimeFormat(undefined, {
|
const dayFormatter = new Intl.DateTimeFormat(undefined, {
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
month: 'long',
|
month: 'short',
|
||||||
// dateStyle: 'medium',
|
// dateStyle: 'medium',
|
||||||
})
|
})
|
||||||
export const formatDay = (timestamp: string) => {
|
export const formatDay = (timestamp: string) => {
|
||||||
@@ -130,16 +143,18 @@ export function getPbTimestamp(timeString: ChartTimes) {
|
|||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const chartTimeData = {
|
export const chartTimeData: ChartTimeData = {
|
||||||
'1h': {
|
'1h': {
|
||||||
type: '1m',
|
type: '1m',
|
||||||
label: '1 hour',
|
label: '1 hour',
|
||||||
|
// ticks: 12,
|
||||||
format: (timestamp: string) => hourWithMinutes(timestamp),
|
format: (timestamp: string) => hourWithMinutes(timestamp),
|
||||||
getOffset: (endTime: Date) => timeHour.offset(endTime, -1),
|
getOffset: (endTime: Date) => timeHour.offset(endTime, -1),
|
||||||
},
|
},
|
||||||
'12h': {
|
'12h': {
|
||||||
type: '10m',
|
type: '10m',
|
||||||
label: '12 hours',
|
label: '12 hours',
|
||||||
|
ticks: 12,
|
||||||
format: (timestamp: string) => hourWithMinutes(timestamp),
|
format: (timestamp: string) => hourWithMinutes(timestamp),
|
||||||
getOffset: (endTime: Date) => timeHour.offset(endTime, -12),
|
getOffset: (endTime: Date) => timeHour.offset(endTime, -12),
|
||||||
},
|
},
|
||||||
@@ -152,12 +167,14 @@ export const chartTimeData = {
|
|||||||
'1w': {
|
'1w': {
|
||||||
type: '120m',
|
type: '120m',
|
||||||
label: '1 week',
|
label: '1 week',
|
||||||
format: (timestamp: string) => formatDay(timestamp),
|
ticks: 7,
|
||||||
|
format: (timestamp: string) => formatShortDate(timestamp),
|
||||||
getOffset: (endTime: Date) => timeDay.offset(endTime, -7),
|
getOffset: (endTime: Date) => timeDay.offset(endTime, -7),
|
||||||
},
|
},
|
||||||
'30d': {
|
'30d': {
|
||||||
type: '480m',
|
type: '480m',
|
||||||
label: '30 days',
|
label: '30 days',
|
||||||
|
ticks: 30,
|
||||||
format: (timestamp: string) => formatDay(timestamp),
|
format: (timestamp: string) => formatDay(timestamp),
|
||||||
getOffset: (endTime: Date) => timeDay.offset(endTime, -30),
|
getOffset: (endTime: Date) => timeDay.offset(endTime, -30),
|
||||||
},
|
},
|
||||||
|
10
hub/site/src/types.d.ts
vendored
10
hub/site/src/types.d.ts
vendored
@@ -81,3 +81,13 @@ export interface AlertRecord extends RecordModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ChartTimes = '1h' | '12h' | '24h' | '1w' | '30d'
|
export type ChartTimes = '1h' | '12h' | '24h' | '1w' | '30d'
|
||||||
|
|
||||||
|
export interface ChartTimeData {
|
||||||
|
[key: string]: {
|
||||||
|
type: '1m' | '10m' | '20m' | '120m' | '480m'
|
||||||
|
label: string
|
||||||
|
ticks?: number
|
||||||
|
format: (timestamp: string) => string
|
||||||
|
getOffset: (endTime: Date) => Date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user