wip: more layout & chartjs

pull/5/head
lyc8503 2023-10-15 16:44:00 +08:00
parent f9212c88a6
commit 4fba030932
7 changed files with 129 additions and 26 deletions

View File

@ -34,11 +34,9 @@ export default function Header() {
<header className={classes.header}>
<Container size="md" className={classes.inner}>
<MantineLogo size={28} />
<Group gap={5} visibleFrom="xs">
<Group gap={5}>
{items}
</Group>
<Burger opened={opened} onClick={toggle} hiddenFrom="xs" size="sm" />
</Container>
</header>
);

View File

View File

@ -0,0 +1,67 @@
import { MonitorState, MonitorTarget } from "@/uptime.types";
import { Card, Center, Divider, Text } from "@mantine/core";
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from 'chart.js';
import { Line } from "react-chartjs-2";
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
export const options = {
responsive: true,
interaction: {
mode: 'index' as const,
intersect: false,
},
plugins: {
legend: {
position: 'top' as const,
},
title: {
display: true,
text: 'Response times',
align: 'start' as const
},
},
};
const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
export const data = {
labels,
datasets: [
{
data: labels.map(() => Math.round(Math.random() * 1000)),
borderColor: 'rgb(255, 99, 132)',
backgroundColor: 'rgba(255, 99, 132, 0.5)',
}
],
};
export default function MonitorList({ config, state }: { config: any, state: MonitorState }) {
return (
<Center>
<Card shadow="sm" padding="lg" radius="md" ml="xl" mr="xl" mt="xl" withBorder style={{ width: '800px' }}>
{config.monitors.map((monitor: MonitorTarget) => (
<div key={monitor.id}>
<Card.Section>
<Text>{monitor.name}</Text>
<Line options={options} data={data} />
</Card.Section>
<Divider />
</div>
))}
</Card>
</Center>
);
}

View File

@ -1,32 +1,27 @@
import { Center, Space, Title } from '@mantine/core'
import { Center, Title } from '@mantine/core'
import { IconCircleCheck, IconAlertCircle } from '@tabler/icons-react'
export default function OverallStatus({ state }: { state: { overallUp: number, overallDown: number, lastUpdate: number }}) {
let statusString = ''
let icon = <IconAlertCircle style={{ width: 64, height: 64 }} />
let icon = <IconAlertCircle style={{ width: 64, height: 64, color: '#b91c1c' }} />
if (state.overallUp === 0 && state.overallDown === 0) {
statusString = 'No data yet'
} else if (state.overallUp === 0) {
statusString = 'All down'
statusString = 'All systems not operational'
} else if (state.overallDown === 0) {
statusString = 'All up'
icon = <IconCircleCheck style={{ width: 64, height: 64 }} />
statusString = 'All systems operational'
icon = <IconCircleCheck style={{ width: 64, height: 64, color: '#059669' }} />
} else {
statusString = `${state.overallUp} up, ${state.overallDown} down`
statusString = `Some systems not operational (${state.overallDown} out of ${state.overallUp + state.overallDown})`
}
return (<>
<Center>
{icon}
</Center>
<Space h="md"/>
<Center>
<Title order={1}>{statusString}</Title>
</Center>
<Center>
<Title order={5}>Last updated on: {new Date(state.lastUpdate * 1000).toLocaleString()}</Title>
</Center>
<Title mt="sm" style={{ textAlign: 'center' }} order={1}>{statusString}</Title>
<Title mt="sm" style={{ textAlign: 'center', color: '#70778c' }} order={5}>Last updated on: {new Date(state.lastUpdate * 1000).toLocaleString()} ({Math.round(Date.now() / 1000) - state.lastUpdate} sec ago)</Title>
</>)
}

46
package-lock.json generated
View File

