265 lines
11 KiB
TypeScript
Raw Normal View History

2024-12-31 13:55:42 +01:00
import type {ReactElement} from 'react'
import React, {useEffect, useState} from 'react'
2024-12-31 13:55:42 +01:00
import type {Domain} from '../../../utils/api'
import {getTrackedDomainList} from '../../../utils/api'
import {Button, Empty, Flex, Result, Skeleton, Table, Tag, Tooltip} from 'antd'
2024-12-30 23:50:15 +01:00
import {t} from 'ttag'
2024-12-31 13:55:42 +01:00
import type {ColumnType} from 'antd/es/table'
2024-12-30 23:50:15 +01:00
import {rdapStatusCodeDetailTranslation} from '../../../utils/functions/rdapTranslation'
import {eppStatusCodeToColor} from '../../../utils/functions/eppStatusCodeToColor'
import {Link} from 'react-router-dom'
import {
BankOutlined,
CheckOutlined,
DeleteOutlined,
ExceptionOutlined,
ExclamationCircleOutlined,
FieldTimeOutlined,
KeyOutlined,
MonitorOutlined,
SafetyCertificateOutlined
} from '@ant-design/icons'
import {DomainToTag} from '../../../utils/functions/DomainToTag'
import {isDomainLocked} from "../../../utils/functions/isDomainLocked"
2024-09-09 11:31:33 +02:00
export function TrackedDomainTable() {
2024-12-30 23:50:15 +01:00
const REDEMPTION_NOTICE = (
<Tooltip
title={t`At least one domain name is in redemption period and will potentially be deleted soon`}
>
<Tag color={eppStatusCodeToColor('redemption period')}>redemption period</Tag>
</Tooltip>
)
const PENDING_DELETE_NOTICE = (
<Tooltip
title={t`At least one domain name is pending deletion and will soon become available for registration again`}
>
<Tag color={eppStatusCodeToColor('pending delete')}>pending delete</Tag>
</Tooltip>
)
interface TableRow {
key: string
ldhName: ReactElement
expirationDate: string
status: ReactElement[]
state: ReactElement
2024-12-30 23:50:15 +01:00
updatedAt: string
rawDomain: Domain
2024-12-30 23:50:15 +01:00
}
2024-09-09 11:31:33 +02:00
2024-12-30 23:50:15 +01:00
const [dataTable, setDataTable] = useState<TableRow[]>([])
const [total, setTotal] = useState<number>()
const [specialNotice, setSpecialNotice] = useState<ReactElement[]>([])
2024-09-09 11:31:33 +02:00
const rdapStatusCodeDetailTranslated = rdapStatusCodeDetailTranslation()
const fetchData = (params: { page: number, itemsPerPage: number }) => {
getTrackedDomainList(params).then(data => {
setTotal(data['hydra:totalItems'])
const notices: ReactElement[] = []
2024-09-09 11:31:33 +02:00
setDataTable(data['hydra:member'].map((d: Domain) => {
const expirationDate = d.events.find(e => e.action === 'expiration' && !e.deleted)?.date
const expiresInDays = d.expiresInDays ? -d.expiresInDays : undefined
2024-09-09 11:31:33 +02:00
if (d.status.includes('redemption period')) {
if (!notices.includes(REDEMPTION_NOTICE)) notices.push(REDEMPTION_NOTICE)
} else if (d.status.includes('pending delete')) {
if (!notices.includes(PENDING_DELETE_NOTICE)) notices.push(PENDING_DELETE_NOTICE)
}
2024-09-09 11:31:33 +02:00
return {
key: d.ldhName,
2024-12-27 21:37:19 +01:00
ldhName: <DomainToTag domain={d}/>,
2024-09-09 11:31:33 +02:00
expirationDate: expirationDate ? new Date(expirationDate).toLocaleString() : '-',
status: d.status.map(s => <Tooltip
2024-12-30 23:50:15 +01:00
key={s}
2024-09-09 11:31:33 +02:00
placement='bottomLeft'
2024-12-30 23:50:15 +01:00
title={rdapStatusCodeDetailTranslated[s as keyof typeof rdapStatusCodeDetailTranslated] || undefined}
>
2024-09-09 11:31:33 +02:00
<Tag color={eppStatusCodeToColor(s)}>{s}</Tag>
</Tooltip>
),
2024-12-03 23:24:00 +01:00
updatedAt: new Date(d.updatedAt).toLocaleString(),
rawDomain: d,
2025-01-01 22:48:56 +01:00
options: <Flex wrap justify='space-evenly' align='center' gap='4px 0'>
<Tooltip title={t`Registry Lock`}>
<Tag
bordered={false} color={isDomainLocked(d.status, 'server') ? 'green' : 'default'}
icon={<SafetyCertificateOutlined/>}
/>
</Tooltip>
<Tooltip title={t`Registrar Lock`}>
<Tag
bordered={false} color={isDomainLocked(d.status, 'client') ? 'green' : 'default'}
icon={<BankOutlined/>}
/>
</Tooltip>
<Tooltip title={t`DNSSEC`}>
<Tag
bordered={false} color={d.delegationSigned ? 'green' : 'default'}
icon={<KeyOutlined/>}
/>
</Tooltip>
</Flex>,
2025-01-01 22:48:56 +01:00
state: <Flex wrap justify='space-evenly' align='center' gap='4px 0'>
{
d.status.includes('auto renew period') ?
<Tooltip title={t`Auto-Renew Grace Period`}>
<Tag
bordered={false}
color='palevioletred'
icon={<FieldTimeOutlined/>}
/>
</Tooltip> :
d.status.includes('redemption period') ?
<Tooltip title={t`Redemption Grace Period`}>
<Tag
bordered={false}
color='magenta'
icon={<ExclamationCircleOutlined/>}
/>
</Tooltip> :
!d.status.includes('redemption period') && d.status.includes('pending delete') ?
<Tooltip title={t`Pending Delete`}>
<Tag
bordered={false}
color='orangered'
icon={<DeleteOutlined/>}
/>
</Tooltip> : <Tooltip title={t`Active`}>
<Tag
bordered={false}
color='green'
icon={<CheckOutlined/>}
/>
</Tooltip>
}
{
d.expiresInDays && <Tooltip title={t`Estimated number of days until WHOIS removal`}>
<Tag bordered={false}
color={d.expiresInDays <= 5 ? 'red' : d.expiresInDays <= 35 ? 'orange' : 'default'}>
{t`J ${expiresInDays}`}
</Tag>
</Tooltip>
}
</Flex>
2024-09-09 11:31:33 +02:00
}
}))
setSpecialNotice(notices)
2024-09-09 11:31:33 +02:00
})
}
useEffect(() => {
fetchData({page: 1, itemsPerPage: 30})
}, [])
2024-12-30 23:50:15 +01:00
interface RecordType {
rawDomain: Domain
2024-12-30 23:50:15 +01:00
}
2024-12-30 23:50:15 +01:00
const columns: Array<ColumnType<RecordType>> = [
2024-09-09 11:31:33 +02:00
{
title: t`Domain`,
dataIndex: 'ldhName',
width: '20%',
align: 'left'
},
{
title: t`Status`,
dataIndex: 'state',
width: '10%',
align: 'center'
},
{
title: t`Options`,
dataIndex: 'options',
width: '10%',
align: 'center',
2024-09-09 11:31:33 +02:00
},
{
title: t`Expiration date`,
2024-12-03 23:24:00 +01:00
dataIndex: 'expirationDate',
2024-12-30 23:50:15 +01:00
sorter: (a: RecordType, b: RecordType) => {
const expirationDate1 = a.rawDomain.events.find(e => e.action === 'expiration' && !e.deleted)?.date
const expirationDate2 = b.rawDomain.events.find(e => e.action === 'expiration' && !e.deleted)?.date
2024-12-03 23:24:00 +01:00
if (expirationDate1 === undefined || expirationDate2 === undefined) return 0
return new Date(expirationDate1).getTime() - new Date(expirationDate2).getTime()
},
width: '15%',
align: 'center'
2024-09-09 11:31:33 +02:00
},
{
title: t`Updated at`,
dataIndex: 'updatedAt',
responsive: ['md'],
sorter: (a: RecordType, b: RecordType) => new Date(a.rawDomain.updatedAt).getTime() - new Date(b.rawDomain.updatedAt).getTime(),
width: '15%',
align: 'center'
},
2024-09-09 11:31:33 +02:00
{
title: t`EPP Status Codes`,
2024-12-03 23:24:00 +01:00
dataIndex: 'status',
responsive: ['md'],
2024-12-03 23:24:00 +01:00
showSorterTooltip: {target: 'full-header'},
filters: [...new Set(dataTable.map((d: RecordType) => d.rawDomain.status).flat())].map(s => ({
2024-12-03 23:24:00 +01:00
text: <Tooltip
placement='bottomLeft'
2024-12-30 23:50:15 +01:00
title={rdapStatusCodeDetailTranslated[s as keyof typeof rdapStatusCodeDetailTranslated] || undefined}
>
2024-12-03 23:24:00 +01:00
<Tag color={eppStatusCodeToColor(s)}>{s}</Tag>
</Tooltip>,
2024-12-30 23:50:15 +01:00
value: s
2024-12-03 23:24:00 +01:00
})),
onFilter: (value, record: RecordType) => record.rawDomain.status.includes(value as string),
width: '30%'
2024-09-09 11:31:33 +02:00
}
]
return total === 0
? <Empty
description={t`No tracked domain names were found, please create your first Watchlist`}
>
<Link to='/tracking/watchlist'>
<Button type='primary'>Create Now</Button>
</Link>
</Empty>
: <Skeleton loading={total === undefined}>
<Result
style={{paddingTop: 0}}
subTitle={t`Please note that this table does not include domain names marked as expired or those with an unknown expiration date`}
{...(specialNotice.length > 0
? {
icon: <ExceptionOutlined/>,
status: 'warning',
title: t`At least one domain name you are tracking requires special attention`,
extra: specialNotice
}
: {
icon: <MonitorOutlined/>,
status: 'info',
title: t`The domain names below are subject to special monitoring`
})}
/>
<Table
loading={total === undefined}
columns={columns}
dataSource={dataTable}
pagination={{
total,
hideOnSinglePage: true,
defaultPageSize: 30,
onChange: (page, itemsPerPage) => {
fetchData({page, itemsPerPage})
}
}}
scroll={{y: '50vh'}}
/>
</Skeleton>
2024-12-30 23:50:15 +01:00
}