domain-watchdog/assets/pages/tracking/WatchlistPage.tsx

278 lines
12 KiB
TypeScript
Raw Normal View History

2024-07-27 18:53:07 +02:00
import React, {useEffect, useState} from "react";
2024-07-27 20:44:10 +02:00
import {Button, Card, Divider, Flex, Form, Input, message, Popconfirm, Select, Skeleton, Space, Typography} from "antd";
2024-07-27 18:53:07 +02:00
2024-07-27 20:44:10 +02:00
import {DeleteFilled, MinusCircleOutlined, PlusOutlined, ThunderboltFilled} from "@ant-design/icons";
2024-07-27 18:53:07 +02:00
import {deleteWatchlist, EventAction, getWatchlists, postWatchlist} from "../../utils/api";
import {AxiosError} from "axios";
2024-07-28 15:36:22 +02:00
import {t} from 'ttag'
2024-07-27 18:53:07 +02:00
const formItemLayout = {
labelCol: {
xs: {span: 24},
sm: {span: 4},
},
wrapperCol: {
xs: {span: 24},
sm: {span: 20},
},
};
const formItemLayoutWithOutLabel = {
wrapperCol: {
xs: {span: 24, offset: 0},
sm: {span: 20, offset: 4},
},
};
const triggerEventItems: { label: string, value: EventAction }[] = [
{
2024-07-28 15:36:22 +02:00
label: t`When a domain is expired`,
2024-07-27 18:53:07 +02:00
value: 'expiration'
},
{
2024-07-28 15:36:22 +02:00
label: t`When a domain is deleted`,
2024-07-27 18:53:07 +02:00
value: 'deletion'
},
2024-07-28 18:02:01 +02:00
{
label: t`When a domain is updated`,
value: 'last changed'
},
2024-07-27 18:53:07 +02:00
{
2024-07-28 15:36:22 +02:00
label: t`When a domain is transferred`,
2024-07-27 18:53:07 +02:00
value: 'transfer'
},
{
2024-07-28 15:36:22 +02:00
label: t`When a domain is locked`,
2024-07-27 18:53:07 +02:00
value: 'locked'
},
{
2024-07-28 15:36:22 +02:00
label: t`When a domain is unlocked`,
2024-07-27 18:53:07 +02:00
value: 'unlocked'
},
{
2024-07-28 15:36:22 +02:00
label: t`When a domain is reregistered`,
2024-07-27 18:53:07 +02:00
value: 'reregistration'
},
{
2024-07-28 15:36:22 +02:00
label: t`When a domain is reinstantiated`,
2024-07-27 18:53:07 +02:00
value: 'reinstantiation'
2024-07-28 18:02:01 +02:00
},
{
label: t`When a domain is registered`,
value: 'registration'
2024-07-27 18:53:07 +02:00
}
]
const trigerActionItems = [
{
2024-07-28 15:36:22 +02:00
label: t`Send me an email`,
2024-07-27 18:53:07 +02:00
value: 'email'
}
]
2024-07-27 17:19:25 +02:00
2024-07-27 20:44:10 +02:00
type Watchlist = { token: string, domains: { ldhName: string }[], triggers?: { event: EventAction, action: string }[] }
2024-07-27 17:19:25 +02:00
export default function WatchlistPage() {
2024-07-27 18:53:07 +02:00
const [form] = Form.useForm()
const [messageApi, contextHolder] = message.useMessage()
2024-07-27 20:44:10 +02:00
const [watchlists, setWatchlists] = useState<Watchlist[] | null>()
2024-07-27 18:53:07 +02:00
const onCreateWatchlist = (values: { domains: string[], triggers: { event: string, action: string }[] }) => {
const domainsURI = values.domains.map(d => '/api/domains/' + d)
postWatchlist(domainsURI, values.triggers).then((w) => {
form.resetFields()
refreshWatchlists()
2024-07-28 15:36:22 +02:00
messageApi.success(t`Watchlist created !`)
2024-07-27 18:53:07 +02:00
}).catch((e: AxiosError) => {
const data = e?.response?.data as { detail: string }
2024-07-28 15:36:22 +02:00
messageApi.error(data.detail ?? t`An error occurred`)
2024-07-27 18:53:07 +02:00
})
}
const refreshWatchlists = () => getWatchlists().then(w => {
setWatchlists(w['hydra:member'])
}).catch(() => setWatchlists(undefined))
useEffect(() => {
refreshWatchlists()
}, [])
return <Flex gap="middle" align="center" justify="center" vertical>
2024-07-28 15:36:22 +02:00
<Card title={t`Create a watchlist`} style={{width: '100%'}}>
2024-07-27 18:53:07 +02:00
{contextHolder}
<Form
{...formItemLayoutWithOutLabel}
form={form}
onFinish={onCreateWatchlist}
>
<Form.List
name="domains"
rules={[
{
validator: async (_, domains) => {
if (!domains || domains.length < 1) {
2024-07-28 15:36:22 +02:00
return Promise.reject(new Error(t`At least one domain name`));
2024-07-27 18:53:07 +02:00
}
},
},
]}
>
{(fields, {add, remove}, {errors}) => (
<>
{fields.map((field, index) => (
<Form.Item
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
2024-07-28 15:36:22 +02:00
label={index === 0 ? t`Domain names` : ''}
2024-07-27 18:53:07 +02:00
required={true}
key={field.key}
>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
rules={[{
required: true,
2024-07-28 15:36:22 +02:00
message: t`Required`
2024-07-27 18:53:07 +02:00
}, {
pattern: /^(?=.*\.)\S*[^.\s]$/,
2024-07-28 15:36:22 +02:00
message: t`This domain name does not appear to be valid`,
2024-07-27 18:53:07 +02:00
max: 63,
min: 2
}]}
noStyle
>
2024-07-28 15:36:22 +02:00
<Input placeholder={t`Domain name`} style={{width: '60%'}} autoComplete='off'/>
2024-07-27 18:53:07 +02:00
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
onClick={() => remove(field.name)}
/>
) : null}
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
style={{width: '60%'}}
icon={<PlusOutlined/>}
>
2024-07-28 15:36:22 +02:00
{t`Add a Domain name`}
2024-07-27 18:53:07 +02:00
</Button>
<Form.ErrorList errors={errors}/>
</Form.Item>
</>
)}
</Form.List>
2024-07-27 20:44:10 +02:00
<Form.List
name="triggers"
rules={[
{
validator: async (_, domains) => {
if (!domains || domains.length < 1) {
2024-07-28 15:36:22 +02:00
return Promise.reject(new Error(t`At least one domain trigger`));
2024-07-27 20:44:10 +02:00
}
},
},
]}
>
{(fields, {add, remove}, {errors}) => (
2024-07-27 18:53:07 +02:00
<>
2024-07-27 20:44:10 +02:00
{fields.map((field, index) => (
<Form.Item
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
2024-07-28 15:36:22 +02:00
label={index === 0 ? t`Domain trigger` : ''}
2024-07-27 20:44:10 +02:00
required={true}
2024-07-27 18:53:07 +02:00
key={field.key}
>
2024-07-27 20:44:10 +02:00
<Space wrap>
<Form.Item {...field}
validateTrigger={['onChange', 'onBlur']}
rules={[{
required: true,
2024-07-28 15:36:22 +02:00
message: t`Required`
2024-07-27 20:44:10 +02:00
}]}
noStyle name={[field.name, 'event']}>
<Select style={{minWidth: 300}} options={triggerEventItems} showSearch
2024-07-28 15:36:22 +02:00
placeholder={t`If this happens`} optionFilterProp="label"/>
2024-07-27 20:44:10 +02:00
</Form.Item>
<Form.Item {...field}
validateTrigger={['onChange', 'onBlur']}
rules={[{
required: true,
2024-07-28 15:36:22 +02:00
message: t`Required`
2024-07-27 20:44:10 +02:00
}]}
noStyle name={[field.name, 'action']}>
<Select style={{minWidth: 300}} options={trigerActionItems} showSearch
2024-07-28 15:36:22 +02:00
placeholder={t`Then do that`}
2024-07-27 20:44:10 +02:00
optionFilterProp="label"/>
</Form.Item>
</Space>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
onClick={() => remove(field.name)}
2024-07-27 18:53:07 +02:00
/>
2024-07-27 20:44:10 +02:00
) : null}
</Form.Item>
2024-07-27 18:53:07 +02:00
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
style={{width: '60%'}}
icon={<ThunderboltFilled/>}
>
2024-07-28 15:36:22 +02:00
{t`Add a Trigger`}
2024-07-27 18:53:07 +02:00
</Button>
2024-07-27 20:44:10 +02:00
<Form.ErrorList errors={errors}/>
2024-07-27 18:53:07 +02:00
</Form.Item>
</>
)}
</Form.List>
<Form.Item>
<Space>
<Button type="primary" htmlType="submit">
2024-07-28 15:36:22 +02:00
{t`Create`}
2024-07-27 18:53:07 +02:00
</Button>
<Button type="default" htmlType="reset">
2024-07-28 15:36:22 +02:00
{t`Reset`}
2024-07-27 18:53:07 +02:00
</Button>
</Space>
</Form.Item>
</Form>
</Card>
2024-07-27 20:44:10 +02:00
<Skeleton loading={watchlists === undefined} active>
{watchlists && watchlists.length > 0 && <Card title="My Watchlists" style={{width: '100%'}}>
{watchlists.map(watchlist =>
2024-07-27 18:53:07 +02:00
<>
2024-07-28 15:36:22 +02:00
<Card title={t`Watchlist ${watchlist.token}`} extra={<Popconfirm
2024-07-28 18:47:07 +02:00
title={t`Delete the Watchlist`}
2024-07-28 15:36:22 +02:00
description={t`Are you sure to delete this Watchlist?`}
2024-07-27 20:44:10 +02:00
onConfirm={() => deleteWatchlist(watchlist.token).then(refreshWatchlists)}
2024-07-28 18:47:07 +02:00
okText={t`Yes`}
2024-07-28 15:36:22 +02:00
cancelText={t`No`}
2024-07-27 20:44:10 +02:00
><DeleteFilled/> </Popconfirm>}>
<Typography.Paragraph>
2024-07-28 15:36:22 +02:00
{t`Domain name`} : {watchlist?.domains.map(d => d.ldhName).join(',')}
2024-07-27 20:44:10 +02:00
</Typography.Paragraph>
{
watchlist.triggers && <Typography.Paragraph>
2024-07-28 15:36:22 +02:00
{t`Domain triggers`} : {watchlist.triggers.map(t => `${t.event} => ${t.action}`).join(',')}
2024-07-27 20:44:10 +02:00
</Typography.Paragraph>
}
2024-07-27 18:53:07 +02:00
</Card>
2024-07-27 20:44:10 +02:00
<Divider/>
2024-07-27 18:53:07 +02:00
</>
)}
2024-07-27 20:44:10 +02:00
</Card>
}
</Skeleton>
2024-07-27 18:53:07 +02:00
</Flex>
2024-07-27 17:19:25 +02:00
}