feat: add eslint linter

This commit is contained in:
Maël Gangloff
2024-12-30 23:50:15 +01:00
parent ebfcc58d16
commit 99d135cc31
64 changed files with 3579 additions and 1846 deletions

View File

@@ -1,16 +1,24 @@
import React, {createContext, useEffect, useState} from "react";
import {Button, Card} from "antd";
import React, {createContext, useEffect, useState} from 'react'
import {Button, Card} from 'antd'
import {t} from 'ttag'
import TextPage from "./TextPage";
import {LoginForm} from "../components/LoginForm";
import {getConfiguration, InstanceConfig} from "../utils/api";
import {RegisterForm} from "../components/RegisterForm";
import TextPage from './TextPage'
import {LoginForm} from '../components/LoginForm'
import {getConfiguration, InstanceConfig} from '../utils/api'
import {RegisterForm} from '../components/RegisterForm'
export const AuthenticatedContext = createContext<any>(null)
export const AuthenticatedContext = createContext<
{
authenticated: (authenticated: boolean) => void
setIsAuthenticated: React.Dispatch<React.SetStateAction<boolean>>
}
>({
authenticated: () => {
},
setIsAuthenticated: () => {
}
})
export default function LoginPage() {
const [wantRegister, setWantRegister] = useState<boolean>(false)
const [configuration, setConfiguration] = useState<InstanceConfig>()
@@ -22,19 +30,24 @@ export default function LoginPage() {
getConfiguration().then(setConfiguration)
}, [])
return <Card title={wantRegister ? t`Register` : t`Log in`} style={{width: '100%'}}>
<Card.Grid style={{width: '50%', textAlign: 'center'}} hoverable={false}>
{wantRegister ? <RegisterForm/> : <LoginForm ssoLogin={configuration?.ssoLogin}/>}
{
configuration?.registerEnabled &&
<Button type='link'
return (
<Card title={wantRegister ? t`Register` : t`Log in`} style={{width: '100%'}}>
<Card.Grid style={{width: '50%', textAlign: 'center'}} hoverable={false}>
{wantRegister ? <RegisterForm/> : <LoginForm ssoLogin={configuration?.ssoLogin}/>}
{
configuration?.registerEnabled &&
<Button
type='link'
block
style={{marginTop: '1em'}}
onClick={toggleWantRegister}>{wantRegister ? t`Log in` : t`Create an account`}</Button>
}
</Card.Grid>
<Card.Grid style={{width: '50%'}} hoverable={false}>
<TextPage resource='ads.md'/>
</Card.Grid>
</Card>
}
onClick={toggleWantRegister}
>{wantRegister ? t`Log in` : t`Create an account`}
</Button>
}
</Card.Grid>
<Card.Grid style={{width: '50%'}} hoverable={false}>
<TextPage resource='ads.md'/>
</Card.Grid>
</Card>
)
}

View File

@@ -1,12 +1,13 @@
import {Result} from "antd";
import React from "react";
import {Result} from 'antd'
import React from 'react'
import {t} from 'ttag'
export default function NotFoundPage() {
return <Result
status="404"
title="404"
subTitle={t`Sorry, the page you visited does not exist.`}
/>
}
return (
<Result
status='404'
title='404'
subTitle={t`Sorry, the page you visited does not exist.`}
/>
)
}

View File

