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 { request } from '../../../api/request';
|
||||
import { useEvent } from '../../../hooks/useEvent';
|
||||
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
|
||||
extends Pick<ModalProps, 'open' | 'onOk' | 'onCancel'> {}
|
||||
extends Pick<ModalProps, 'open' | 'onCancel'>,
|
||||
Pick<FormProps, 'initialValues'> {
|
||||
onSubmit: (values: NotificationFormValues) => void;
|
||||
}
|
||||
export const NotificationInfoModal: React.FC<NotificationInfoModalProps> =
|
||||
React.memo((props) => {
|
||||
const [notificationType, setNotificationType] = useState(
|
||||
notificationStrategies[0].name
|
||||
);
|
||||
const [form] = Form.useForm();
|
||||
const typeValue = Form.useWatch('type', form);
|
||||
|
||||
const form = useMemo(() => {
|
||||
const strategy = notificationStrategies.find(
|
||||
(s) => s.name === notificationType
|
||||
);
|
||||
const formEl = useMemo(() => {
|
||||
const strategy = notificationStrategies.find((s) => s.name === typeValue);
|
||||
|
||||
if (!strategy) {
|
||||
return null;
|
||||
@ -22,23 +43,57 @@ export const NotificationInfoModal: React.FC<NotificationInfoModalProps> =
|
||||
const Component = strategy.form;
|
||||
|
||||
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 (
|
||||
<Modal
|
||||
title="Notification"
|
||||
destroyOnClose={true}
|
||||
maskClosable={false}
|
||||
centered={true}
|
||||
open={props.open}
|
||||
onOk={props.onOk}
|
||||
onCancel={props.onCancel}
|
||||
footer={
|
||||
<div>
|
||||
<Button onClick={handleTest}>Test</Button>
|
||||
<Button type="primary" onClick={handleSave}>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Form layout="vertical">
|
||||
<Form.Item label="Notification Type">
|
||||
<Select
|
||||
value={notificationType}
|
||||
onChange={(val) => setNotificationType(val)}
|
||||
<div className="overflow-y-auto max-h-[80vh]">
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={props.initialValues ?? defaultValues}
|
||||
>
|
||||
<Form.Item label="Notification Type" name="type">
|
||||
<Select>
|
||||
{notificationStrategies.map((s) => (
|
||||
<Select.Option value={s.name}>{s.label}</Select.Option>
|
||||
<Select.Option key={s.name} value={s.name}>
|
||||
{s.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
@ -47,8 +102,9 @@ export const NotificationInfoModal: React.FC<NotificationInfoModalProps> =
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
{form}
|
||||
<Form.Item name="payload">{formEl}</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
|
@ -4,31 +4,35 @@ import React from 'react';
|
||||
export const NotificationSMTP: React.FC = React.memo(() => {
|
||||
return (
|
||||
<>
|
||||
<Form.Item label="Host" name="hostname">
|
||||
<Form.Item label="Host" name="hostname" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</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} />
|
||||
</Form.Item>
|
||||
<Form.Item label="Security" name="security">
|
||||
<Form.Item label="Security" name="security" initialValue={false}>
|
||||
<Select>
|
||||
<Select.Option value={false}>None / STARTTLS (25, 587)</Select.Option>
|
||||
<Select.Option value={true}>TLS (465)</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form name="ignoreTLS">
|
||||
<Form.Item name="ignoreTLS">
|
||||
<Checkbox>Ignore TLS Error</Checkbox>
|
||||
</Form>
|
||||
<Form.Item label="Username" name="username">
|
||||
</Form.Item>
|
||||
<Form.Item label="Username" name="username" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label="Password" name="password">
|
||||
<Form.Item label="Password" name="password" rules={[{ required: true }]}>
|
||||
<Input.Password />
|
||||
</Form.Item>
|
||||
<Form.Item label="From Email" name="from">
|
||||
<Form.Item label="From Email" name="from" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label="To Email" name="to">
|
||||
<Form.Item label="To Email" name="to" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label="CC" name="cc">
|
||||
|
@ -23,6 +23,38 @@ a {
|
||||
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 components;
|
||||
@tailwind utilities;
|
||||
|
@ -1,15 +1,18 @@
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, List } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { NotificationInfoModal } from '../../components/modals/NotificationInfo';
|
||||
import {
|
||||
NotificationFormValues,
|
||||
NotificationInfoModal,
|
||||
} from '../../components/modals/NotificationInfo';
|
||||
import { PageHeader } from '../../components/PageHeader';
|
||||
import { useEvent } from '../../hooks/useEvent';
|
||||
|
||||
export const NotificationList: React.FC = React.memo(() => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleOk = useEvent(() => {
|
||||
console.log('ok');
|
||||
const handleSubmit = useEvent((values: NotificationFormValues) => {
|
||||
console.log('ok', values);
|
||||
setOpen(false);
|
||||
});
|
||||
|
||||
@ -47,7 +50,7 @@ export const NotificationList: React.FC = React.memo(() => {
|
||||
|
||||
<NotificationInfoModal
|
||||
open={open}
|
||||
onOk={handleOk}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={() => setOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user