domain-watchdog/assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx

125 lines
4.3 KiB
TypeScript
Raw Normal View History

2024-08-16 03:54:48 +02:00
import {Button, Flex, Modal, Space, Typography} from "antd"
2024-08-15 22:58:15 +02:00
import {t} from "ttag"
2024-08-16 03:54:48 +02:00
import React, {useEffect, useState} from "react"
2024-08-15 22:58:15 +02:00
import {ApartmentOutlined} from "@ant-design/icons"
2024-08-16 13:56:52 +02:00
import vCard from "vcf";
2024-08-15 22:58:15 +02:00
2024-08-16 03:54:48 +02:00
import '@xyflow/react/dist/style.css'
import {Background, Controls, MiniMap, ReactFlow, useEdgesState, useNodesState} from "@xyflow/react";
2024-08-16 13:56:52 +02:00
import {getWatchlist, Watchlist} from "../../../utils/api";
import {translateRoles} from "../../search/EntitiesList";
import {getLayoutedElements} from "./getLayoutedElements";
2024-08-16 03:54:48 +02:00
function watchlistToNodes(watchlist: Watchlist) {
const domains = watchlist.domains.map(d => ({
id: d.ldhName,
data: {label: <b>{d.ldhName}</b>},
style: {
width: 200
}
}))
const entities = [...new Set(watchlist.domains
.map(d => d.entities
.filter(e => !e.roles.includes('registrar'))
.map(e => e.entity
)
).flat())].map(e => {
const jCard = vCard.fromJSON(e.jCard)
let label = e.handle
if (jCard.data.fn !== undefined && !Array.isArray(jCard.data.fn)) label = jCard.data.fn.valueOf()
return {
id: e.handle,
data: {label},
style: {
width: 200
}
}
})
return [...domains, ...entities]
}
const rolesToColor = (roles: string[]) => roles.includes('registrant') ? 'green' :
2024-08-16 13:56:52 +02:00
roles.includes('administrative') ? 'blue' :
roles.includes('technical') ? 'orange' : 'violet'
2024-08-16 03:54:48 +02:00
function watchlistToEdges(watchlist: Watchlist) {
2024-08-16 13:56:52 +02:00
const domainRole = translateRoles()
2024-08-16 03:54:48 +02:00
return watchlist.domains
.map(d => d.entities
.filter(e => !e.roles.includes('registrar'))
.map(e => ({
id: `${d.ldhName}-${e.entity.handle}`,
source: e.roles.includes('technical') ? d.ldhName : e.entity.handle,
target: e.roles.includes('technical') ? e.entity.handle : d.ldhName,
style: {stroke: rolesToColor(e.roles), strokeWidth: 3},
2024-08-16 13:56:52 +02:00
label: e.roles.map(r => Object.keys(domainRole).includes(r) ? domainRole[r as keyof typeof domainRole] : r).join(', '),
2024-08-16 03:54:48 +02:00
animated: e.roles.includes('registrant'),
}))
).flat(2)
}
export function ViewDiagramWatchlistButton({token}: { token: string }) {
2024-08-15 22:58:15 +02:00
const [open, setOpen] = useState(false)
2024-08-16 03:54:48 +02:00
const [loading, setLoading] = useState(false)
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
useEffect(() => {
if (!open) return
setLoading(true)
getWatchlist(token).then(w => {
const e = getLayoutedElements(watchlistToNodes(w), watchlistToEdges(w))
setNodes(e.nodes)
setEdges(e.edges)
}).catch(() => setOpen(false)).finally(() => setLoading(false))
}, [open])
2024-08-15 22:58:15 +02:00
return <>
<Typography.Link>
<ApartmentOutlined title={t`View the Watchlist Entity Diagram`}
style={{color: 'darkviolet'}}
onClick={() => setOpen(true)}/>
</Typography.Link>
<Modal
title={t`Watchlist Entity Diagram`}
centered
open={open}
2024-08-16 03:54:48 +02:00
loading={loading}
2024-08-15 22:58:15 +02:00
footer={
<Space>
<Button type="default" onClick={() => setOpen(false)}>
Close
</Button>
</Space>
}
onOk={() => setOpen(false)}
onCancel={() => setOpen(false)}
2024-08-16 03:54:48 +02:00
width='80vw'
2024-08-15 22:58:15 +02:00
>
2024-08-16 03:54:48 +02:00
{nodes && edges && <Flex style={{width: '75vw', height: '80vh'}}>
<ReactFlow
2024-08-16 13:56:52 +02:00
fitView
colorMode='system'
nodesConnectable={false}
edgesReconnectable={false}
2024-08-16 03:54:48 +02:00
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
style={{width: '100%', height: '100vh'}}
>
<MiniMap/>
<Controls/>
<Background/>
</ReactFlow>
</Flex>}
2024-08-15 22:58:15 +02:00
</Modal>
</>
}