@@ -1,17 +1,16 @@
import React, {useEffect, useState} from "react";
import {getStatistics, Statistics} from "../utils/api";
import {Card, Col, Divider, Row, Statistic, Tooltip} from "antd";
import {t} from "ttag";
import React, {useEffect, useState} from 'react'
import {getStatistics, Statistics} from '../utils/api'
import {Card, Col, Divider, Row, Statistic, Tooltip} from 'antd'
import {t} from 'ttag'
import {
AimOutlined,
CompassOutlined,
DatabaseOutlined,
FieldTimeOutlined,
NotificationOutlined
} from "@ant-design/icons";
AimOutlined,
CompassOutlined,
DatabaseOutlined,
FieldTimeOutlined,
NotificationOutlined
} from '@ant-design/icons'
export default function StatisticsPage() {
const [stats, setStats] = useState<Statistics>()
useEffect(() => {
@@ -20,101 +19,104 @@ export default function StatisticsPage() {
const totalDomainPurchase = (stats?.domainPurchased ?? 0) + (stats?.domainPurchaseFailed ?? 0)
const successRate = stats !== undefined ?
(totalDomainPurchase === 0 ? undefined : stats.domainPurchased / totalDomainPurchase)
const successRate = stats !== undefined
? (totalDomainPurchase === 0 ? undefined : stats.domainPurchased / totalDomainPurchase)
: undefined
return <>
<Row gutter={16}>
<Col span={12}>
<Card bordered={false}>
<Statistic
loading={stats === undefined}
prefix={<CompassOutlined/>}
title={t`RDAP queries`}
value={stats?.rdapQueries}
/>
</Card>
</Col>
<Col span={12}>
<Card bordered={false}>
<Statistic
loading={stats === undefined}
title={t`Alerts sent`}
prefix={<NotificationOutlined/>}
value={stats?.alertSent}
valueStyle={{color: 'violet'}}
/>
</Card>
</Col>
</Row>
<Divider/>
<Row gutter={16}>
<Col span={12}>
<Card bordered={false}>
<Statistic
loading={stats === undefined}
title={t`Domain names in database`}
prefix={<DatabaseOutlined/>}
value={stats?.domainCountTotal}
valueStyle={{color: 'orange'}}
/>
</Card>
</Col>
<Col span={12}>
<Card bordered={false}>
<Statistic
loading={stats === undefined}
title={t`Tracked domain names`}
prefix={<AimOutlined/>}
value={stats?.domainTracked}
valueStyle={{color: 'violet'}}
/>
</Card>
</Col>
</Row>
<Divider/>
<Row gutter={16}>
<Col span={12}>
<Card bordered={false}>
<Statistic
loading={stats === undefined}
title={t`Purchased domain names`}
prefix={<FieldTimeOutlined/>}
value={stats?.domainPurchased}
valueStyle={{color: 'green'}}
/>
</Card>
</Col>
<Col span={12}>
<Card bordered={false}>
<Tooltip
title={t`This value is based on the status code of the HTTP response from the providers following the domain order.`}>
<Statistic
loading={stats === undefined}
title={t`Success rate`}
value={successRate === undefined ? '-' : successRate * 100}
suffix='%'
valueStyle={{color: successRate === undefined ? 'grey' : successRate >= 0.5 ? 'darkgreen' : 'orange'}}
/>
</Tooltip>
</Card>
</Col>
</Row>
<Divider/>
<Row gutter={16} justify='center' align='middle'>
{stats?.domainCount
.sort((a, b) => b.domain - a.domain)
.map(({domain, tld}) => <Col span={4}>
return (
<>
<Row gutter={16}>
<Col span={12}>
<Card bordered={false}>
<Statistic
loading={stats === undefined}
title={`.${tld}`}
value={domain}
valueStyle={{color: 'darkorange'}}
prefix={<CompassOutlined/>}
title={t`RDAP queries`}
value={stats?.rdapQueries}
/>
</Card>
</Col>)}
</Row>
</>
}
</Col>
<Col span={12}>
<Card bordered={false}>
<Statistic
loading={stats === undefined}
title={t`Alerts sent`}
prefix={<NotificationOutlined/>}
value={stats?.alertSent}
valueStyle={{color: 'violet'}}
/>
</Card>
</Col>
</Row>
<Divider/>
<Row gutter={16}>
<Col span={12}>
<Card bordered={false}>
<Statistic
loading={stats === undefined}
title={t`Domain names in database`}
prefix={<DatabaseOutlined/>}
value={stats?.domainCountTotal}
valueStyle={{color: 'orange'}}
/>
</Card>
</Col>
<Col span={12}>
<Card bordered={false}>
<Statistic
loading={stats === undefined}
title={t`Tracked domain names`}
prefix={<AimOutlined/>}
value={stats?.domainTracked}
valueStyle={{color: 'violet'}}
/>
</Card>
</Col>
</Row>
<Divider/>
<Row gutter={16}>
<Col span={12}>
<Card bordered={false}>
<Statistic
loading={stats === undefined}
title={t`Purchased domain names`}
prefix={<FieldTimeOutlined/>}
value={stats?.domainPurchased}
valueStyle={{color: 'green'}}
/>
</Card>
</Col>
<Col span={12}>
<Card bordered={false}>
<Tooltip
title={t`This value is based on the status code of the HTTP response from the providers following the domain order.`}
>
<Statistic
loading={stats === undefined}
title={t`Success rate`}
value={successRate === undefined ? '-' : successRate * 100}
suffix='%'
valueStyle={{color: successRate === undefined ? 'grey' : successRate >= 0.5 ? 'darkgreen' : 'orange'}}
/>
</Tooltip>
</Card>
</Col>
</Row>
<Divider/>
<Row gutter={16} justify='center' align='middle'>
{stats?.domainCount
.sort((a, b) => b.domain - a.domain)
.map(({domain, tld}) => <Col key={tld} span={4}>
<Card bordered={false}>
<Statistic
loading={stats === undefined}
title={`.${tld}`}
value={domain}
valueStyle={{color: 'darkorange'}}
/>
</Card>
</Col>)}
</Row>
</>
)
}

View File

@@ -1,8 +1,8 @@
import React, {useEffect, useState} from "react";
import snarkdown from "snarkdown"
import {Skeleton, Typography} from "antd";
import axios from "axios";
import {t} from "ttag";
import React, {useEffect, useState} from 'react'
import snarkdown from 'snarkdown'
import {Skeleton, Typography} from 'antd'
import axios from 'axios'
import {t} from 'ttag'
export default function TextPage({resource}: { resource: string }) {
const [loading, setLoading] = useState<boolean>(false)
@@ -12,18 +12,22 @@ export default function TextPage({resource}: { resource: string }) {
setLoading(true)
axios.get('/content/' + resource)
.then(res => setMarkdown(res.data))
.catch(err => {
.catch(() => {
console.error(`Please create the /public/content/${resource} file.`)
setMarkdown(undefined)
})
.finally(() => setLoading(false))
}, [resource])
return <Skeleton loading={loading} active>
{markdown !== undefined ? <div
dangerouslySetInnerHTML={{__html: snarkdown(markdown)}}></div> :
<Typography.Text strong>
{t`📝 Please create the /public/content/${resource} file.`}
</Typography.Text>}
</Skeleton>
}
return (
<Skeleton loading={loading} active>
{markdown !== undefined
? <div
dangerouslySetInnerHTML={{__html: snarkdown(markdown)}}
/>
: <Typography.Text strong>
{t`📝 Please create the /public/content/${resource} file.`}
</Typography.Text>}
</Skeleton>
)
}

View File

@@ -1,26 +1,27 @@
import React, {useEffect, useState} from "react";
import {Card, Flex, Skeleton, Typography} from "antd";
import {getUser, User} from "../utils/api";
import React, {useEffect, useState} from 'react'
import {Card, Flex, Skeleton, Typography} from 'antd'
import {getUser, User} from '../utils/api'
import {t} from 'ttag'
export default function UserPage() {
const [user, setUser] = useState<User | null>(null)
useEffect(() => {
getUser().then(setUser)
}, [])
return <Skeleton loading={user === null} active>
<Flex gap="middle" align="center" justify="center" vertical>
<Card title={t`My Account`}>
<Typography.Paragraph>
{t`Username`} : {user?.email}
</Typography.Paragraph>
<Typography.Paragraph>
{t`Roles`} : {user?.roles.join(',')}
</Typography.Paragraph>
</Card>
</Flex>
</Skeleton>
}
return (
<Skeleton loading={user === null} active>
<Flex gap='middle' align='center' justify='center' vertical>
<Card title={t`My Account`}>
<Typography.Paragraph>
{t`Username`} : {user?.email}
</Typography.Paragraph>
<Typography.Paragraph>
{t`Roles`} : {user?.roles.join(',')}
</Typography.Paragraph>
</Card>
</Flex>
</Skeleton>
)
}

View File

@@ -1,12 +1,12 @@
import React, {useEffect, useState} from "react";
import {Empty, Flex, FormProps, message, Skeleton} from "antd";
import {Domain, getDomain} from "../../utils/api";
import {AxiosError} from "axios"
import React, {useEffect, useState} from 'react'
import {Empty, Flex, FormProps, message, Skeleton} from 'antd'
import {Domain, getDomain} from '../../utils/api'
import {AxiosError} from 'axios'
import {t} from 'ttag'
import {DomainSearchBar, FieldType} from "../../components/search/DomainSearchBar";
import {DomainResult} from "../../components/search/DomainResult";
import {showErrorAPI} from "../../utils/functions/showErrorAPI";
import {useNavigate, useParams} from "react-router-dom";
import {DomainSearchBar, FieldType} from '../../components/search/DomainSearchBar'
import {DomainResult} from '../../components/search/DomainResult'
import {showErrorAPI} from '../../utils/functions/showErrorAPI'
import {useNavigate, useParams} from 'react-router-dom'
export default function DomainSearchPage() {
const {query} = useParams()
@@ -36,17 +36,21 @@ export default function DomainSearchPage() {
onFinish({ldhName: query})
}, [])
return <Flex gap="middle" align="center" justify="center" vertical>
{contextHolder}
<DomainSearchBar initialValue={query} onFinish={onFinish}/>
return (
<Flex gap='middle' align='center' justify='center' vertical>
{contextHolder}
<DomainSearchBar initialValue={query} onFinish={onFinish}/>
<Skeleton loading={domain === null} active>
{
domain &&
(!domain.deleted ? <DomainResult domain={domain}/>
: <Empty
description={t`Although the domain exists in my database, it has been deleted from the WHOIS by its registrar.`}/>)
}
</Skeleton>
</Flex>
}
<Skeleton loading={domain === null} active>
{
(domain != null) &&
(!domain.deleted
? <DomainResult domain={domain}/>
: <Empty
description={t`Although the domain exists in my database, it has been deleted from the WHOIS by its registrar.`}
/>)
}
</Skeleton>
</Flex>
)
}