@ -13,8 +13,10 @@
"@mantine/ds": "^7.1.3",
"@mantine/hooks": "^7.1.3",
"@tabler/icons-react": "^2.39.0",
"chart.js": "^4.4.0",
"next": "13.5.4",
"react": "^18",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18"
},
"devDependencies": {
@ -664,6 +666,11 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
"node_modules/@kurkle/color": {
"version": "0.3.2",
"resolved": "https://mirrors.cloud.tencent.com/npm/@kurkle/color/-/color-0.3.2.tgz",
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
},
"node_modules/@mantine/code-highlight": {
"version": "7.1.3",
"resolved": "https://mirrors.cloud.tencent.com/npm/@mantine/code-highlight/-/code-highlight-7.1.3.tgz",
@ -1534,6 +1541,17 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chart.js": {
"version": "4.4.0",
"resolved": "https://mirrors.cloud.tencent.com/npm/chart.js/-/chart.js-4.4.0.tgz",
"integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=7"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://mirrors.cloud.tencent.com/npm/chokidar/-/chokidar-3.5.3.tgz",
@ -4007,6 +4025,15 @@
"node": ">=0.10.0"
}
},
"node_modules/react-chartjs-2": {
"version": "5.2.0",
"resolved": "https://mirrors.cloud.tencent.com/npm/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz",
"integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==",
"peerDependencies": {
"chart.js": "^4.1.1",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://mirrors.cloud.tencent.com/npm/react-dom/-/react-dom-18.2.0.tgz",
@ -5489,6 +5516,11 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
"@kurkle/color": {
"version": "0.3.2",
"resolved": "https://mirrors.cloud.tencent.com/npm/@kurkle/color/-/color-0.3.2.tgz",
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
},
"@mantine/code-highlight": {
"version": "7.1.3",
"resolved": "https://mirrors.cloud.tencent.com/npm/@mantine/code-highlight/-/code-highlight-7.1.3.tgz",
@ -6072,6 +6104,14 @@
"supports-color": "^7.1.0"
}
},
"chart.js": {
"version": "4.4.0",
"resolved": "https://mirrors.cloud.tencent.com/npm/chart.js/-/chart.js-4.4.0.tgz",
"integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==",
"requires": {
"@kurkle/color": "^0.3.0"
}
},
"chokidar": {
"version": "3.5.3",
"resolved": "https://mirrors.cloud.tencent.com/npm/chokidar/-/chokidar-3.5.3.tgz",
@ -7871,6 +7911,12 @@
"loose-envify": "^1.1.0"
}
},
"react-chartjs-2": {
"version": "5.2.0",
"resolved": "https://mirrors.cloud.tencent.com/npm/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz",
"integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==",
"requires": {}
},
"react-dom": {
"version": "18.2.0",
"resolved": "https://mirrors.cloud.tencent.com/npm/react-dom/-/react-dom-18.2.0.tgz",

View File

@ -14,8 +14,10 @@
"@mantine/ds": "^7.1.3",
"@mantine/hooks": "^7.1.3",
"@tabler/icons-react": "^2.39.0",
"chart.js": "^4.4.0",
"next": "13.5.4",
"react": "^18",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18"
},
"devDependencies": {

View File

@ -1,3 +1,5 @@
'use client'
import Head from 'next/head'
import { Inter } from 'next/font/google'
@ -6,6 +8,7 @@ import { KVNamespace } from '@cloudflare/workers-types'
import config from '@/uptime.config'
import OverallStatus from '@/components/OverallStatus'
import Header from '@/components/Header'
import MonitorList from '@/components/MonitorList'
export const runtime = 'experimental-edge'
const inter = Inter({ subsets: ['latin'] })
@ -19,18 +22,10 @@ export default function Home({ state }: { state: MonitorState }) {
</Head>
<main className={inter.className}>
<Header />
<OverallStatus state={state} />
{
config.monitors.map(monitor => (
<div key={monitor.id}>
<h2>{monitor.name}</h2>
</div>
))
}
<p>{JSON.stringify(state)}</p>
<MonitorList config={config} state={state} />
{/* <p>{JSON.stringify(state)}</p> */}
</main>
</>
)