mirror of
https://github.com/fankes/beszel.git
synced 2025-10-20 02:09:28 +08:00
site updates
This commit is contained in:
@@ -26,8 +26,8 @@ export function AddServerButton() {
|
|||||||
function copyDockerCompose(port: string) {
|
function copyDockerCompose(port: string) {
|
||||||
copyToClipboard(`services:
|
copyToClipboard(`services:
|
||||||
agent:
|
agent:
|
||||||
image: 'henrygd/ubik-agent'
|
image: 'henrygd/quoma-agent'
|
||||||
container_name: 'ubik-agent'
|
container_name: 'quoma-agent'
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- '${port}:45876'
|
- '${port}:45876'
|
||||||
|
110
site/src/components/charts/container-mem-chart.tsx
Normal file
110
site/src/components/charts/container-mem-chart.tsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts'
|
||||||
|
import {
|
||||||
|
ChartConfig,
|
||||||
|
ChartContainer,
|
||||||
|
ChartTooltip,
|
||||||
|
ChartTooltipContent,
|
||||||
|
} from '@/components/ui/chart'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { formatShortDate, formatShortTime } from '@/lib/utils'
|
||||||
|
import Spinner from '../spinner'
|
||||||
|
|
||||||
|
export default function ({
|
||||||
|
chartData,
|
||||||
|
max,
|
||||||
|
}: {
|
||||||
|
chartData: Record<string, number | string>[]
|
||||||
|
max: number
|
||||||
|
}) {
|
||||||
|
console.log('max', max)
|
||||||
|
const chartConfig = useMemo(() => {
|
||||||
|
let config = {} as Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
label: string
|
||||||
|
color: string
|
||||||
|
}
|
||||||
|
>
|
||||||
|
const totalUsage = {} as Record<string, number>
|
||||||
|
for (let stats of chartData) {
|
||||||
|
for (let key in stats) {
|
||||||
|
if (key === 'time') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!(key in totalUsage)) {
|
||||||
|
totalUsage[key] = 0
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
totalUsage[key] += stats[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let keys = Object.keys(totalUsage)
|
||||||
|
keys.sort((a, b) => (totalUsage[a] > totalUsage[b] ? -1 : 1))
|
||||||
|
const length = keys.length
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const key = keys[i]
|
||||||
|
const hue = ((i * 360) / length) % 360
|
||||||
|
config[key] = {
|
||||||
|
label: key,
|
||||||
|
color: `hsl(${hue}, 60%, 60%)`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config satisfies ChartConfig
|
||||||
|
}, [chartData])
|
||||||
|
|
||||||
|
if (!chartData.length) {
|
||||||
|
return <Spinner />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChartContainer config={chartConfig} className="h-full w-full absolute aspect-auto">
|
||||||
|
<AreaChart
|
||||||
|
accessibilityLayer
|
||||||
|
data={chartData}
|
||||||
|
|
||||||
|
// reverseStackOrder={true}
|
||||||
|
>
|
||||||
|
<CartesianGrid vertical={false} />
|
||||||
|
<YAxis
|
||||||
|
domain={[0, max]}
|
||||||
|
tickCount={9}
|
||||||
|
tickLine={false}
|
||||||
|
axisLine={false}
|
||||||
|
tickFormatter={(v) => `${Math.ceil(v / 1024)} GiB`}
|
||||||
|
/>
|
||||||
|
<XAxis
|
||||||
|
dataKey="time"
|
||||||
|
tickLine={true}
|
||||||
|
axisLine={false}
|
||||||
|
tickMargin={8}
|
||||||
|
minTickGap={30}
|
||||||
|
tickFormatter={formatShortTime}
|
||||||
|
/>
|
||||||
|
<ChartTooltip
|
||||||
|
cursor={false}
|
||||||
|
labelFormatter={formatShortDate}
|
||||||
|
// itemSorter={(item) => {
|
||||||
|
// console.log('itemSorter', item)
|
||||||
|
// return -item.value
|
||||||
|
// }}
|
||||||
|
content={<ChartTooltipContent indicator="line" />}
|
||||||
|
/>
|
||||||
|
{Object.keys(chartConfig).map((key) => (
|
||||||
|
<Area
|
||||||
|
key={key}
|
||||||
|
// isAnimationActive={false}
|
||||||
|
animateNewValues={false}
|
||||||
|
dataKey={key}
|
||||||
|
type="natural"
|
||||||
|
fill={chartConfig[key].color}
|
||||||
|
fillOpacity={0.4}
|
||||||
|
stroke={chartConfig[key].color}
|
||||||
|
stackId="a"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</AreaChart>
|
||||||
|
</ChartContainer>
|
||||||
|
)
|
||||||
|
}
|
@@ -1,7 +1,8 @@
|
|||||||
export function Logo({ className }: { className?: string }) {
|
export function Logo({ className }: { className?: string }) {
|
||||||
return (
|
return (
|
||||||
<svg viewBox="0 0 421.6 140.2" className={className}>
|
// audiowide
|
||||||
<path d="M0 109.4V0h20.4v109.4a9.6 9.6 0 0 0 2.4 6.4 12.8 12.8 0 0 0 .7.7q3.1 3.1 7.1 3.1h58.6V0h20.2v119.6H89.2V140H30.6a33.4 33.4 0 0 1-9.6-1.3 30.7 30.7 0 0 1-2.7-1 30 30 0 0 1-7-4 26.8 26.8 0 0 1-2.7-2.3 28.1 28.1 0 0 1-5.5-8 33 33 0 0 1-.8-1.7 32.2 32.2 0 0 1-2.3-11 37.1 37.1 0 0 1 0-1.3ZM307 0h20.4v73.8l58.4-30.6q2 0 3-.8t1.5-2q.5-1.2.6-2.5l.1-2.3V17.8l20.4-10.2v28a29.5 29.5 0 0 1-3.8 14.4 34.6 34.6 0 0 1-.1.2 26 26 0 0 1-11 10.5 31.6 31.6 0 0 1-.5.3l-17.8 10.2 43.4 68.8h-23l-38.2-58.6-28 12.8q-2 2-3.5 3.8a7 7 0 0 0-1 1.5 5.3 5.3 0 0 0-.5 2.3V140H307V0ZM127.6 109.4v-89h20.2V0H214q5.8 0 11.3 2.3 5.5 2.3 9.8 6.3a30 30 0 0 1 6.5 9 34.4 34.4 0 0 1 .4.7 29.3 29.3 0 0 1 2.6 12.2 33.4 33.4 0 0 1 0 .1v12.6a23.3 23.3 0 0 1-1.2 7.4 26.1 26.1 0 0 1-.1.2q-1.3 3.8-3.4 7.1-2.1 3.3-4.9 6.1a48.8 48.8 0 0 1-3.3 3 39.5 39.5 0 0 1-2.3 1.8l2.4 2.4q7.6 4 12.7 11a28 28 0 0 1 3.7 6.7 22.5 22.5 0 0 1 1.4 7.9v12.6a29.4 29.4 0 0 1-2.4 11.8 28.6 28.6 0 0 1-.2.5 30.7 30.7 0 0 1-5.3 8 28.7 28.7 0 0 1-1.6 1.7q-4.3 4-9.7 6.3-5.4 2.3-11.2 2.3h-63.6q-5.8 0-10.9-2.3-5.1-2.3-8.9-6.3a28 28 0 0 1-5-7.3 33.7 33.7 0 0 1-1-2.4 33.3 33.3 0 0 1-2.2-10.6 38.6 38.6 0 0 1 0-1.7Zm20.2-89V112q0 6 5 7.3a11.8 11.8 0 0 0 2.8.3h63.6a9.2 9.2 0 0 0 5.3-1.7 13 13 0 0 0 1.7-1.4 11.2 11.2 0 0 0 2.1-2.7 9 9 0 0 0 1.1-4.4V96.8q0-2-1.6-3.9a11 11 0 0 0 0 0 57.9 57.9 0 0 0-2.2-2.4 70.3 70.3 0 0 0-1.4-1.5l-17.8-7.6-43.2 23v-23l56-30.4q1.6 0 3-2.3a12.4 12.4 0 0 0 .4-.6 18.3 18.3 0 0 0 .8-1.6q.7-1.8.8-3.1a5.2 5.2 0 0 0 0-.2V30.6a9.6 9.6 0 0 0-2.4-6.4 12.8 12.8 0 0 0-.7-.7q-3.1-3.1-7.1-3.1h-66.2ZM266.4 0H287v140.2h-20.6V0Z" />
|
<svg viewBox="0 0 370 82" className={className} fill="currentColor">
|
||||||
|
<path d="M271.8 36v35H259V36a7.5 7.5 0 0 0-.1-1.5q-.2-.8-.6-1.4a3.9 3.9 0 0 0-.6-.9q-1.1-1-3.1-1.3a9.2 9.2 0 0 0-.8 0h-16.5V71h-12.7V24.5a6.3 6.3 0 0 1 .4-2.2 6 6 0 0 1 0-.3q.5-1.1 1.4-2 .9-.8 2-1.3a6.4 6.4 0 0 1 2.6-.5H254a17.2 17.2 0 0 1 3 .3 22.8 22.8 0 0 1 2.6.6q3 1 5.8 3v-3.9h17.2a17.7 17.7 0 0 1 2.5.2 21.3 21.3 0 0 1 1.3.2 19.5 19.5 0 0 1 3.8 1.3 21.5 21.5 0 0 1 .2 0q2 .9 3.7 2.3a15 15 0 0 1 3.2 3.3 16.3 16.3 0 0 1 1.6 3 19.7 19.7 0 0 1 .6 1.6q.8 2.6.8 5.9v35h-12.7V36a8 8 0 0 0 0-1.5q-.3-1.4-1.2-2.3-1.2-1.3-3.8-1.3H271q.7 2.4.7 5Zm94.7-.3v17.8a17.3 17.3 0 0 1-.3 3 23.1 23.1 0 0 1-.6 2.7 17 17 0 0 1-3 5.7 15.9 15.9 0 0 1-3.7 3.3 19.6 19.6 0 0 1-1.7 1Q354 71 349.6 71a25.4 25.4 0 0 1-.6 0h-22.9a17.3 17.3 0 0 1-3-.3 23.1 23.1 0 0 1-2.7-.6 17 17 0 0 1-5.6-3 15.9 15.9 0 0 1-3.3-3.7 19.6 19.6 0 0 1-1.1-1.8q-1.7-3.1-1.8-7.5a25.4 25.4 0 0 1 0-.6 17.3 17.3 0 0 1 .3-3 23.4 23.4 0 0 1 .7-2.7q.9-3 3-5.7a15.9 15.9 0 0 1 3.6-3.3 19.6 19.6 0 0 1 1.8-1q3.1-1.7 7.6-1.8a25.4 25.4 0 0 1 .5 0h23V48h-23a6.8 6.8 0 0 0-1.6.2 4.6 4.6 0 0 0-2.4 1.4 5.5 5.5 0 0 0-1.4 3.3 7.3 7.3 0 0 0 0 .6 6 6 0 0 0 .2 1.7 4.4 4.4 0 0 0 1.4 2.2 5.8 5.8 0 0 0 4 1.4H349a6.9 6.9 0 0 0 1.7-.2 4.6 4.6 0 0 0 2.3-1.4 5.4 5.4 0 0 0 1.4-3.2 7.2 7.2 0 0 0 0-.7V35.7a6.5 6.5 0 0 0-.2-1.7 4.8 4.8 0 0 0-1.3-2.3q-1.5-1.4-3.9-1.4h-27.9v-12h28a17.3 17.3 0 0 1 3 .2 23.1 23.1 0 0 1 2.6.6 17 17 0 0 1 5.7 3 15.9 15.9 0 0 1 3.3 3.7 19.6 19.6 0 0 1 1 1.8q1.7 3.1 1.8 7.5a25.4 25.4 0 0 1 0 .6Zm-222-17.5v46.4a6.7 6.7 0 0 1-.2 2 6.1 6.1 0 0 1-.3.5 6.2 6.2 0 0 1-1.3 2q-.9 1-2 1.4a6.2 6.2 0 0 1-2.4.5h-28.8a23.3 23.3 0 0 1-3-.2 29 29 0 0 1-2-.3 23.6 23.6 0 0 1-5.2-1.8 27 27 0 0 1-4.7-2.8 30 30 0 0 1-.3-.2q-2.5-1.8-4.3-4.4a21.5 21.5 0 0 1-2.1-4 26.1 26.1 0 0 1-.8-2 22.4 22.4 0 0 1-.9-4.2 29.5 29.5 0 0 1-.2-3.6V18.2h12.7v29.3A12.5 12.5 0 0 0 99 50a10 10 0 0 0 .6 2 10.5 10.5 0 0 0 2 3 10 10 0 0 0 .3.4q1.5 1.4 3.4 2.1 2 .8 4.3.8h22.2v-40h12.7ZM75.7 29.3v13.4a32.8 32.8 0 0 1-.8 7.1 29.5 29.5 0 0 1-.5 2 29 29 0 0 1-3.1 7 26.9 26.9 0 0 1-.6 1 27 27 0 0 1-5.7 6 27 27 0 0 1-7.4 4.2l14.8 11.8H54L41.8 72H29.3a32.1 32.1 0 0 1-8.2-1 28.7 28.7 0 0 1-3.5-1.2q-5.3-2.2-9.3-6a28.2 28.2 0 0 1-6-9.4A29.6 29.6 0 0 1 0 45.2a35.1 35.1 0 0 1-.1-2.5V29.3A31.8 31.8 0 0 1 1 21a28.5 28.5 0 0 1 1.2-3.4 28.1 28.1 0 0 1 5.2-8.3 26.7 26.7 0 0 1 1-1 28 28 0 0 1 9.1-6 31.8 31.8 0 0 1 .1-.1A29.9 29.9 0 0 1 27.4.1a35 35 0 0 1 1.9-.1h17.2a31.6 31.6 0 0 1 8.2 1 28.2 28.2 0 0 1 3.4 1.2 28.1 28.1 0 0 1 9.3 6 27.5 27.5 0 0 1 6 9 31.3 31.3 0 0 1 0 .4 30.1 30.1 0 0 1 2.2 9.6 35.4 35.4 0 0 1 0 2.1ZM213.9 36v17.2a22.2 22.2 0 0 1-.2 3 17 17 0 0 1-.7 2.9 18.6 18.6 0 0 1-1.1 2.8 15.3 15.3 0 0 1-1 1.7 15 15 0 0 1-3.2 3.4 18.5 18.5 0 0 1-3.8 2.3 19.5 19.5 0 0 1-4 1.3 20.8 20.8 0 0 1-2.3.3 17 17 0 0 1-1.6.1h-22.9q-2.6 0-5.7-1-3.1-.9-5.8-3a16.3 16.3 0 0 1-3.4-3.7 20 20 0 0 1-1-1.8 14.7 14.7 0 0 1-1.4-3.8q-.3-1.7-.4-3.6a26.1 26.1 0 0 1 0-1V36q0-4.4 1.4-7.6a13.2 13.2 0 0 1 .4-.7 18.6 18.6 0 0 1 2.3-3.5 15.6 15.6 0 0 1 2.1-2q2.7-2.1 5.8-3a24.2 24.2 0 0 1 2.8-.7q1.5-.3 3-.3H196a22.7 22.7 0 0 1 3.8.3q2.1.4 3.9 1.2a13.5 13.5 0 0 1 .6.3 18 18 0 0 1 3.5 2.2 15 15 0 0 1 2 2.2q2.2 2.7 3.1 5.8a23.3 23.3 0 0 1 .7 3 17.3 17.3 0 0 1 .3 2.8Zm-151 6.7V29.3a20.4 20.4 0 0 0-.3-4 16.6 16.6 0 0 0-.8-2.8 15.4 15.4 0 0 0-2.5-4.2 14.3 14.3 0 0 0-.9-1 15 15 0 0 0-4.8-3.2 17.2 17.2 0 0 0-.4-.2 17.5 17.5 0 0 0-4.9-1.1 21 21 0 0 0-1.8-.1H29.3a20 20 0 0 0-4 .4 16.6 16.6 0 0 0-2.8.8q-3 1.2-5.2 3.4a14.8 14.8 0 0 0-3.3 5 17 17 0 0 0-.1.2 17.3 17.3 0 0 0-1 4.4 21.6 21.6 0 0 0-.2 2.4v13.4a20.4 20.4 0 0 0 .4 4 16.6 16.6 0 0 0 .8 2.8 15 15 0 0 0 2.7 4.5 14.3 14.3 0 0 0 .7.7 15.2 15.2 0 0 0 5 3.3 17.5 17.5 0 0 0 .2 0 17.4 17.4 0 0 0 4.7 1.2 21.3 21.3 0 0 0 2.1 0h17a20 20 0 0 0 4.2-.3A16.6 16.6 0 0 0 53 58q3.1-1.2 5.3-3.4a14.8 14.8 0 0 0 3.3-5 17 17 0 0 0 0-.2A17.3 17.3 0 0 0 63 45a21.6 21.6 0 0 0 0-2.4Zm138.3 10.5V36a8.4 8.4 0 0 0-.1-1.5l-.5-1.4a3.7 3.7 0 0 0-.7-1 4.2 4.2 0 0 0-1.8-1l-1.4-.3a8.9 8.9 0 0 0-.7 0h-22.8q-1.7 0-3 .6a4 4 0 0 0-.8.7q-1.2 1.1-1.3 3.2a8.7 8.7 0 0 0 0 .6v17.2q0 1.8.7 3a4 4 0 0 0 .6.8 4.2 4.2 0 0 0 1.7 1l1.5.3a8.9 8.9 0 0 0 .6 0H196a8 8 0 0 0 1.5-.1l1.4-.5a3.7 3.7 0 0 0 1-.7 4.2 4.2 0 0 0 1-1.7l.3-1.5a8.9 8.9 0 0 0 0-.6Z" />
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,8 @@ import CpuChart from '../charts/cpu-chart'
|
|||||||
import MemChart from '../charts/mem-chart'
|
import MemChart from '../charts/mem-chart'
|
||||||
import DiskChart from '../charts/disk-chart'
|
import DiskChart from '../charts/disk-chart'
|
||||||
import ContainerCpuChart from '../charts/container-cpu-chart'
|
import ContainerCpuChart from '../charts/container-cpu-chart'
|
||||||
import { CpuIcon } from 'lucide-react'
|
import { CpuIcon, MemoryStickIcon } from 'lucide-react'
|
||||||
|
import ContainerMemChart from '../charts/container-mem-chart'
|
||||||
|
|
||||||
// const CpuChart = lazy(() => import('../cpu-chart'))
|
// const CpuChart = lazy(() => import('../cpu-chart'))
|
||||||
|
|
||||||
@@ -42,14 +43,17 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
const [containerCpuChartData, setContainerCpuChartData] = useState(
|
const [containerCpuChartData, setContainerCpuChartData] = useState(
|
||||||
[] as Record<string, number | string>[]
|
[] as Record<string, number | string>[]
|
||||||
)
|
)
|
||||||
|
const [containerMemChartData, setContainerMemChartData] = useState(
|
||||||
|
[] as Record<string, number | string>[]
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = name
|
document.title = name
|
||||||
return () => {
|
return () => {
|
||||||
setContainerCpuChartData([])
|
setContainerCpuChartData([])
|
||||||
setCpuChartData({} as { max: number; data: { time: string; cpu: number }[] })
|
setCpuChartData({} as { max: number; data: { time: string; cpu: number }[] })
|
||||||
setMemChartData([] as { time: string; mem: number; memUsed: number }[])
|
setMemChartData([])
|
||||||
setDiskChartData([] as { time: string; disk: number; diskUsed: number }[])
|
setDiskChartData([])
|
||||||
}
|
}
|
||||||
}, [name])
|
}, [name])
|
||||||
|
|
||||||
@@ -76,16 +80,18 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
if (!serverStats.length) {
|
if (!serverStats.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
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.cpu })
|
cpuData.push({ time: created, cpu: stats.cpu })
|
||||||
|
maxCpu = Math.max(maxCpu, stats.cpu)
|
||||||
memData.push({ time: created, mem: stats.mem, memUsed: stats.memUsed })
|
memData.push({ time: created, mem: stats.mem, memUsed: stats.memUsed })
|
||||||
diskData.push({ time: created, disk: stats.disk, diskUsed: stats.diskUsed })
|
diskData.push({ time: created, disk: stats.disk, diskUsed: stats.diskUsed })
|
||||||
}
|
}
|
||||||
setCpuChartData({
|
setCpuChartData({
|
||||||
max: Math.ceil(Math.max(...cpuData.map((d) => d.cpu))),
|
max: Math.ceil(maxCpu),
|
||||||
data: cpuData.reverse(),
|
data: cpuData.reverse(),
|
||||||
})
|
})
|
||||||
setMemChartData(memData.reverse())
|
setMemChartData(memData.reverse())
|
||||||
@@ -125,15 +131,20 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
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>[]
|
||||||
|
|
||||||
for (let { created, stats } of containers) {
|
for (let { created, stats } of containers) {
|
||||||
let obj = { time: created } as Record<string, number | string>
|
let cpuData = { time: created } as Record<string, number | string>
|
||||||
for (let { name, cpu } of stats) {
|
let memData = { time: created } as Record<string, number | string>
|
||||||
obj[name] = cpu
|
for (let container of stats) {
|
||||||
|
cpuData[container.name] = container.cpu
|
||||||
|
memData[container.name] = container.mem
|
||||||
}
|
}
|
||||||
containerCpuData.push(obj)
|
containerCpuData.push(cpuData)
|
||||||
|
containerMemData.push(memData)
|
||||||
}
|
}
|
||||||
setContainerCpuChartData(containerCpuData.reverse())
|
setContainerCpuChartData(containerCpuData.reverse())
|
||||||
|
setContainerMemChartData(containerMemData.reverse())
|
||||||
}, [containers])
|
}, [containers])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -175,9 +186,28 @@ export default function ServerDetail({ name }: { name: string }) {
|
|||||||
<CardDescription>Precise usage at the recorded time</CardDescription>
|
<CardDescription>Precise usage 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 />}>
|
||||||
<MemChart chartData={memChartData} />
|
<MemChart chartData={memChartData} />
|
||||||
{/* </Suspense> */}
|
</Suspense>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<Card className="pb-2">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex gap-2 justify-between">
|
||||||
|
<span>Docker Memory Usage</span>
|
||||||
|
<MemoryStickIcon className="opacity-70" />
|
||||||
|
</CardTitle>{' '}
|
||||||
|
<CardDescription>Memory usage of docker containers</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className={'pl-1 w-[calc(100%-2em)] h-52 relative'}>
|
||||||
|
<Suspense fallback={<Spinner />}>
|
||||||
|
{server?.stats?.mem && (
|
||||||
|
<ContainerMemChart
|
||||||
|
chartData={containerMemChartData}
|
||||||
|
max={server.stats.mem * 1024}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Suspense>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className="pb-2">
|
<Card className="pb-2">
|
||||||
|
@@ -69,7 +69,7 @@ function CellFormatter(info: CellContext<SystemRecord, unknown>) {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<span className="grow block bg-muted h-4 relative rounded-sm overflow-hidden">
|
<span className="grow min-w-10 block bg-muted h-4 relative rounded-sm overflow-hidden">
|
||||||
<span
|
<span
|
||||||
className={cn('absolute inset-0 w-full h-full origin-left', `bg-${color}-500`)}
|
className={cn('absolute inset-0 w-full h-full origin-left', `bg-${color}-500`)}
|
||||||
style={{ transform: `scalex(${val}%)` }}
|
style={{ transform: `scalex(${val}%)` }}
|
||||||
@@ -109,7 +109,7 @@ export default function () {
|
|||||||
<span className="flex gap-0.5 items-center text-base">
|
<span className="flex gap-0.5 items-center text-base">
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-2.5 h-2.5 block left-0 rounded-full',
|
'w-2.5 h-2.5 left-0 rounded-full',
|
||||||
info.row.original.active ? 'bg-green-500' : 'bg-red-500'
|
info.row.original.active ? 'bg-green-500' : 'bg-red-500'
|
||||||
)}
|
)}
|
||||||
style={{ marginBottom: '-1px' }}
|
style={{ marginBottom: '-1px' }}
|
||||||
@@ -159,12 +159,15 @@ export default function () {
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||||
<DropdownMenuItem
|
{/* <DropdownMenuItem
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
navigate(`/server/${system.name}`)
|
navigate(`/server/${system.name}`)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
View details
|
View details
|
||||||
|
</DropdownMenuItem> */}
|
||||||
|
<DropdownMenuItem onClick={() => console.log('pause server')}>
|
||||||
|
Pause
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => navigator.clipboard.writeText(system.ip)}>
|
<DropdownMenuItem onClick={() => navigator.clipboard.writeText(system.ip)}>
|
||||||
Copy IP address
|
Copy IP address
|
||||||
|
@@ -10,7 +10,7 @@ import { buttonVariants } from './components/ui/button.tsx'
|
|||||||
import { Github } from 'lucide-react'
|
import { Github } from 'lucide-react'
|
||||||
import { useStore } from '@nanostores/react'
|
import { useStore } from '@nanostores/react'
|
||||||
import { Toaster } from './components/ui/toaster.tsx'
|
import { Toaster } from './components/ui/toaster.tsx'
|
||||||
import { Can, Logo } from './components/logo.tsx'
|
import { Logo } from './components/logo.tsx'
|
||||||
import {
|
import {
|
||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -51,19 +51,20 @@ const Layout = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="flex items-center py-3.5 bg-card px-6 border bt-0 rounded-md my-5">
|
<div className="flex items-center h-16 bg-card px-6 border bt-0 rounded-md my-5">
|
||||||
<TooltipProvider delayDuration={300}>
|
<TooltipProvider delayDuration={300}>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
aria-label="Home"
|
aria-label="Home"
|
||||||
|
className={'p-2 pl-0 -mb-1'}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
navigate('/')
|
navigate('/')
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Logo className="h-5 fill-foreground" />
|
<Logo className="h-[1.1em] fill-foreground" />
|
||||||
</a>
|
</a>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
|
2
site/src/types.d.ts
vendored
2
site/src/types.d.ts
vendored
@@ -27,7 +27,7 @@ interface ContainerStats {
|
|||||||
name: string
|
name: string
|
||||||
cpu: number
|
cpu: number
|
||||||
mem: number
|
mem: number
|
||||||
mempct: number
|
memPct: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SystemStatsRecord extends RecordModel {
|
export interface SystemStatsRecord extends RecordModel {
|
||||||
|
Reference in New Issue
Block a user