wip: more layout & chartjs
parent
f9212c88a6
commit
4fba030932
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -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>
|
||||
</>)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue