feat: add link on domain name tags

This commit is contained in:
Maël Gangloff 2024-12-27 21:37:19 +01:00
parent 8d43290f0e
commit d3adc4b4ef
No known key found for this signature in database
GPG Key ID: 11FDC81C24A7F629
7 changed files with 59 additions and 39 deletions

View File

@ -96,6 +96,7 @@ export default function App() {
<Route path="/home" element={<TextPage resource='home.md'/>}/> <Route path="/home" element={<TextPage resource='home.md'/>}/>
<Route path="/search/domain" element={<DomainSearchPage/>}/> <Route path="/search/domain" element={<DomainSearchPage/>}/>
<Route path="/search/domain/:query" element={<DomainSearchPage/>}/>
<Route path="/search/entity" element={<EntitySearchPage/>}/> <Route path="/search/entity" element={<EntitySearchPage/>}/>
<Route path="/search/nameserver" element={<NameserverSearchPage/>}/> <Route path="/search/nameserver" element={<NameserverSearchPage/>}/>
<Route path="/search/tld" element={<TldPage/>}/> <Route path="/search/tld" element={<TldPage/>}/>

View File

@ -0,0 +1,21 @@
import {Tag} from "antd";
import {DeleteOutlined, ExclamationCircleOutlined} from "@ant-design/icons";
import punycode from "punycode/punycode";
import {Link} from "react-router-dom";
import React from "react";
export function DomainToTag({domain}: { domain: { ldhName: string, deleted: boolean, status: string[] } }) {
return <Link to={'/search/domain/' + domain.ldhName}>
<Tag
color={
domain.deleted ? 'magenta' :
domain.status.includes('redemption period') ? 'yellow' :
domain.status.includes('pending delete') ? 'volcano' : 'default'
}
icon={
domain.deleted ? <DeleteOutlined/> :
domain.status.includes('redemption period') ? <ExclamationCircleOutlined/> :
domain.status.includes('pending delete') ? <DeleteOutlined/> : null
}>{punycode.toUnicode(domain.ldhName)}</Tag>
</Link>
}

View File

@ -7,6 +7,7 @@ import {rdapStatusCodeDetailTranslation} from "../../../utils/functions/rdapTran
import {eppStatusCodeToColor} from "../../../utils/functions/eppStatusCodeToColor"; import {eppStatusCodeToColor} from "../../../utils/functions/eppStatusCodeToColor";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import {ExceptionOutlined, MonitorOutlined} from '@ant-design/icons' import {ExceptionOutlined, MonitorOutlined} from '@ant-design/icons'
import {DomainToTag} from "../DomainToTag";
export function TrackedDomainTable() { export function TrackedDomainTable() {
@ -42,7 +43,7 @@ export function TrackedDomainTable() {
return { return {
key: d.ldhName, key: d.ldhName,
ldhName: d.ldhName, ldhName: <DomainToTag domain={d}/>,
expirationDate: expirationDate ? new Date(expirationDate).toLocaleString() : '-', expirationDate: expirationDate ? new Date(expirationDate).toLocaleString() : '-',
status: d.status.map(s => <Tooltip status: d.status.map(s => <Tooltip
placement='bottomLeft' placement='bottomLeft'

View File

@ -1,10 +1,9 @@
import {Card, Divider, Space, Table, Tag, Tooltip} from "antd"; import {Card, Divider, Space, Table, Tag, Tooltip} from "antd";
import {DeleteOutlined, DisconnectOutlined, ExclamationCircleOutlined, LinkOutlined} from "@ant-design/icons"; import {DisconnectOutlined, LinkOutlined} from "@ant-design/icons";
import {t} from "ttag"; import {t} from "ttag";
import {ViewDiagramWatchlistButton} from "./diagram/ViewDiagramWatchlistButton"; import {ViewDiagramWatchlistButton} from "./diagram/ViewDiagramWatchlistButton";
import {UpdateWatchlistButton} from "./UpdateWatchlistButton"; import {UpdateWatchlistButton} from "./UpdateWatchlistButton";
import {DeleteWatchlistButton} from "./DeleteWatchlistButton"; import {DeleteWatchlistButton} from "./DeleteWatchlistButton";
import punycode from "punycode/punycode";
import React from "react"; import React from "react";
import {Watchlist} from "../../../pages/tracking/WatchlistPage"; import {Watchlist} from "../../../pages/tracking/WatchlistPage";
import {Connector} from "../../../utils/api/connectors"; import {Connector} from "../../../utils/api/connectors";
@ -13,6 +12,7 @@ import {CalendarWatchlistButton} from "./CalendarWatchlistButton";
import {rdapEventDetailTranslation, rdapEventNameTranslation} from "../../../utils/functions/rdapTranslation"; import {rdapEventDetailTranslation, rdapEventNameTranslation} from "../../../utils/functions/rdapTranslation";
import {actionToColor} from "../../../utils/functions/actionToColor"; import {actionToColor} from "../../../utils/functions/actionToColor";
import {DomainToTag} from "../DomainToTag";
export function WatchlistCard({watchlist, onUpdateWatchlist, connectors, onDelete}: { export function WatchlistCard({watchlist, onUpdateWatchlist, connectors, onDelete}: {
watchlist: Watchlist, watchlist: Watchlist,
@ -78,17 +78,7 @@ export function WatchlistCard({watchlist, onUpdateWatchlist, connectors, onDelet
pagination={false} pagination={false}
style={{width: '100%'}} style={{width: '100%'}}
dataSource={[{ dataSource={[{
domains: watchlist.domains.map(d => <Tag domains: watchlist.domains.map(d => <DomainToTag domain={d}/>),
color={
d.deleted ? 'magenta' :
d.status.includes('redemption period') ? 'yellow' :
d.status.includes('pending delete') ? 'volcano' : 'default'
}
icon={
d.deleted ? <DeleteOutlined/> :
d.status.includes('redemption period') ? <ExclamationCircleOutlined/> :
d.status.includes('pending delete') ? <DeleteOutlined/> : null
}>{punycode.toUnicode(d.ldhName)}</Tag>),
events: watchlist.triggers?.filter(t => t.action === 'email') events: watchlist.triggers?.filter(t => t.action === 'email')
.map(t => <Tooltip .map(t => <Tooltip
title={t.event in rdapEventDetailTranslated ? rdapEventDetailTranslated[t.event as keyof typeof rdapEventDetailTranslated] : undefined}> title={t.event in rdapEventDetailTranslated ? rdapEventDetailTranslated[t.event as keyof typeof rdapEventDetailTranslated] : undefined}>

View File

@ -1,4 +1,4 @@
import React, {useState} from "react"; import React, {useEffect, useState} from "react";
import {Empty, Flex, FormProps, message, Skeleton} from "antd"; import {Empty, Flex, FormProps, message, Skeleton} from "antd";
import {Domain, getDomain} from "../../utils/api"; import {Domain, getDomain} from "../../utils/api";
import {AxiosError} from "axios" import {AxiosError} from "axios"
@ -6,11 +6,14 @@ import {t} from 'ttag'
import {DomainSearchBar, FieldType} from "../../components/search/DomainSearchBar"; import {DomainSearchBar, FieldType} from "../../components/search/DomainSearchBar";
import {DomainResult} from "../../components/search/DomainResult"; import {DomainResult} from "../../components/search/DomainResult";
import {showErrorAPI} from "../../utils/functions/showErrorAPI"; import {showErrorAPI} from "../../utils/functions/showErrorAPI";
import {useParams} from "react-router-dom";
export default function DomainSearchPage() { export default function DomainSearchPage() {
const [domain, setDomain] = useState<Domain | null>() const [domain, setDomain] = useState<Domain | null>()
const [messageApi, contextHolder] = message.useMessage() const [messageApi, contextHolder] = message.useMessage()
const {query} = useParams()
const onFinish: FormProps<FieldType>['onFinish'] = (values) => { const onFinish: FormProps<FieldType>['onFinish'] = (values) => {
setDomain(null) setDomain(null)
getDomain(values.ldhName).then(d => { getDomain(values.ldhName).then(d => {
@ -22,6 +25,10 @@ export default function DomainSearchPage() {
}) })
} }
useEffect(() => {
if (query) onFinish({ldhName: query})
}, [query])
return <Flex gap="middle" align="center" justify="center" vertical> return <Flex gap="middle" align="center" justify="center" vertical>
{contextHolder} {contextHolder}
<DomainSearchBar onFinish={onFinish}/> <DomainSearchBar onFinish={onFinish}/>

View File

@ -83,7 +83,7 @@ readonly class RDAPService
]; ];
/* @see https://www.iana.org/assignments/registrar-ids/registrar-ids.xhtml */ /* @see https://www.iana.org/assignments/registrar-ids/registrar-ids.xhtml */
public const ENTITY_IANA_RESERVED_IDS = [ public const IANA_RESERVED_IDS = [
1, 3, 8, 119, 365, 376, 9994, 9995, 9996, 9997, 9998, 9999, 10009, 4000001, 8888888, 1, 3, 8, 119, 365, 376, 9994, 9995, 9996, 9997, 9998, 9999, 10009, 4000001, 8888888,
]; ];
@ -509,7 +509,7 @@ readonly class RDAPService
$entity->setHandle($rdapEntity['handle']); $entity->setHandle($rdapEntity['handle']);
if (array_key_exists('vcardArray', $rdapEntity) && !in_array($rdapEntity['handle'], self::ENTITY_IANA_RESERVED_IDS)) { if (array_key_exists('vcardArray', $rdapEntity) && !in_array($rdapEntity['handle'], self::IANA_RESERVED_IDS)) {
if (empty($entity->getJCard())) { if (empty($entity->getJCard())) {
$entity->setJCard($rdapEntity['vcardArray']); $entity->setJCard($rdapEntity['vcardArray']);
} else { } else {
@ -524,7 +524,7 @@ readonly class RDAPService
} }
} }
if ($isIANAid || !array_key_exists('events', $rdapEntity) || in_array($rdapEntity['handle'], self::ENTITY_IANA_RESERVED_IDS)) { if ($isIANAid || !array_key_exists('events', $rdapEntity) || in_array($rdapEntity['handle'], self::IANA_RESERVED_IDS)) {
return $entity; return $entity;
} }

View File

@ -3,34 +3,34 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Plural-Forms: nplurals=2; plural=(n!=1);\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n"
#: assets/App.tsx:122 #: assets/App.tsx:123
msgid "TOS" msgid "TOS"
msgstr "" msgstr ""
#: assets/App.tsx:123 #: assets/App.tsx:124
msgid "Privacy Policy" msgid "Privacy Policy"
msgstr "" msgstr ""
#: assets/App.tsx:124 #: assets/App.tsx:125
msgid "FAQ" msgid "FAQ"
msgstr "" msgstr ""
#: assets/App.tsx:127 #: assets/App.tsx:128
msgid "Documentation" msgid "Documentation"
msgstr "" msgstr ""
#: assets/App.tsx:130 #: assets/App.tsx:131
#, javascript-format #, javascript-format
msgid "" msgid ""
"${ ProjectLink } is an open source project distributed under the ${ " "${ ProjectLink } is an open source project distributed under the ${ "
"LicenseLink } license." "LicenseLink } license."
msgstr "" msgstr ""
#: assets/App.tsx:143 #: assets/App.tsx:144
msgid "Official git repository" msgid "Official git repository"
msgstr "" msgstr ""
#: assets/App.tsx:146 #: assets/App.tsx:147
msgid "Submit an issue" msgid "Submit an issue"
msgstr "" msgstr ""
@ -123,7 +123,7 @@ msgid "Search"
msgstr "" msgstr ""
#: assets/components/Sider.tsx:39 #: assets/components/Sider.tsx:39
#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:68 #: assets/components/tracking/watchlist/TrackedDomainTable.tsx:69
msgid "Domain" msgid "Domain"
msgstr "" msgstr ""
@ -367,54 +367,54 @@ msgstr ""
msgid "Watchlist Entity Diagram" msgid "Watchlist Entity Diagram"
msgstr "" msgstr ""
#: assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx:37 #: assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx:42
msgid "Registry" msgid "Registry"
msgstr "" msgstr ""
#: assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx:30 #: assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx:37
#, javascript-format #, javascript-format
msgid ".${ tld.tld } Registry" msgid ".${ tld.tld } Registry"
msgstr "" msgstr ""
#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:14 #: assets/components/tracking/watchlist/TrackedDomainTable.tsx:15
msgid "" msgid ""
"At least one domain name is in redemption period and will potentially be " "At least one domain name is in redemption period and will potentially be "
"deleted soon" "deleted soon"
msgstr "" msgstr ""
#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:19 #: assets/components/tracking/watchlist/TrackedDomainTable.tsx:20
msgid "" msgid ""
"At least one domain name is pending deletion and will soon become available " "At least one domain name is pending deletion and will soon become available "
"for registration again" "for registration again"
msgstr "" msgstr ""
#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:72 #: assets/components/tracking/watchlist/TrackedDomainTable.tsx:73
msgid "Expiration date" msgid "Expiration date"
msgstr "" msgstr ""
#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:85 #: assets/components/tracking/watchlist/TrackedDomainTable.tsx:86
msgid "Updated at" msgid "Updated at"
msgstr "" msgstr ""
#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:92 #: assets/components/tracking/watchlist/TrackedDomainTable.tsx:93
msgid "Status" msgid "Status"
msgstr "" msgstr ""
#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:110 #: assets/components/tracking/watchlist/TrackedDomainTable.tsx:111
msgid "No tracked domain names were found, please create your first Watchlist" msgid "No tracked domain names were found, please create your first Watchlist"
msgstr "" msgstr ""
#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:118 #: assets/components/tracking/watchlist/TrackedDomainTable.tsx:119
msgid "" msgid ""
"Please note that this table does not include domain names marked as expired " "Please note that this table does not include domain names marked as expired "
"or those with an unknown expiration date" "or those with an unknown expiration date"
msgstr "" msgstr ""
#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:122 #: assets/components/tracking/watchlist/TrackedDomainTable.tsx:123
msgid "At least one domain name you are tracking requires special attention" msgid "At least one domain name you are tracking requires special attention"
msgstr "" msgstr ""
#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:127 #: assets/components/tracking/watchlist/TrackedDomainTable.tsx:128
msgid "The domain names below are subject to special monitoring" msgid "The domain names below are subject to special monitoring"
msgstr "" msgstr ""
@ -515,11 +515,11 @@ msgstr ""
msgid "Sorry, the page you visited does not exist." msgid "Sorry, the page you visited does not exist."
msgstr "" msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:18 #: assets/pages/search/DomainSearchPage.tsx:21
msgid "Found !" msgid "Found !"
msgstr "" msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:34 #: assets/pages/search/DomainSearchPage.tsx:41
msgid "" msgid ""
"Although the domain exists in my database, it has been deleted from the " "Although the domain exists in my database, it has been deleted from the "
"WHOIS by its registrar." "WHOIS by its registrar."