refactor: split components Domain Finder

This commit is contained in:
Maël Gangloff
2024-07-29 18:41:36 +02:00
parent a8d624c7c9
commit 4b1414a251
5 changed files with 240 additions and 237 deletions

View File

@@ -0,0 +1,35 @@
import {Form, Input} from "antd";
import {t} from "ttag";
import {SearchOutlined} from "@ant-design/icons";
import React from "react";
export type FieldType = {
ldhName: string
}
export function DomainSearchBar({onFinish}: { onFinish: any }) {
return <Form
name="basic"
labelCol={{span: 8}}
wrapperCol={{span: 16}}
onFinish={onFinish}
autoComplete="off"
>
<Form.Item<FieldType>
name="ldhName"
rules={[{
required: true,
message: t`Required`
}, {
pattern: /^(?=.*\.)\S*[^.\s]$/,
message: t`This domain name does not appear to be valid`,
max: 63,
min: 2
}]}
>
<Input size="large" prefix={<SearchOutlined/>} placeholder="example.com" autoFocus={true}
autoComplete='off'/>
</Form.Item>
</Form>
}

View File

@@ -0,0 +1,53 @@
import vCard from "vcf";
import {Avatar, List} from "antd";
import {BankOutlined, IdcardOutlined, SignatureOutlined, ToolOutlined, UserOutlined} from "@ant-design/icons";
import React from "react";
import {Domain} from "../../utils/api";
import {t} from "ttag";
export function EntitiesList({domain}: { domain: Domain }) {
const domainRole = {
registrant: t`Registrant`,
technical: t`Technical`,
administrative: t`Administrative`,
abuse: t`Abuse`,
billing: t`Billing`,
registrar: t`Registrar`,
reseller: t`Reseller`,
sponsor: t`Sponsor`,
proxy: t`Proxy`,
notifications: t`Notifications`,
noc: t`Noc`
}
return <List
className="demo-loadmore-list"
itemLayout="horizontal"
dataSource={domain.entities.sort((e1, e2) => {
const p = (r: string[]) => r.includes('registrant') ? 4 : r.includes('administrative') ? 3 : r.includes('billing') ? 2 : 1
return p(e2.roles) - p(e1.roles)
})}
renderItem={(e) => {
const jCard = vCard.fromJSON(e.entity.jCard)
let name = ''
if (jCard.data.fn !== undefined && !Array.isArray(jCard.data.fn)) name = jCard.data.fn.valueOf()
return <List.Item>
<List.Item.Meta
avatar={<Avatar style={{backgroundColor: '#87d068'}}
icon={e.roles.includes('registrant') ?
<SignatureOutlined/> : e.roles.includes('registrar') ?
<BankOutlined/> :
e.roles.includes('technical') ?
<ToolOutlined/> :
e.roles.includes('administrative') ?
<IdcardOutlined/> :
<UserOutlined/>}/>}
title={e.entity.handle}
description={name}
/>
<div>{e.roles.map((r) => Object.keys(domainRole).includes(r) ? domainRole[r as keyof typeof domainRole] : r).join(', ')}</div>
</List.Item>
}}
/>
}

View File

@@ -0,0 +1,70 @@
import {
ClockCircleOutlined,
DeleteOutlined,
ReloadOutlined,
ShareAltOutlined,
SignatureOutlined,
SyncOutlined
} from "@ant-design/icons";
import {Timeline} from "antd";
import React from "react";
import {Domain} from "../../utils/api";
import {t} from "ttag";
export function EventTimeline({domain}: { domain: Domain }) {
const domainEvent = {
registration: t`Registration`,
reregistration: t`Reregistration`,
'last changed': t`Last changed`,
expiration: t`Expiration`,
deletion: t`Deletion`,
reinstantiation: t`Reinstantiation`,
transfer: t`Transfer`,
locked: t`Locked`,
unlocked: t`Unlocked`,
'registrar expiration': t`Registrar expiration`,
'enum validation expiration': t`ENUM validation expiration`
}
const locale = navigator.language.split('-')[0]
return <Timeline
mode="right"
items={domain.events
.sort((e1, e2) => new Date(e2.date).getTime() - new Date(e1.date).getTime())
.map(({action, date}) => {
let color, dot
if (action === 'registration') {
color = 'green'
dot = <SignatureOutlined style={{fontSize: '16px'}}/>
} else if (action === 'expiration') {
color = 'red'
dot = <ClockCircleOutlined style={{fontSize: '16px'}}/>
} else if (action === 'transfer') {
color = 'orange'
dot = <ShareAltOutlined style={{fontSize: '16px'}}/>
} else if (action === 'last changed') {
color = 'blue'
dot = <SyncOutlined style={{fontSize: '16px'}}/>
} else if (action === 'deletion') {
color = 'red'
dot = <DeleteOutlined style={{fontSize: '16px'}}/>
} else if (action === 'reregistration') {
color = 'green'
dot = <ReloadOutlined style={{fontSize: '16px'}}/>
}
return {
label: new Date(date).toLocaleString(locale),
children: Object.keys(domainEvent).includes(action) ? domainEvent[action as keyof typeof domainEvent] : action,
color,
dot,
pending: new Date(date).getTime() > new Date().getTime()
}
}
)
}
/>
}

