mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-29 16:15:04 +00:00
feat: user can enable/disable a watchlist
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import {Button, Drawer, Form} from 'antd'
|
||||
import {t} from 'ttag'
|
||||
import {WatchlistForm} from './WatchlistForm'
|
||||
import React, {useState} from 'react'
|
||||
import type {Connector} from '../../../utils/api/connectors'
|
||||
import useBreakpoint from "../../../hooks/useBreakpoint"
|
||||
|
||||
export function CreateWatchlistButton({onUpdateWatchlist, connectors}: {
|
||||
onUpdateWatchlist: (values: {
|
||||
domains: string[],
|
||||
trackedEvents: string[],
|
||||
trackedEppStatus: string[],
|
||||
token: string
|
||||
}) => Promise<void>
|
||||
connectors: Array<Connector & { id: string }>
|
||||
}) {
|
||||
const [form] = Form.useForm()
|
||||
const [open, setOpen] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const sm = useBreakpoint('sm')
|
||||
|
||||
const showDrawer = () => setOpen(true)
|
||||
|
||||
const onClose = () => {
|
||||
setOpen(false)
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type='default' block onClick={() => {
|
||||
showDrawer()
|
||||
}}>{t`Create a Watchlist`}</Button>
|
||||
<Drawer
|
||||
title={t`Create a Watchlist`}
|
||||
width={sm ? '100%' : '80%'}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
loading={loading}
|
||||
styles={{
|
||||
body: {
|
||||
paddingBottom: 80
|
||||
}
|
||||
}}
|
||||
extra={<Button onClick={onClose}>{t`Cancel`}</Button>}
|
||||
>
|
||||
<WatchlistForm
|
||||
form={form}
|
||||
onFinish={values => {
|
||||
setLoading(true)
|
||||
onUpdateWatchlist(values).then(onClose).catch(() => setLoading(false))
|
||||
}}
|
||||
connectors={connectors}
|
||||
isCreation
|
||||
/>
|
||||
</Drawer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import {Popconfirm, theme, Typography} from 'antd'
|
||||
import {t} from 'ttag'
|
||||
import type { Watchlist} from '../../../utils/api'
|
||||
import {patchWatchlist} from '../../../utils/api'
|
||||
import {PauseCircleOutlined, PlayCircleOutlined} from '@ant-design/icons'
|
||||
import React from 'react'
|
||||
|
||||
export function DisableWatchlistButton({watchlist, onChange, enabled}: {
|
||||
watchlist: Watchlist,
|
||||
onChange: () => void,
|
||||
enabled: boolean
|
||||
}) {
|
||||
const {token} = theme.useToken()
|
||||
|
||||
return (
|
||||
enabled ?
|
||||
<Popconfirm
|
||||
title={t`Disable the Watchlist`}
|
||||
description={t`Are you sure to disable this Watchlist?`}
|
||||
onConfirm={async () => await patchWatchlist(watchlist.token, {enabled: !enabled}).then(onChange)}
|
||||
okText={t`Yes`}
|
||||
cancelText={t`No`}
|
||||
okButtonProps={{danger: true}}
|
||||
>
|
||||
<Typography.Link>
|
||||
<PauseCircleOutlined style={{color: token.colorText}} title={t`Disable the Watchlist`}/>
|
||||
</Typography.Link>
|
||||
</Popconfirm> : <Typography.Link>
|
||||
<PlayCircleOutlined style={{color: token.colorWarning}} title={t`Enable the Watchlist`}
|
||||
onClick={async () => await patchWatchlist(watchlist.token, {enabled: !enabled}).then(onChange)}/>
|
||||
</Typography.Link>
|
||||
)
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import React, {useState} from 'react'
|
||||
import {EditOutlined} from '@ant-design/icons'
|
||||
import type {Connector} from '../../../utils/api/connectors'
|
||||
import type {Watchlist} from '../../../utils/api'
|
||||
import useBreakpoint from "../../../hooks/useBreakpoint"
|
||||
|
||||
export function UpdateWatchlistButton({watchlist, onUpdateWatchlist, connectors}: {
|
||||
watchlist: Watchlist
|
||||
@@ -14,10 +15,9 @@ export function UpdateWatchlistButton({watchlist, onUpdateWatchlist, connectors}
|
||||
const [form] = Form.useForm()
|
||||
const [open, setOpen] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const sm = useBreakpoint('sm')
|
||||
|
||||
const showDrawer = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
const showDrawer = () => setOpen(true)
|
||||
|
||||
const onClose = () => {
|
||||
setOpen(false)
|
||||
@@ -44,7 +44,7 @@ export function UpdateWatchlistButton({watchlist, onUpdateWatchlist, connectors}
|
||||
</Typography.Link>
|
||||
<Drawer
|
||||
title={t`Update a Watchlist`}
|
||||
width='80%'
|
||||
width={sm ? '100%' : '80%'}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
loading={loading}
|
||||
|
||||
@@ -17,8 +17,9 @@ import {actionToColor} from '../../../utils/functions/actionToColor'
|
||||
import {DomainToTag} from '../../../utils/functions/DomainToTag'
|
||||
import type {Watchlist} from '../../../utils/api'
|
||||
import {eppStatusCodeToColor} from "../../../utils/functions/eppStatusCodeToColor"
|
||||
import {DisableWatchlistButton} from "./DisableWatchlistButton"
|
||||
|
||||
export function WatchlistCard({watchlist, onUpdateWatchlist, connectors, onDelete}: {
|
||||
export function WatchlistCard({watchlist, onUpdateWatchlist, connectors, onChange}: {
|
||||
watchlist: Watchlist
|
||||
onUpdateWatchlist: (values: {
|
||||
domains: string[],
|
||||
@@ -27,7 +28,7 @@ export function WatchlistCard({watchlist, onUpdateWatchlist, connectors, onDelet
|
||||
token: string
|
||||
}) => Promise<void>
|
||||
connectors: Array<Connector & { id: string }>
|
||||
onDelete: () => void
|
||||
onChange: () => void
|
||||
}) {
|
||||
const rdapEventNameTranslated = rdapEventNameTranslation()
|
||||
const rdapEventDetailTranslated = rdapEventDetailTranslation()
|
||||
@@ -36,7 +37,14 @@ export function WatchlistCard({watchlist, onUpdateWatchlist, connectors, onDelet
|
||||
return (
|
||||
<>
|
||||
<Card
|
||||
aria-disabled={true}
|
||||
type='inner'
|
||||
style={{
|
||||
width: '100%',
|
||||
opacity: watchlist.enabled ? 1 : 0.5,
|
||||
filter: watchlist.enabled ? 'none' : 'grayscale(0.7)',
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
title={<>
|
||||
{
|
||||
(watchlist.connector != null)
|
||||
@@ -52,7 +60,6 @@ export function WatchlistCard({watchlist, onUpdateWatchlist, connectors, onDelet
|
||||
</Tooltip>
|
||||
</>}
|
||||
size='small'
|
||||
style={{width: '100%'}}
|
||||
extra={
|
||||
<Space size='middle'>
|
||||
<ViewDiagramWatchlistButton token={watchlist.token}/>
|
||||
@@ -65,7 +72,9 @@ export function WatchlistCard({watchlist, onUpdateWatchlist, connectors, onDelet
|
||||
connectors={connectors}
|
||||
/>
|
||||
|
||||
<DeleteWatchlistButton watchlist={watchlist} onDelete={onDelete}/>
|
||||
<DisableWatchlistButton watchlist={watchlist} onChange={onChange}
|
||||
enabled={watchlist.enabled}/>
|
||||
<DeleteWatchlistButton watchlist={watchlist} onDelete={onChange}/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -3,21 +3,21 @@ import type {Connector} from '../../../utils/api/connectors'
|
||||
import {WatchlistCard} from './WatchlistCard'
|
||||
import type {Watchlist} from '../../../utils/api'
|
||||
|
||||
export function WatchlistsList({watchlists, onDelete, onUpdateWatchlist, connectors}: {
|
||||
export function WatchlistsList({watchlists, onChange, onUpdateWatchlist, connectors}: {
|
||||
watchlists: Watchlist[]
|
||||
onDelete: () => void
|
||||
onChange: () => void
|
||||
onUpdateWatchlist: (values: { domains: string[], trackedEvents: string[], trackedEppStatus: string[], token: string }) => Promise<void>
|
||||
connectors: Array<Connector & { id: string }>
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{watchlists.map(watchlist =>
|
||||
{[...watchlists.filter(w => w.enabled), ...watchlists.filter(w => !w.enabled)].map(watchlist =>
|
||||
<WatchlistCard
|
||||
key={watchlist.token}
|
||||
watchlist={watchlist}
|
||||
onUpdateWatchlist={onUpdateWatchlist}
|
||||
connectors={connectors}
|
||||
onDelete={onDelete}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {Card, Divider, Flex, Form, message} from 'antd'
|
||||
import {Divider, Flex, Form, message} from 'antd'
|
||||
import type {Watchlist} from '../../utils/api'
|
||||
import {getWatchlists, postWatchlist, putWatchlist} from '../../utils/api'
|
||||
import type {AxiosError} from 'axios'
|
||||
import {t} from 'ttag'
|
||||
import {WatchlistForm} from '../../components/tracking/watchlist/WatchlistForm'
|
||||
import {WatchlistsList} from '../../components/tracking/watchlist/WatchlistsList'
|
||||
import type {Connector} from '../../utils/api/connectors'
|
||||
import { getConnectors} from '../../utils/api/connectors'
|
||||
|
||||
import {showErrorAPI} from '../../utils/functions/showErrorAPI'
|
||||
import {CreateWatchlistButton} from "../../components/tracking/watchlist/CreateWatchlistButton"
|
||||
|
||||
interface FormValuesType {
|
||||
name?: string
|
||||
@@ -20,17 +20,15 @@ interface FormValuesType {
|
||||
dsn?: string[]
|
||||
}
|
||||
|
||||
const getRequestDataFromFormCreation = (values: FormValuesType) => {
|
||||
const domainsURI = values.domains.map(d => '/api/domains/' + d.toLowerCase())
|
||||
return {
|
||||
name: values.name,
|
||||
domains: domainsURI,
|
||||
const getRequestDataFromFormCreation = (values: FormValuesType) =>
|
||||
({ name: values.name,
|
||||
domains: values.domains.map(d => '/api/domains/' + d.toLowerCase()),
|
||||
trackedEvents: values.trackedEvents,
|
||||
trackedEppStatus: values.trackedEppStatus,
|
||||
connector: values.connector !== undefined ? ('/api/connectors/' + values.connector) : undefined,
|
||||
dsn: values.dsn
|
||||
}
|
||||
}
|
||||
dsn: values.dsn,
|
||||
enabled: true
|
||||
})
|
||||
|
||||
export default function WatchlistPage() {
|
||||
const [form] = Form.useForm()
|
||||
@@ -38,15 +36,13 @@ export default function WatchlistPage() {
|
||||
const [watchlists, setWatchlists] = useState<Watchlist[]>()
|
||||
const [connectors, setConnectors] = useState<Array<Connector & { id: string }>>()
|
||||
|
||||
const onCreateWatchlist = (values: FormValuesType) => {
|
||||
postWatchlist(getRequestDataFromFormCreation(values)).then(() => {
|
||||
const onCreateWatchlist = async (values: FormValuesType) => await postWatchlist(getRequestDataFromFormCreation(values)).then(() => {
|
||||
form.resetFields()
|
||||
refreshWatchlists()
|
||||
messageApi.success(t`Watchlist created !`)
|
||||
}).catch((e: AxiosError) => {
|
||||
showErrorAPI(e, messageApi)
|
||||
})
|
||||
}
|
||||
|
||||
const onUpdateWatchlist = async (values: FormValuesType & { token: string }) => await putWatchlist({
|
||||
token: values.token,
|
||||
@@ -78,18 +74,18 @@ export default function WatchlistPage() {
|
||||
return (
|
||||
<Flex gap='middle' align='center' justify='center' vertical>
|
||||
{contextHolder}
|
||||
<Card size='small' loading={connectors === undefined} title={t`Create a Watchlist`} style={{width: '100%'}}>
|
||||
{(connectors != null) &&
|
||||
<WatchlistForm form={form} onFinish={onCreateWatchlist} connectors={connectors} isCreation/>}
|
||||
</Card>
|
||||
<Divider/>
|
||||
|
||||
{(connectors != null) && (watchlists != null) && watchlists.length > 0 &&
|
||||
<WatchlistsList
|
||||
watchlists={watchlists}
|
||||
onDelete={refreshWatchlists}
|
||||
connectors={connectors}
|
||||
onUpdateWatchlist={onUpdateWatchlist}
|
||||
/>}
|
||||
<>
|
||||
<CreateWatchlistButton onUpdateWatchlist={onCreateWatchlist} connectors={connectors} />
|
||||
<Divider/>
|
||||
<WatchlistsList
|
||||
watchlists={watchlists}
|
||||
onChange={refreshWatchlists}
|
||||
connectors={connectors}
|
||||
onUpdateWatchlist={onUpdateWatchlist}
|
||||
/>
|
||||
</>}
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ export interface WatchlistRequest {
|
||||
trackedEppStatus?: string[]
|
||||
connector?: string
|
||||
dsn?: string[]
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
export interface Watchlist {
|
||||
@@ -100,6 +101,7 @@ export interface Watchlist {
|
||||
createdAt: string
|
||||
}
|
||||
createdAt: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export interface InstanceConfig {
|
||||
|
||||
@@ -32,6 +32,18 @@ export async function postWatchlist(watchlist: WatchlistRequest) {
|
||||
return response.data
|
||||
}
|
||||
|
||||
export async function patchWatchlist(token: string, watchlist: Partial<WatchlistRequest>) {
|
||||
const response = await request<{ token: string }>({
|
||||
method: 'PATCH',
|
||||
url: 'watchlists/' + token,
|
||||
data: watchlist,
|
||||
headers: {
|
||||
'Content-Type': 'application/merge-patch+json'
|
||||
}
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
export async function deleteWatchlist(token: string): Promise<void> {
|
||||
await request({
|
||||
method: 'DELETE',
|
||||
|
||||
Reference in New Issue
Block a user