feat: notification form and modal
and global scrollbar
This commit is contained in:
parent
b6a26d8777
commit
53c0fd563d
@ -1,19 +1,40 @@
|
|||||||
import { Form, Input, Modal, ModalProps, Select } from 'antd';
|
import {
|
||||||
|
Button,
|
||||||
|
Form,
|
||||||
|
FormProps,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalProps,
|
||||||
|
Select,
|
||||||
|
} from 'antd';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
|
import { request } from '../../../api/request';
|
||||||
|
import { useEvent } from '../../../hooks/useEvent';
|
||||||
import { notificationStrategies } from './strategies';
|
import { notificationStrategies } from './strategies';
|
||||||
|
|
||||||
|
export interface NotificationFormValues {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
payload: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultValues: Omit<NotificationFormValues, 'payload'> = {
|
||||||
|
name: 'New Notification',
|
||||||
|
type: notificationStrategies[0].name,
|
||||||
|
};
|
||||||
|
|
||||||
interface NotificationInfoModalProps
|
interface NotificationInfoModalProps
|
||||||
extends Pick<ModalProps, 'open' | 'onOk' | 'onCancel'> {}
|
extends Pick<ModalProps, 'open' | 'onCancel'>,
|
||||||
|
Pick<FormProps, 'initialValues'> {
|
||||||
|
onSubmit: (values: NotificationFormValues) => void;
|
||||||
|
}
|
||||||
export const NotificationInfoModal: React.FC<NotificationInfoModalProps> =
|
export const NotificationInfoModal: React.FC<NotificationInfoModalProps> =
|
||||||
React.memo((props) => {
|
React.memo((props) => {
|
||||||
const [notificationType, setNotificationType] = useState(
|
const [form] = Form.useForm();
|
||||||
notificationStrategies[0].name
|
const typeValue = Form.useWatch('type', form);
|
||||||
);
|
|
||||||
|
|
||||||
const form = useMemo(() => {
|
const formEl = useMemo(() => {
|
||||||
const strategy = notificationStrategies.find(
|
const strategy = notificationStrategies.find((s) => s.name === typeValue);
|
||||||
(s) => s.name === notificationType
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!strategy) {
|
if (!strategy) {
|
||||||
return null;
|
return null;
|
||||||
@ -22,33 +43,68 @@ export const NotificationInfoModal: React.FC<NotificationInfoModalProps> =
|
|||||||
const Component = strategy.form;
|
const Component = strategy.form;
|
||||||
|
|
||||||
return <Component />;
|
return <Component />;
|
||||||
}, [notificationType]);
|
}, [typeValue]);
|
||||||
|
|
||||||
|
const handleSave = useEvent(async () => {
|
||||||
|
await form.validateFields();
|
||||||
|
const values = form.getFieldsValue();
|
||||||
|
const { name, type, ...payload } = values;
|
||||||
|
|
||||||
|
props.onSubmit({
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleTest = useEvent(async () => {
|
||||||
|
await form.validateFields();
|
||||||
|
const values = form.getFieldsValue();
|
||||||
|
const { name, type, ...payload } = values;
|
||||||
|
|
||||||
|
console.log('TODO', { name, type, payload });
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="Notification"
|
title="Notification"
|
||||||
|
destroyOnClose={true}
|
||||||
|
maskClosable={false}
|
||||||
|
centered={true}
|
||||||
open={props.open}
|
open={props.open}
|
||||||
onOk={props.onOk}
|
|
||||||
onCancel={props.onCancel}
|
onCancel={props.onCancel}
|
||||||
|
footer={
|
||||||
|
<div>
|
||||||
|
<Button onClick={handleTest}>Test</Button>
|
||||||
|
<Button type="primary" onClick={handleSave}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Form layout="vertical">
|
<div className="overflow-y-auto max-h-[80vh]">
|
||||||
<Form.Item label="Notification Type">
|
<Form
|
||||||
<Select
|
form={form}
|
||||||
value={notificationType}
|
layout="vertical"
|
||||||
onChange={(val) => setNotificationType(val)}
|
initialValues={props.initialValues ?? defaultValues}
|
||||||
>
|
>
|
||||||
{notificationStrategies.map((s) => (
|
<Form.Item label="Notification Type" name="type">
|
||||||
<Select.Option value={s.name}>{s.label}</Select.Option>
|
<Select>
|
||||||
))}
|
{notificationStrategies.map((s) => (
|
||||||
</Select>
|
<Select.Option key={s.name} value={s.name}>
|
||||||
</Form.Item>
|
{s.label}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label="Display Name" name="name">
|
<Form.Item label="Display Name" name="name">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
{form}
|
<Form.Item name="payload">{formEl}</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -4,31 +4,35 @@ import React from 'react';
|
|||||||
export const NotificationSMTP: React.FC = React.memo(() => {
|
export const NotificationSMTP: React.FC = React.memo(() => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form.Item label="Host" name="hostname">
|
<Form.Item label="Host" name="hostname" rules={[{ required: true }]}>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Port" name="port">
|
<Form.Item
|
||||||
|
label="Port"
|
||||||
|
name="port"
|
||||||
|
rules={[{ required: true }, { type: 'number', min: 0, max: 65535 }]}
|
||||||
|
>
|
||||||
<InputNumber max={65535} min={1} />
|
<InputNumber max={65535} min={1} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Security" name="security">
|
<Form.Item label="Security" name="security" initialValue={false}>
|
||||||
<Select>
|
<Select>
|
||||||
<Select.Option value={false}>None / STARTTLS (25, 587)</Select.Option>
|
<Select.Option value={false}>None / STARTTLS (25, 587)</Select.Option>
|
||||||
<Select.Option value={true}>TLS (465)</Select.Option>
|
<Select.Option value={true}>TLS (465)</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form name="ignoreTLS">
|
<Form.Item name="ignoreTLS">
|
||||||
<Checkbox>Ignore TLS Error</Checkbox>
|
<Checkbox>Ignore TLS Error</Checkbox>
|
||||||
</Form>
|
</Form.Item>
|
||||||
<Form.Item label="Username" name="username">
|
<Form.Item label="Username" name="username" rules={[{ required: true }]}>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Password" name="password">
|
<Form.Item label="Password" name="password" rules={[{ required: true }]}>
|
||||||
<Input.Password />
|
<Input.Password />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="From Email" name="from">
|
<Form.Item label="From Email" name="from" rules={[{ required: true }]}>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="To Email" name="to">
|
<Form.Item label="To Email" name="to" rules={[{ required: true }]}>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="CC" name="cc">
|
<Form.Item label="CC" name="cc">
|
||||||
|
@ -23,6 +23,38 @@ a {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 滚动条 */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-corner {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
@apply bg-black bg-opacity-10;
|
||||||
|
border-color: transparent;
|
||||||
|
background-clip: padding-box;
|
||||||
|
border-width: 2px;
|
||||||
|
border-style: solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
@apply bg-black bg-opacity-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
@apply bg-black bg-opacity-5;
|
||||||
|
border-color: transparent;
|
||||||
|
background-clip: padding-box;
|
||||||
|
border-width: 2px;
|
||||||
|
border-style: solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Button, List } from 'antd';
|
import { Button, List } from 'antd';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { NotificationInfoModal } from '../../components/modals/NotificationInfo';
|
import {
|
||||||
|
NotificationFormValues,
|
||||||
|
NotificationInfoModal,
|
||||||
|
} from '../../components/modals/NotificationInfo';
|
||||||
import { PageHeader } from '../../components/PageHeader';
|
import { PageHeader } from '../../components/PageHeader';
|
||||||
import { useEvent } from '../../hooks/useEvent';
|
import { useEvent } from '../../hooks/useEvent';
|
||||||
|
|
||||||
export const NotificationList: React.FC = React.memo(() => {
|
export const NotificationList: React.FC = React.memo(() => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const handleOk = useEvent(() => {
|
const handleSubmit = useEvent((values: NotificationFormValues) => {
|
||||||
console.log('ok');
|
console.log('ok', values);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -47,7 +50,7 @@ export const NotificationList: React.FC = React.memo(() => {
|
|||||||
|
|
||||||
<NotificationInfoModal
|
<NotificationInfoModal
|
||||||
open={open}
|
open={open}
|
||||||
onOk={handleOk}
|
onSubmit={handleSubmit}
|
||||||
onCancel={() => setOpen(false)}
|
onCancel={() => setOpen(false)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user