feat: add monitor editor

This commit is contained in:
moonrailgun 2023-10-05 21:09:03 +08:00
parent a92dd513f1
commit ea5c237c29
15 changed files with 144 additions and 32 deletions

View File

@ -1,17 +0,0 @@
import React from 'react';
import type { Monitor } from '@prisma/client';
type MonitorInfoEditorValues = Omit<Monitor, 'id'> & {
id?: string;
};
interface MonitorInfoEditorProps {
initValue?: MonitorInfoEditorValues;
onSave: (value: MonitorInfoEditorValues) => void;
}
export const MonitorInfoEditor: React.FC<MonitorInfoEditorProps> = React.memo(
(props) => {
return <div>MonitorInfoEditor</div>;
}
);
MonitorInfoEditor.displayName = 'MonitorInfoEditor';

View File

@ -3,7 +3,6 @@ import {
Form,
FormProps,
Input,
message,
Modal,
ModalProps,
Select,

View File

@ -0,0 +1,92 @@
import React, { useMemo } from 'react';
import type { Monitor } from '@prisma/client';
import { Button, Form, Input, InputNumber, Select } from 'antd';
import { monitorProviders } from './provider';
import { useEvent } from '../../../hooks/useEvent';
type MonitorInfoEditorValues = Omit<
Monitor,
'id' | 'workspaceId' | 'createdAt'
> & {
id?: string;
};
const defaultValues: Omit<MonitorInfoEditorValues, 'payload'> = {
name: 'New Monitor',
type: monitorProviders[0].name,
active: true,
interval: 60,
};
interface MonitorInfoEditorProps {
initialValues?: MonitorInfoEditorValues;
onSave: (value: MonitorInfoEditorValues) => void;
}
export const MonitorInfoEditor: React.FC<MonitorInfoEditorProps> = React.memo(
(props) => {
const [form] = Form.useForm();
const typeValue = Form.useWatch('type', form);
const formEl = useMemo(() => {
const provider = monitorProviders.find((s) => s.name === typeValue);
if (!provider) {
return null;
}
const Component = provider.form;
return <Component />;
}, [typeValue]);
const handleSubmit = useEvent((values) => {
props.onSave({
...values,
active: true,
});
});
return (
<div className="px-4">
<Form
preserve={false}
form={form}
layout="vertical"
initialValues={props.initialValues ?? defaultValues}
onFinish={handleSubmit}
>
<Form.Item hidden name="id" />
<Form.Item label="Monitor Type" name="type">
<Select>
{monitorProviders.map((m) => (
<Select.Option key={m.name} value={m.name}>
{m.label}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="Name" name="name" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Form.Item
label="Check Interval"
name="interval"
rules={[{ required: true }]}
>
<InputNumber min={5} max={10000} step={10} />
</Form.Item>
{formEl}
<Button type="primary" htmlType="submit">
Save
</Button>
</Form>
</div>
);
}
);
MonitorInfoEditor.displayName = 'MonitorInfoEditor';

View File

@ -1,9 +1,9 @@
import clsx from 'clsx';
import React, { useState } from 'react';
import { trpc } from '../api/trpc';
import { useCurrentWorkspaceId } from '../store/user';
import { HealthBar } from './HealthBar';
import { NoWorkspaceTip } from './NoWorkspaceTip';
import { trpc } from '../../../api/trpc';
import { useCurrentWorkspaceId } from '../../../store/user';
import { HealthBar } from '../../HealthBar';
import { NoWorkspaceTip } from '../../NoWorkspaceTip';
export const MonitorList: React.FC = React.memo(() => {
const currentWorkspaceId = useCurrentWorkspaceId()!;

View File

@ -0,0 +1,16 @@
import React from 'react';
import { MonitorPing } from './ping';
interface MonitorProvider {
label: string;
name: string;
form: React.ComponentType;
}
export const monitorProviders: MonitorProvider[] = [
{
label: 'Ping',
name: 'ping',
form: MonitorPing,
},
];

View File

@ -0,0 +1,17 @@
import { Form, Input } from 'antd';
import React from 'react';
export const MonitorPing: React.FC = React.memo(() => {
return (
<>
<Form.Item
label="Host"
name={['payload', 'hostname']}
rules={[{ required: true }]}
>
<Input placeholder="example.com or 1.2.3.4" />
</Form.Item>
</>
);
});
MonitorPing.displayName = 'MonitorPing';

View File

@ -28,7 +28,7 @@ export const Layout: React.FC = React.memo(() => {
<NavItem to="/dashboard" label="Dashboard" />
<NavItem to="/monitor" label="Monitor" />
<NavItem to="/website" label="Website" />
<NavItem to="/Servers" label="Servers" />
<NavItem to="/servers" label="Servers" />
<NavItem to="/settings" label="Settings" />
</div>

View File

@ -1,5 +1,5 @@
import React from 'react';
import { MonitorInfoEditor } from '../../components/MonitorInfoEditor';
import { MonitorInfoEditor } from '../../components/modals/monitor/MonitorInfoEditor';
import { useCurrentWorkspaceId } from '../../store/user';
export const MonitorAdd: React.FC = React.memo(() => {

View File

@ -3,7 +3,7 @@ import { useParams } from 'react-router';
import { trpc } from '../../api/trpc';
import { ErrorTip } from '../../components/ErrorTip';
import { Loading } from '../../components/Loading';
import { MonitorInfoEditor } from '../../components/MonitorInfoEditor';
import { MonitorInfoEditor } from '../../components/modals/monitor/MonitorInfoEditor';
import { useCurrentWorkspaceId } from '../../store/user';
export const MonitorEdit: React.FC = React.memo(() => {
@ -25,7 +25,7 @@ export const MonitorEdit: React.FC = React.memo(() => {
return (
<div>
<MonitorInfoEditor
initValue={monitor}
initialValues={monitor}
onSave={(value) => {
console.log(value);
}}

View File

@ -1,6 +1,6 @@
import React from 'react';
import { Route, Routes, useNavigate } from 'react-router';
import { MonitorList } from '../../components/MonitorList';
import { MonitorList } from '../../components/modals/monitor/MonitorList';
import { MonitorAdd } from './Add';
import { MonitorDetail } from './Detail';
import { MonitorEdit } from './Edit';

View File

@ -1,6 +1,6 @@
import { ping } from './ping';
import type { MonitorProvider } from './type';
export const monitorProviders: Record<string, MonitorProvider> = {
export const monitorProviders: Record<string, MonitorProvider<any>> = {
ping,
};

View File

@ -1,13 +1,15 @@
import { MonitorProvider } from './type';
import pingUtils from 'ping';
export const ping: MonitorProvider = {
export const ping: MonitorProvider<{
hostname: string;
}> = {
run: async (monitor) => {
if (typeof monitor.payload !== 'object') {
throw new Error('monitor.payload should be object');
}
const { hostname } = monitor.payload as any;
const { hostname } = monitor.payload;
const res = await pingAction(hostname);

View File

@ -1,5 +1,6 @@
import { Monitor } from '@prisma/client';
import type { ExactType } from '../../../../types';
export interface MonitorProvider {
run: (monitor: Monitor) => Promise<number>;
export interface MonitorProvider<Payload extends Record<string, any>> {
run: (monitor: ExactType<Monitor, { payload: Payload }>) => Promise<number>;
}

View File

@ -1 +1,2 @@
export * from './server';
export * from './utils';

1
src/types/utils.ts Normal file
View File

@ -0,0 +1 @@
export type ExactType<T, U extends Partial<T>> = Omit<T, keyof U> & U;