View File

@@ -1,77 +1,14 @@
import React, {useState} from "react";
import {
Avatar,
Badge,
Card,
Divider,
Empty,
Flex,
Form,
FormProps,
Input,
List,
message,
Skeleton,
Space,
Tag,
Timeline,
Typography
} from "antd";
import {
BankOutlined,
ClockCircleOutlined,
DeleteOutlined,
IdcardOutlined,
ReloadOutlined,
SearchOutlined,
ShareAltOutlined,
SignatureOutlined,
SyncOutlined,
ToolOutlined,
UserOutlined
} from "@ant-design/icons";
import {Badge, Card, Divider, Empty, Flex, FormProps, message, Skeleton, Space, Tag} from "antd";
import {Domain, getDomain} from "../../utils/api";
import {AxiosError} from "axios"
import vCard from 'vcf'
import {t} from 'ttag'
import {DomainSearchBar, FieldType} from "../../components/search/DomainSearchBar";
import {EventTimeline} from "../../components/search/EventTimeline";
import {EntitiesList} from "../../components/search/EntitiesList";
type FieldType = {
ldhName: string
}
const locale = navigator.language.split('-')[0]
export default function DomainSearchPage() {
const domainRole = {
registrant: t`Registrant`,
technical: t`Technical`,
administrative: t`Administrative`,
abuse: t`Abuse`,
billing: t`Billing`,
registrar: t`Registrar`,
reseller: t`Reseller`,
sponsor: t`Sponsor`,
proxy: t`Proxy`,
notifications: t`Notifications`,
noc: t`Noc`
}
const domainEvent = {
registration: t`Registration`,
reregistration: t`Reregistration`,
'last changed': t`Last changed`,
expiration: t`Expiration`,
deletion: t`Deletion`,
reinstantiation: t`Reinstantiation`,
transfer: t`Transfer`,
locked: t`Locked`,
unlocked: t`Unlocked`,
'registrar expiration': t`Registrar expiration`,
'enum validation expiration': t`ENUM validation expiration`
}
const [domain, setDomain] = useState<Domain | null>()
const [messageApi, contextHolder] = message.useMessage()
@@ -90,29 +27,7 @@ export default function DomainSearchPage() {
return <Flex gap="middle" align="center" justify="center" vertical>
<Card title={t`Domain finder`} style={{width: '100%'}}>
{contextHolder}
<Form
name="basic"
labelCol={{span: 8}}
wrapperCol={{span: 16}}
onFinish={onFinish}
autoComplete="off"
>
<Form.Item<FieldType>
name="ldhName"
rules={[{
required: true,
message: t`Required`
}, {
pattern: /^(?=.*\.)\S*[^.\s]$/,
message: t`This domain name does not appear to be valid`,
max: 63,
min: 2
}]}
>
<Input size="large" prefix={<SearchOutlined/>} placeholder="example.com" autoFocus={true}
autoComplete='off'/>
</Form.Item>
</Form>
<DomainSearchBar onFinish={onFinish}/>
<Skeleton loading={domain === null} active>
{
@@ -140,89 +55,19 @@ export default function DomainSearchPage() {
</>
}
<Divider orientation="left">{t`Timeline`}</Divider>
<Timeline
mode="right"
items={domain.events
.sort((e1, e2) => new Date(e2.date).getTime() - new Date(e1.date).getTime())
.map(({action, date}) => {
let color, dot
if (action === 'registration') {
color = 'green'
dot = <SignatureOutlined style={{fontSize: '16px'}}/>
} else if (action === 'expiration') {
color = 'red'
dot = <ClockCircleOutlined style={{fontSize: '16px'}}/>
} else if (action === 'transfer') {
color = 'orange'
dot = <ShareAltOutlined style={{fontSize: '16px'}}/>
} else if (action === 'last changed') {
color = 'blue'
dot = <SyncOutlined style={{fontSize: '16px'}}/>
} else if (action === 'deletion') {
color = 'red'
dot = <DeleteOutlined style={{fontSize: '16px'}}/>
} else if (action === 'reregistration') {
color = 'green'
dot = <ReloadOutlined style={{fontSize: '16px'}}/>
}
return {
label: new Date(date).toLocaleString(locale),
children: Object.keys(domainEvent).includes(action) ? domainEvent[action as keyof typeof domainEvent] : action,
color,
dot,
pending: new Date(date).getTime() > new Date().getTime()
}
}
)
}
/>
<EventTimeline domain={domain}/>
{
domain.entities.length > 0 &&
<>
<Divider orientation="left">{t`Entities`}</Divider>
<List
className="demo-loadmore-list"
itemLayout="horizontal"
dataSource={domain.entities.sort((e1, e2) => {
const p = (r: string[]) => r.includes('registrant') ? 4 : r.includes('administrative') ? 3 : r.includes('billing') ? 2 : 1
return p(e2.roles) - p(e1.roles)
})}
renderItem={(e) => {
const jCard = vCard.fromJSON(e.entity.jCard)
let name = ''
if (jCard.data.fn !== undefined && !Array.isArray(jCard.data.fn)) name = jCard.data.fn.valueOf()
return <List.Item>
<List.Item.Meta
avatar={<Avatar style={{backgroundColor: '#87d068'}}
icon={e.roles.includes('registrant') ?
<SignatureOutlined/> : e.roles.includes('registrar') ?
<BankOutlined/> :
e.roles.includes('technical') ?
<ToolOutlined/> :
e.roles.includes('administrative') ?
<IdcardOutlined/> :
<UserOutlined/>}/>}
title={e.entity.handle}
description={name}
/>
<div>{e.roles.map((r) => Object.keys(domainRole).includes(r) ? domainRole[r as keyof typeof domainRole] : r).join(', ')}</div>
</List.Item>
}}
/>
<EntitiesList domain={domain}/>
</>
}
</Card>
</Badge.Ribbon>
</Space>
: <Empty
description={
<Typography.Text>
{t`Although the domain exists in my database, it has been deleted from the WHOIS by its registrar.`}
</Typography.Text>
}/>)
description={t`Although the domain exists in my database, it has been deleted from the WHOIS by its registrar.`}/>)
}
</Skeleton>
</Card>

