feat: add basic notification settings ui
This commit is contained in:
parent
254c5079c4
commit
c180d1d5f1
14
src/client/components/PageHeader.tsx
Normal file
14
src/client/components/PageHeader.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const PageHeader: React.FC<{
|
||||||
|
title: string;
|
||||||
|
action?: React.ReactNode;
|
||||||
|
}> = React.memo((props) => {
|
||||||
|
return (
|
||||||
|
<div className="h-24 flex items-center">
|
||||||
|
<div className="text-2xl flex-1">{props.title}</div>
|
||||||
|
{props.action}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
PageHeader.displayName = 'PageHeader';
|
@ -18,6 +18,7 @@ import { useRequest } from '../hooks/useRequest';
|
|||||||
import { useUserStore } from '../store/user';
|
import { useUserStore } from '../store/user';
|
||||||
import { useEvent } from '../hooks/useEvent';
|
import { useEvent } from '../hooks/useEvent';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
|
import { PageHeader } from './PageHeader';
|
||||||
|
|
||||||
export const WebsiteList: React.FC = React.memo(() => {
|
export const WebsiteList: React.FC = React.memo(() => {
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
@ -43,19 +44,21 @@ export const WebsiteList: React.FC = React.memo(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="h-24 flex items-center">
|
<PageHeader
|
||||||
<div className="text-2xl flex-1">Websites</div>
|
title="Websites"
|
||||||
<div>
|
action={
|
||||||
<Button
|
<div>
|
||||||
type="primary"
|
<Button
|
||||||
icon={<PlusOutlined />}
|
type="primary"
|
||||||
size="large"
|
icon={<PlusOutlined />}
|
||||||
onClick={() => setIsModalOpen(true)}
|
size="large"
|
||||||
>
|
onClick={() => setIsModalOpen(true)}
|
||||||
Add Website
|
>
|
||||||
</Button>
|
Add Website
|
||||||
</div>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<WebsiteListTable workspaceId={currentWorkspace.id} />
|
<WebsiteListTable workspaceId={currentWorkspace.id} />
|
||||||
|
|
||||||
|
55
src/client/components/modals/NotificationInfo/index.tsx
Normal file
55
src/client/components/modals/NotificationInfo/index.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { Form, Input, Modal, ModalProps, Select } from 'antd';
|
||||||
|
import React, { useMemo, useState } from 'react';
|
||||||
|
import { notificationStrategies } from './strategies';
|
||||||
|
|
||||||
|
interface NotificationInfoModalProps
|
||||||
|
extends Pick<ModalProps, 'open' | 'onOk' | 'onCancel'> {}
|
||||||
|
export const NotificationInfoModal: React.FC<NotificationInfoModalProps> =
|
||||||
|
React.memo((props) => {
|
||||||
|
const [notificationType, setNotificationType] = useState(
|
||||||
|
notificationStrategies[0].name
|
||||||
|
);
|
||||||
|
|
||||||
|
const form = useMemo(() => {
|
||||||
|
const strategy = notificationStrategies.find(
|
||||||
|
(s) => s.name === notificationType
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!strategy) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Component = strategy.form;
|
||||||
|
|
||||||
|
return <Component />;
|
||||||
|
}, [notificationType]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="Notification"
|
||||||
|
open={props.open}
|
||||||
|
onOk={props.onOk}
|
||||||
|
onCancel={props.onCancel}
|
||||||
|
>
|
||||||
|
<Form layout="vertical">
|
||||||
|
<Form.Item label="Notification Type">
|
||||||
|
<Select
|
||||||
|
value={notificationType}
|
||||||
|
onChange={(val) => setNotificationType(val)}
|
||||||
|
>
|
||||||
|
{notificationStrategies.map((s) => (
|
||||||
|
<Select.Option value={s.name}>{s.label}</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="Display Name" name="name">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{form}
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
NotificationInfoModal.displayName = 'NotificationInfoModal';
|
@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { NotificationSMTP } from './smtp';
|
||||||
|
|
||||||
|
interface NotificationStrategy {
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
form: React.ComponentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const notificationStrategies: NotificationStrategy[] = [
|
||||||
|
{
|
||||||
|
label: 'Email(SMTP)',
|
||||||
|
name: 'smtp',
|
||||||
|
form: NotificationSMTP,
|
||||||
|
},
|
||||||
|
];
|
@ -0,0 +1,43 @@
|
|||||||
|
import { Checkbox, Form, Input, InputNumber, Select } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const NotificationSMTP: React.FC = React.memo(() => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item label="Host" name="hostname">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Port" name="port">
|
||||||
|
<InputNumber max={65535} min={1} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Security" name="security">
|
||||||
|
<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">
|
||||||
|
<Checkbox>Ignore TLS Error</Checkbox>
|
||||||
|
</Form>
|
||||||
|
<Form.Item label="Username" name="username">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Password" name="password">
|
||||||
|
<Input.Password />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="From Email" name="from">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="To Email" name="to">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="CC" name="cc">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="BCC" name="bcc">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
NotificationSMTP.displayName = 'NotificationSMTP';
|
56
src/client/pages/Settings/NotificationList.tsx
Normal file
56
src/client/pages/Settings/NotificationList.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, List } from 'antd';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { 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');
|
||||||
|
setOpen(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PageHeader
|
||||||
|
title="Notification List"
|
||||||
|
action={
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
size="large"
|
||||||
|
onClick={() => setOpen(true)}
|
||||||
|
>
|
||||||
|
New
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<List
|
||||||
|
bordered={true}
|
||||||
|
dataSource={[
|
||||||
|
{ id: '1', name: 'Email Notify' },
|
||||||
|
{ id: '1', name: 'Email Notify' },
|
||||||
|
{ id: '1', name: 'Email Notify' },
|
||||||
|
]}
|
||||||
|
renderItem={(item) => (
|
||||||
|
<List.Item actions={[<Button>edit</Button>]}>
|
||||||
|
<List.Item.Meta title={item.name} />
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<NotificationInfoModal
|
||||||
|
open={open}
|
||||||
|
onOk={handleOk}
|
||||||
|
onCancel={() => setOpen(false)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
NotificationList.displayName = 'NotificationList';
|
@ -4,6 +4,7 @@ import { Routes, Route, useLocation, useNavigate } from 'react-router-dom';
|
|||||||
import { WebsiteInfo } from '../../components/WebsiteInfo';
|
import { WebsiteInfo } from '../../components/WebsiteInfo';
|
||||||
import { WebsiteList } from '../../components/WebsiteList';
|
import { WebsiteList } from '../../components/WebsiteList';
|
||||||
import { useEvent } from '../../hooks/useEvent';
|
import { useEvent } from '../../hooks/useEvent';
|
||||||
|
import { NotificationList } from './NotificationList';
|
||||||
|
|
||||||
export const SettingsPage: React.FC = React.memo(() => {
|
export const SettingsPage: React.FC = React.memo(() => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -14,6 +15,10 @@ export const SettingsPage: React.FC = React.memo(() => {
|
|||||||
key: 'websites',
|
key: 'websites',
|
||||||
label: 'Websites',
|
label: 'Websites',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'notifications',
|
||||||
|
label: 'Notifications',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const onClick: MenuProps['onClick'] = useEvent((e) => {
|
const onClick: MenuProps['onClick'] = useEvent((e) => {
|
||||||
@ -40,6 +45,7 @@ export const SettingsPage: React.FC = React.memo(() => {
|
|||||||
<Route path="/" element={<WebsiteList />} />
|
<Route path="/" element={<WebsiteList />} />
|
||||||
<Route path="/websites" element={<WebsiteList />} />
|
<Route path="/websites" element={<WebsiteList />} />
|
||||||
<Route path="/website/:websiteId" element={<WebsiteInfo />} />
|
<Route path="/website/:websiteId" element={<WebsiteInfo />} />
|
||||||
|
<Route path="/notifications" element={<NotificationList />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user