View File

@@ -1,7 +1,9 @@
import React from "react";
import React from 'react'
export default function EntitySearchPage() {
return <p>
Not implemented
</p>
}
return (
<p>
Not implemented
</p>
)
}

View File

@@ -1,7 +1,9 @@
import React from "react";
import React from 'react'
export default function NameserverSearchPage() {
return <p>
Not implemented
</p>
}
return (
<p>
Not implemented
</p>
)
}

View File

@@ -1,41 +1,53 @@
import React, {useEffect, useState} from "react";
import {Collapse, Divider, Table, Typography} from "antd";
import {getTldList, Tld} from "../../utils/api";
import React, {ReactElement, useEffect, useState} from 'react'
import {Collapse, Divider, Table, Typography} from 'antd'
import {getTldList, Tld} from '../../utils/api'
import {t} from 'ttag'
import {regionNames} from "../../i18n";
import useBreakpoint from "../../hooks/useBreakpoint";
import {ColumnType} from "antd/es/table";
import punycode from "punycode/punycode";
import {getCountryCode} from "../../utils/functions/getCountryCode";
import {tldToEmoji} from "../../utils/functions/tldToEmoji";
import {regionNames} from '../../i18n'
import useBreakpoint from '../../hooks/useBreakpoint'
import {ColumnType} from 'antd/es/table'
import punycode from 'punycode/punycode'
import {getCountryCode} from '../../utils/functions/getCountryCode'
import {tldToEmoji} from '../../utils/functions/tldToEmoji'
const {Text, Paragraph} = Typography
type TldType = 'iTLD' | 'sTLD' | 'gTLD' | 'ccTLD'
type FiltersType = { type: TldType, contractTerminated?: boolean, specification13?: boolean }
interface FiltersType {
type: TldType,
contractTerminated?: boolean,
specification13?: boolean
}
function TldTable(filters: FiltersType) {
interface TableRow {
key: string
TLD: ReactElement
Flag?: string
Country?: string
}
const sm = useBreakpoint('sm')
const [dataTable, setDataTable] = useState<Tld[]>([])
const [dataTable, setDataTable] = useState<TableRow[]>([])
const [total, setTotal] = useState(0)
const fetchData = (params: FiltersType & { page: number, itemsPerPage: number }) => {
getTldList(params).then((data) => {
setTotal(data['hydra:totalItems'])
setDataTable(data['hydra:member'].map((tld: Tld) => {
const rowData = {
key: tld.tld,
TLD: <Typography.Text code>{punycode.toUnicode(tld.tld)}</Typography.Text>
}
switch (filters.type) {
const type = filters.type
let countryName
switch (type) {
case 'ccTLD':
let countryName
try {
countryName = regionNames.of(getCountryCode(tld.tld))
} catch (e) {
} catch {
countryName = '-'
}
@@ -60,98 +72,104 @@ function TldTable(filters: FiltersType) {
fetchData({...filters, page: 1, itemsPerPage: 30})
}, [])
let columns: ColumnType<any>[] = [
let columns: Array<ColumnType<TableRow>> = [
{
title: t`TLD`,
dataIndex: "TLD"
dataIndex: 'TLD'
}
]
if (filters.type === 'ccTLD') columns = [...columns, {
title: t`Flag`,
dataIndex: "Flag",
}, {
title: t`Country`,
dataIndex: "Country"
}]
if (filters.type === 'ccTLD') {
columns = [...columns, {
title: t`Flag`,
dataIndex: 'Flag'
}, {
title: t`Country`,
dataIndex: 'Country'
}]
}
if (filters.type === 'gTLD') columns = [...columns, {
title: t`Registry Operator`,
dataIndex: "Operator"
}]
if (filters.type === 'gTLD') {
columns = [...columns, {
title: t`Registry Operator`,
dataIndex: 'Operator'
}]
}
return (
<Table
columns={columns}
dataSource={dataTable}
pagination={{
total,
hideOnSinglePage: true,
defaultPageSize: 30,
onChange: (page, itemsPerPage) => {
fetchData({...filters, page, itemsPerPage})
}
}}
return <Table
columns={columns}
dataSource={dataTable}
pagination={{
total,
hideOnSinglePage: true,
defaultPageSize: 30,
onChange: (page, itemsPerPage) => {
fetchData({...filters, page, itemsPerPage})
}
}}
{...(sm ? {scroll: {y: 'max-content'}} : {scroll: {y: 240}})}
/>
{...(sm ? {scroll: {y: 'max-content'}} : {scroll: {y: 240}})}
/>
)
}
export default function TldPage() {
const sm = useBreakpoint('sm')
return <>
<Paragraph>
{t`This page presents all active TLDs in the root zone database.`}
</Paragraph>
<Paragraph>
{t`IANA provides the list of currently active TLDs, regardless of their type, and ICANN provides the list of gTLDs.
return (
<>
<Paragraph>
{t`This page presents all active TLDs in the root zone database.`}
</Paragraph>
<Paragraph>
{t`IANA provides the list of currently active TLDs, regardless of their type, and ICANN provides the list of gTLDs.
In most cases, the two-letter ccTLD assigned to a country is made in accordance with the ISO 3166-1 standard.
This data is updated every month. Three HTTP requests are needed for the complete update of TLDs in Domain Watchdog (two requests to IANA and one to ICANN).
At the same time, the list of root RDAP servers is updated.`}
</Paragraph>
<Divider/>
<Collapse
accordion
size={sm ? 'small' : 'large'}
items={[
{
key: 'sTLD',
label: t`Sponsored Top-Level-Domains`,
children: <>
<Text>{t`Top-level domains sponsored by specific organizations that set rules for registration and use, often related to particular interest groups or industries.`}</Text>
<Divider/>
<TldTable type='sTLD'/>
</>
},
{
key: 'gTLD',
label: t`Generic Top-Level-Domains`,
children: <>
<Text>{t`Generic top-level domains open to everyone, not restricted by specific criteria, representing various themes or industries.`}</Text>
<Divider/>
<TldTable type='gTLD' contractTerminated={false} specification13={false}/>
</>
},
{
key: 'ngTLD',
label: t`Brand Generic Top-Level-Domains`,
children: <>
<Text>{t`Generic top-level domains associated with specific brands, allowing companies to use their own brand names as domains.`}</Text>
<Divider/>
<TldTable type='gTLD' contractTerminated={false} specification13={true}/>
</>
},
{
key: 'ccTLD',
label: t`Country-Code Top-Level-Domains`,
children: <>
<Text>{t`Top-level domains based on country codes, identifying websites according to their country of origin.`}</Text>
<Divider/><TldTable type='ccTLD'/>
</>
}
]}
/>
</>
}
</Paragraph>
<Divider/>
<Collapse
accordion
size={sm ? 'small' : 'large'}
items={[
{
key: 'sTLD',
label: t`Sponsored Top-Level-Domains`,
children: <>
<Text>{t`Top-level domains sponsored by specific organizations that set rules for registration and use, often related to particular interest groups or industries.`}</Text>
<Divider/>
<TldTable type='sTLD'/>
</>
},
{
key: 'gTLD',
label: t`Generic Top-Level-Domains`,
children: <>
<Text>{t`Generic top-level domains open to everyone, not restricted by specific criteria, representing various themes or industries.`}</Text>
<Divider/>
<TldTable type='gTLD' contractTerminated={false} specification13={false}/>
</>
},
{
key: 'ngTLD',
label: t`Brand Generic Top-Level-Domains`,
children: <>
<Text>{t`Generic top-level domains associated with specific brands, allowing companies to use their own brand names as domains.`}</Text>
<Divider/>
<TldTable type='gTLD' contractTerminated={false} specification13/>
</>
},
{
key: 'ccTLD',
label: t`Country-Code Top-Level-Domains`,
children: <>
<Text>{t`Top-level domains based on country codes, identifying websites according to their country of origin.`}</Text>
<Divider/><TldTable type='ccTLD'/>
</>
}
]}
/>
</>
)
}

View File

@@ -1,12 +1,12 @@
import React, {useEffect, useState} from "react";
import {Card, Flex, Form, message, Skeleton} from "antd";
import {t} from "ttag";
import {Connector, getConnectors, postConnector} from "../../utils/api/connectors";
import {ConnectorForm} from "../../components/tracking/connector/ConnectorForm";
import {AxiosError} from "axios";
import {ConnectorElement, ConnectorsList} from "../../components/tracking/connector/ConnectorsList";
import React, {useEffect, useState} from 'react'
import {Card, Flex, Form, message, Skeleton} from 'antd'
import {t} from 'ttag'
import {Connector, getConnectors, postConnector} from '../../utils/api/connectors'
import {ConnectorForm} from '../../components/tracking/connector/ConnectorForm'
import {AxiosError} from 'axios'
import {ConnectorElement, ConnectorsList} from '../../components/tracking/connector/ConnectorsList'
import {showErrorAPI} from "../../utils/functions/showErrorAPI";
import {showErrorAPI} from '../../utils/functions/showErrorAPI'
export default function ConnectorPage() {
const [form] = Form.useForm()
@@ -14,7 +14,7 @@ export default function ConnectorPage() {
const [connectors, setConnectors] = useState<ConnectorElement[] | null>()
const onCreateConnector = (values: Connector) => {
postConnector(values).then((w) => {
postConnector(values).then(() => {
form.resetFields()
refreshConnectors()
messageApi.success(t`Connector created !`)
@@ -23,7 +23,7 @@ export default function ConnectorPage() {
})
}
const refreshConnectors = () => getConnectors().then(c => {
const refreshConnectors = async () => await getConnectors().then(c => {
setConnectors(c['hydra:member'])
}).catch((e: AxiosError) => {
setConnectors(undefined)
@@ -34,18 +34,17 @@ export default function ConnectorPage() {
refreshConnectors()
}, [])
return (
<Flex gap='middle' align='center' justify='center' vertical>
<Card title={t`Create a Connector`} style={{width: '100%'}}>
{contextHolder}
<ConnectorForm form={form} onCreate={onCreateConnector}/>
</Card>
return <Flex gap="middle" align="center" justify="center" vertical>
<Card title={t`Create a Connector`} style={{width: '100%'}}>
{contextHolder}
<ConnectorForm form={form} onCreate={onCreateConnector}/>
</Card>
<Skeleton loading={connectors === undefined} active>
{connectors && connectors.length > 0 &&
<ConnectorsList connectors={connectors} onDelete={refreshConnectors}/>
}
</Skeleton>
</Flex>
}
<Skeleton loading={connectors === undefined} active>
{(connectors != null) && connectors.length > 0 &&
<ConnectorsList connectors={connectors} onDelete={refreshConnectors}/>}
</Skeleton>
</Flex>
)
}

View File

@@ -1,6 +1,6 @@
import {TrackedDomainTable} from "../../components/tracking/watchlist/TrackedDomainTable";
import React from "react";
import {TrackedDomainTable} from '../../components/tracking/watchlist/TrackedDomainTable'
import React from 'react'
export default function TrackedDomainPage() {
return <TrackedDomainTable/>
}
}

View File

@@ -1,34 +1,19 @@
import React, {useEffect, useState} from "react";
import {Card, Divider, Flex, Form, message} from "antd";
import {EventAction, getWatchlists, postWatchlist, putWatchlist} from "../../utils/api";
import {AxiosError} from "axios";
import React, {useEffect, useState} from 'react'
import {Card, Divider, Flex, Form, message} from 'antd'
import {getWatchlists, postWatchlist, putWatchlist, Watchlist} from '../../utils/api'
import {AxiosError} from 'axios'
import {t} from 'ttag'
import {WatchlistForm} from "../../components/tracking/watchlist/WatchlistForm";
import {WatchlistsList} from "../../components/tracking/watchlist/WatchlistsList";
import {Connector, getConnectors} from "../../utils/api/connectors";
import {WatchlistForm} from '../../components/tracking/watchlist/WatchlistForm'
import {WatchlistsList} from '../../components/tracking/watchlist/WatchlistsList'
import {Connector, getConnectors} from '../../utils/api/connectors'
import {showErrorAPI} from "../../utils/functions/showErrorAPI";
import {showErrorAPI} from '../../utils/functions/showErrorAPI'
export type Watchlist = {
interface FormValuesType {
name?: string
token: string,
domains: { ldhName: string, deleted: boolean, status: string[] }[],
triggers?: { event: EventAction, action: string }[],
dsn?: string[]
connector?: {
id: string
provider: string
createdAt: string
}
createdAt: string
}
type FormValuesType = {
name?: string
domains: string[],
domains: string[]
triggers: string[]
connector?: string,
connector?: string
dsn?: string[]
}
@@ -52,14 +37,13 @@ const getRequestDataFromForm = (values: FormValuesType) => {
}
export default function WatchlistPage() {
const [form] = Form.useForm()
const [messageApi, contextHolder] = message.useMessage()
const [watchlists, setWatchlists] = useState<Watchlist[]>()
const [connectors, setConnectors] = useState<(Connector & { id: string })[]>()
const [connectors, setConnectors] = useState<Array<Connector & { id: string }>>()
const onCreateWatchlist = (values: FormValuesType) => {
postWatchlist(getRequestDataFromForm(values)).then((w) => {
postWatchlist(getRequestDataFromForm(values)).then(() => {
form.resetFields()
refreshWatchlists()
messageApi.success(t`Watchlist created !`)
@@ -68,18 +52,18 @@ export default function WatchlistPage() {
})
}
const onUpdateWatchlist = async (values: FormValuesType & { token: string }) => putWatchlist({
const onUpdateWatchlist = async (values: FormValuesType & { token: string }) => await putWatchlist({
token: values.token,
...getRequestDataFromForm(values)
}
).then((w) => {
).then(() => {
refreshWatchlists()
messageApi.success(t`Watchlist updated !`)
}).catch((e: AxiosError) => {
throw showErrorAPI(e, messageApi)
})
const refreshWatchlists = () => getWatchlists().then(w => {
const refreshWatchlists = async () => await getWatchlists().then(w => {
setWatchlists(w['hydra:member'])
}).catch((e: AxiosError) => {
setWatchlists(undefined)
@@ -95,18 +79,20 @@ export default function WatchlistPage() {
})
}, [])
return <Flex gap="middle" align="center" justify="center" vertical>
{contextHolder}
<Card loading={connectors === undefined} title={t`Create a Watchlist`} style={{width: '100%'}}>
{connectors &&
<WatchlistForm form={form} onFinish={onCreateWatchlist} connectors={connectors} isCreation={true}/>
}
</Card>
<Divider/>
{connectors && watchlists && watchlists.length > 0 &&
<WatchlistsList watchlists={watchlists} onDelete={refreshWatchlists}
connectors={connectors}
onUpdateWatchlist={onUpdateWatchlist}
/>}
</Flex>
}
return (
<Flex gap='middle' align='center' justify='center' vertical>
{contextHolder}
<Card 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}
/>}
</Flex>
)
}