View File

@@ -3,134 +3,134 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Plural-Forms: nplurals=2; plural=(n!=1);\n"
#: assets/pages/search/DomainSearchPage.tsx:48
msgid "Registrant"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:49
msgid "Technical"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:50
msgid "Administrative"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:51
msgid "Abuse"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:52
msgid "Billing"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:53
msgid "Registrar"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:54
msgid "Reseller"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:55
msgid "Sponsor"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:56
msgid "Proxy"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:57
msgid "Notifications"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:58
msgid "Noc"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:62
#: assets/components/search/EventTimeline.tsx:17
msgid "Registration"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:63
#: assets/components/search/EventTimeline.tsx:18
msgid "Reregistration"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:64
#: assets/components/search/EventTimeline.tsx:19
msgid "Last changed"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:65
#: assets/components/search/EventTimeline.tsx:20
msgid "Expiration"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:66
#: assets/components/search/EventTimeline.tsx:21
msgid "Deletion"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:67
#: assets/components/search/EventTimeline.tsx:22
msgid "Reinstantiation"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:68
#: assets/components/search/EventTimeline.tsx:23
msgid "Transfer"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:69
#: assets/components/search/EventTimeline.tsx:24
msgid "Locked"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:70
#: assets/components/search/EventTimeline.tsx:25
msgid "Unlocked"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:71
#: assets/components/search/EventTimeline.tsx:26
msgid "Registrar expiration"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:72
#: assets/components/search/EventTimeline.tsx:27
msgid "ENUM validation expiration"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:82
msgid "Found !"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:86
#: assets/pages/tracking/WatchlistPage.tsx:91
msgid "An error occurred"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:91
msgid "Domain finder"
msgstr ""
#: assets/components/search/DomainSearchBar.tsx:23
#: assets/pages/LoginPage.tsx:53
#: assets/pages/LoginPage.tsx:61
#: assets/pages/search/DomainSearchPage.tsx:104
#: assets/pages/tracking/WatchlistPage.tsx:137
#: assets/pages/tracking/WatchlistPage.tsx:197
#: assets/pages/tracking/WatchlistPage.tsx:207
msgid "Required"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:107
#: assets/components/search/DomainSearchBar.tsx:26
#: assets/pages/tracking/WatchlistPage.tsx:140
msgid "This domain name does not appear to be valid"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:132
#: assets/components/search/EntitiesList.tsx:10
msgid "Registrant"
msgstr ""
#: assets/components/search/EntitiesList.tsx:11
msgid "Technical"
msgstr ""
#: assets/components/search/EntitiesList.tsx:12
msgid "Administrative"
msgstr ""
#: assets/components/search/EntitiesList.tsx:13
msgid "Abuse"
msgstr ""
#: assets/components/search/EntitiesList.tsx:14
msgid "Billing"
msgstr ""
#: assets/components/search/EntitiesList.tsx:15
msgid "Registrar"
msgstr ""
#: assets/components/search/EntitiesList.tsx:16
msgid "Reseller"
msgstr ""
#: assets/components/search/EntitiesList.tsx:17
msgid "Sponsor"
msgstr ""
#: assets/components/search/EntitiesList.tsx:18
msgid "Proxy"
msgstr ""
#: assets/components/search/EntitiesList.tsx:19
msgid "Notifications"
msgstr ""
#: assets/components/search/EntitiesList.tsx:20
msgid "Noc"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:19
msgid "Found !"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:23
#: assets/pages/tracking/WatchlistPage.tsx:91
msgid "An error occurred"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:28
msgid "Domain finder"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:47
msgid "EPP Status Codes"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:142
#: assets/pages/search/DomainSearchPage.tsx:57
msgid "Timeline"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:184
#: assets/pages/search/DomainSearchPage.tsx:62
msgid "Entities"
msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:223
#: assets/pages/search/DomainSearchPage.tsx:70
msgid ""
"Although the domain exists in my database, it has been deleted from the "
"WHOIS by its registrar."