almost done: more and more css

pull/5/head
lyc8503 2023-10-16 12:09:48 +08:00
parent 4fba030932
commit 7b85b89325
6 changed files with 153 additions and 52 deletions

View File

@ -0,0 +1,93 @@
import { Line } from "react-chartjs-2"
import { Text } from "@mantine/core"
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, TimeScale } from 'chart.js'
import { MonitorState, MonitorTarget } from "@/uptime.types";
import 'chartjs-adapter-moment'
import { IconAlertCircle, IconCircleCheck } from "@tabler/icons-react";
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
TimeScale
)
export default function MonitorDetail({ monitor, state }: { monitor: MonitorTarget, state: MonitorState }) {
if (!state.latency[monitor.id]) return (
<>
<Text>{monitor.name}</Text>
<Text>No data available, please make sure you have deployed your workers with latest config and check your worker status!</Text>
</>
)
const statusIcon = (state.incident[monitor.id].slice(-1)[0] ?? { end: 'dummy' }).end === undefined ? <IconAlertCircle style={{ width: '1em', height: '1em', color: '#b91c1c' }} /> : <IconCircleCheck style={{ width: '1em', height: '1em', color: '#059669' }} />
const latencyData = state.latency[monitor.id].recent.map((point) => ({ x: point.time * 1000, y: point.ping, loc: point.loc }))
let data = {
datasets: [
{
data: latencyData,
borderColor: 'rgb(112, 119, 140)',
borderWidth: 2,
radius: 0,
cubicInterpolationMode: 'monotone' as const,
tension: 0.4
}
]
}
let options = {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: 'index' as const,
intersect: false,
},
plugins: {
tooltip: {
callbacks: {
label: (item: any) => {
if (item.parsed.y) {
return `${item.parsed.y}ms (${item.raw.loc})`
}
}
}
},
legend: {
display: false
},
title: {
display: true,
text: 'Response times(ms)',
align: 'start' as const
},
},
scales: {
x: {
type: 'time' as const,
ticks: {
source: 'auto' as const,
maxRotation: 0,
autoSkip: true
}
}
}
}
return (
<>
<Text mt='sm' style={{ display: 'flex', alignItems: 'center' }}>{statusIcon} {monitor.name}</Text>
<div style={{ height: '150px' }}>
<Line options={options} data={data} />
</div>
</>
)
}

View File

@ -1,50 +1,6 @@
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)',
}
],
};
import { Card, Center, Divider } from "@mantine/core";
import MonitorDetail from "./MonitorDetail";
export default function MonitorList({ config, state }: { config: any, state: MonitorState }) {
@ -53,15 +9,14 @@ export default function MonitorList({ config, state }: { config: any, state: Mon
<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 ml="xs" mr="xs">
<MonitorDetail monitor={monitor} state={state} />
</Card.Section>
<Divider />
</div>
))}
</Card>
</Center>
);
)
}

30
package-lock.json generated
View File

@ -14,6 +14,8 @@
"@mantine/hooks": "^7.1.3",
"@tabler/icons-react": "^2.39.0",
"chart.js": "^4.4.0",
"chartjs-adapter-moment": "^1.0.1",
"moment": "^2.29.4",
"next": "13.5.4",
"react": "^18",
"react-chartjs-2": "^5.2.0",
@ -1552,6 +1554,15 @@
"pnpm": ">=7"
}
},
"node_modules/chartjs-adapter-moment": {
"version": "1.0.1",
"resolved": "https://mirrors.cloud.tencent.com/npm/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz",
"integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==",
"peerDependencies": {
"chart.js": ">=3.0.0",
"moment": "^2.10.2"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://mirrors.cloud.tencent.com/npm/chokidar/-/chokidar-3.5.3.tgz",
@ -3480,6 +3491,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://mirrors.cloud.tencent.com/npm/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://mirrors.cloud.tencent.com/npm/ms/-/ms-2.1.2.tgz",
@ -6112,6 +6131,12 @@
"@kurkle/color": "^0.3.0"
}
},
"chartjs-adapter-moment": {
"version": "1.0.1",
"resolved": "https://mirrors.cloud.tencent.com/npm/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz",
"integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==",
"requires": {}
},
"chokidar": {
"version": "3.5.3",
"resolved": "https://mirrors.cloud.tencent.com/npm/chokidar/-/chokidar-3.5.3.tgz",
@ -7563,6 +7588,11 @@
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true
},
"moment": {
"version": "2.29.4",
"resolved": "https://mirrors.cloud.tencent.com/npm/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
},
"ms": {
"version": "2.1.2",
"resolved": "https://mirrors.cloud.tencent.com/npm/ms/-/ms-2.1.2.tgz",

View File

@ -15,6 +15,8 @@
"@mantine/hooks": "^7.1.3",
"@tabler/icons-react": "^2.39.0",
"chart.js": "^4.4.0",
"chartjs-adapter-moment": "^1.0.1",
"moment": "^2.29.4",
"next": "13.5.4",
"react": "^18",
"react-chartjs-2": "^5.2.0",

View File

@ -35,6 +35,6 @@ export default function Home({ state }: { state: MonitorState }) {
export async function getServerSideProps() {
const { UPTIMEFLARE_STATE } = process.env as unknown as { UPTIMEFLARE_STATE: KVNamespace }
const state = await UPTIMEFLARE_STATE.get('state', 'json') as unknown as MonitorState
return { props: { state } }
}

View File

@ -28,12 +28,33 @@ const config = {
method: 'GET',
target: 'https://cloudflare.com',
expectedCode: [200],
timeout: 5,
timeout: 10000,
headers: {
"User-Agent": "Uptimeflare"
},
body: undefined
},
{
id: 'blog',
name: 'My Blog',
method: 'GET',
target: 'https://blog.lyc8503.site',
timeout: 10000
},
{
id: 'pan',
name: 'My Fileshare',
method: 'GET',
target: 'https://pan.lyc8503.site',
timeout: 10000
},
{
id: 'server',
name: 'My Aliyun Server',
method: 'GET',
target: 'https://server.lyc8503.site',
timeout: 10000
},
{
id: 'broken-test',
name: 'Ping 1.1.1.1',