280 lines
11 KiB
TypeScript
Raw Normal View History

2024-12-31 13:55:42 +01:00
import type { FormInstance, SelectProps} from 'antd'
import {Button, Form, Input, Select, Space, Tag, Tooltip, Typography} from 'antd'
2024-12-30 23:50:15 +01:00
import {t} from 'ttag'
import {ApiOutlined, MinusCircleOutlined, PlusOutlined} from '@ant-design/icons'
2025-10-15 19:52:44 +02:00
import React from 'react'
2024-12-31 13:55:42 +01:00
import type {Connector} from '../../../utils/api/connectors'
2024-12-30 23:50:15 +01:00
import {rdapEventDetailTranslation, rdapEventNameTranslation} from '../../../utils/functions/rdapTranslation'
import {actionToColor} from '../../../utils/functions/actionToColor'
import {actionToIcon} from '../../../utils/functions/actionToIcon'
2025-05-30 13:31:16 +02:00
import type {EventAction, Watchlist} from '../../../utils/api'
2025-02-18 21:53:04 +01:00
import {formItemLayoutWithOutLabel} from "../../../utils/providers"
2024-12-30 23:50:15 +01:00
type TagRender = SelectProps['tagRender']
const formItemLayout = {
labelCol: {
xs: {span: 24},
2024-12-30 23:50:15 +01:00
sm: {span: 4}
},
wrapperCol: {
xs: {span: 24},
2024-12-30 23:50:15 +01:00
sm: {span: 20}
}
}
2025-10-15 19:52:44 +02:00
export function WatchlistForm({form, connectors, onFinish, isCreation}: {
2024-12-30 23:50:15 +01:00
form: FormInstance
connectors: Array<Connector & { id: string }>
2025-10-15 19:52:44 +02:00
onFinish: (values: { domains: string[], trackedEvents: string[], token: string }) => void
2025-05-22 14:00:17 +02:00
isCreation: boolean,
watchList?: Watchlist,
}) {
2024-08-20 15:22:14 +02:00
const rdapEventNameTranslated = rdapEventNameTranslation()
2024-08-20 19:15:04 +02:00
const rdapEventDetailTranslated = rdapEventDetailTranslation()
2024-12-30 23:50:15 +01:00
const triggerTagRenderer: TagRender = ({value, closable, onClose}: {
value: EventAction
closable: boolean
onClose: () => void
}) => {
2024-08-03 18:56:37 +02:00
const onPreventMouseDown = (event: React.MouseEvent<HTMLSpanElement>) => {
2024-08-15 03:04:31 +02:00
event.preventDefault()
event.stopPropagation()
}
2024-12-30 23:50:15 +01:00
return (
<Tooltip
title={rdapEventDetailTranslated[value as keyof typeof rdapEventDetailTranslated] || undefined}
>
2024-08-20 19:15:04 +02:00
<Tag
2024-08-21 02:01:20 +02:00
icon={actionToIcon(value)}
2024-08-20 19:15:04 +02:00
color={actionToColor(value)}
onMouseDown={onPreventMouseDown}
closable={closable}
onClose={onClose}
style={{marginInlineEnd: 4}}
>
{rdapEventNameTranslated[value as keyof typeof rdapEventNameTranslated]}
</Tag>
</Tooltip>
2024-08-03 18:56:37 +02:00
)
}
2024-12-30 23:50:15 +01:00
return (
<Form
{...formItemLayoutWithOutLabel}
form={form}
onFinish={onFinish}
2025-10-15 19:52:44 +02:00
initialValues={{trackedEvents: ['last changed', 'transfer', 'expiration', 'deletion']}}
2024-12-30 23:50:15 +01:00
>
2024-08-15 03:04:31 +02:00
2024-12-30 23:50:15 +01:00
<Form.Item name='token' hidden>
<Input hidden/>
</Form.Item>
2024-08-15 03:04:31 +02:00
2024-12-30 23:50:15 +01:00
<Form.Item
label={t`Name`}
name='name'
labelCol={{
xs: {span: 24},
sm: {span: 4}
}}
wrapperCol={{
md: {span: 12},
sm: {span: 20}
}}
>
<Input
placeholder={t`Watchlist Name`}
title={t`Naming the Watchlist makes it easier to find in the list below.`}
autoComplete='off'
autoFocus
/>
</Form.Item>
<Form.List
name='domains'
rules={[
{
validator: async (_, domains) => {
if (!domains || domains.length < 1) {
2025-02-19 22:21:20 +01:00
throw new Error(t`At least one domain name`)
2024-12-30 23:50:15 +01:00
}
}
2024-12-30 23:50:15 +01:00
}
]}
>
{(fields, {add, remove}, {errors}) => (
<>
{fields.map((field, index) => (
<Form.Item
2024-12-30 23:50:15 +01:00
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
label={index === 0 ? t`Domain names` : ''}
required
key={field.key}
>
2024-12-30 23:50:15 +01:00
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
rules={[{
required: true,
message: t`Required`
}, {
pattern: /^(?=.*\.)\S*[^.\s]$/,
message: t`This domain name does not appear to be valid`,
max: 63,
min: 2
}]}
noStyle
>
<Input placeholder={t`Domain name`} style={{width: '60%'}} autoComplete='off'/>
</Form.Item>
{fields.length > 1
? (
<MinusCircleOutlined
className='dynamic-delete-button'
onClick={() => remove(field.name)}
/>
)
: null}
</Form.Item>
2024-12-30 23:50:15 +01:00
))}
<Form.Item>
<Button
type='dashed'
onClick={() => add()}
style={{width: '60%'}}
icon={<PlusOutlined/>}
>
{t`Add a Domain name`}
</Button>
<Form.ErrorList errors={errors}/>
</Form.Item>
2024-12-30 23:50:15 +01:00
</>
)}
</Form.List>
<Form.Item
label={t`Tracked events`}
2025-10-15 19:52:44 +02:00
name='trackedEvents'
2024-12-30 23:50:15 +01:00
rules={[{required: true, message: t`At least one trigger`, type: 'array'}]}
labelCol={{
xs: {span: 24},
sm: {span: 4}
}}
wrapperCol={{
md: {span: 12},
sm: {span: 20}
}}
required
>
<Select
mode='multiple'
tagRender={triggerTagRenderer}
style={{width: '100%'}}
options={Object.keys(rdapEventNameTranslated).map(e => ({
value: e,
title: rdapEventDetailTranslated[e as keyof typeof rdapEventDetailTranslated] || undefined,
label: rdapEventNameTranslated[e as keyof typeof rdapEventNameTranslated]
}))}
/>
</Form.Item>
2024-12-30 23:50:15 +01:00
<Form.Item
label={t`Connector`}
name='connector'
labelCol={{
xs: {span: 24},
sm: {span: 4}
}}
wrapperCol={{
md: {span: 12},
sm: {span: 20}
}}
help={t`Please make sure the connector information is valid to purchase a domain that may be available soon.`}
>
<Select
showSearch
2024-07-30 22:03:04 +02:00
allowClear
placeholder={t`Connector`}
suffixIcon={<ApiOutlined/>}
2024-12-30 23:50:15 +01:00
optionFilterProp='label'
2024-07-30 22:03:04 +02:00
options={connectors.map(c => ({
label: `${c.provider} (${c.id})`,
value: c.id
}))}
2024-12-30 23:50:15 +01:00
/>
</Form.Item>
<Form.List
name='dsn'
>
{(fields, {add, remove}, {errors}) => (
<>
{fields.map((field, index) => (
2024-08-16 23:57:52 +02:00
<Form.Item
2024-12-30 23:50:15 +01:00
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
label={index === 0 ? t`DSN` : ''}
required
key={field.key}
2024-08-16 23:57:52 +02:00
>
2024-12-30 23:50:15 +01:00
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
rules={[{
required: true,
message: t`Required`
}, {
pattern: /:\/\//,
message: t`This DSN does not appear to be valid`
}]}
noStyle
>
<Input
placeholder='slack://TOKEN@default?channel=CHANNEL' style={{width: '60%'}}
autoComplete='off'
/>
</Form.Item>
{fields.length > 0
? (
<MinusCircleOutlined
className='dynamic-delete-button'
onClick={() => remove(field.name)}
/>
)
: null}
2024-08-16 23:57:52 +02:00
</Form.Item>
2024-12-30 23:50:15 +01:00
))}
<Form.Item help={
<Typography.Link
href='https://symfony.com/doc/current/notifier.html#chat-channel'
target='_blank'
>
{t`Check out this link to the Symfony documentation to help you build the DSN`}
</Typography.Link>
}
2024-08-16 23:57:52 +02:00
>
2024-12-30 23:50:15 +01:00
<Button
type='dashed'
onClick={() => add()}
style={{width: '60%'}}
icon={<PlusOutlined/>}
>
{t`Add a Webhook`}
</Button>
<Form.ErrorList errors={errors}/>
</Form.Item>
</>
)}
</Form.List>
2025-02-18 21:42:15 +01:00
<Form.Item style={{marginTop: '5em'}}>
2024-12-30 23:50:15 +01:00
<Space>
<Button type='primary' htmlType='submit'>
{isCreation ? t`Create` : t`Update`}
</Button>
<Button type='default' htmlType='reset'>
{t`Reset`}
</Button>
</Space>
</Form.Item>
</Form>
)
}