From 8a5ade89a9648cd87a7f4c581a6d14ade7e88f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Gangloff?= Date: Sun, 18 Aug 2024 17:28:45 +0200 Subject: [PATCH] feat: add qr-code for iCalendar export --- .../watchlist/CalendarWatchlistButton.tsx | 22 +++++ .../tracking/watchlist/WatchlistCard.tsx | 87 +++++++++++++++++++ .../tracking/watchlist/WatchlistsList.tsx | 86 ++---------------- .../diagram/ViewDiagramWatchlistButton.tsx | 19 ++-- .../watchlist/diagram/watchlistToEdges.tsx | 7 +- .../watchlist/diagram/watchlistToNodes.tsx | 11 ++- translations/translations.pot | 26 +++--- 7 files changed, 151 insertions(+), 107 deletions(-) create mode 100644 assets/components/tracking/watchlist/CalendarWatchlistButton.tsx create mode 100644 assets/components/tracking/watchlist/WatchlistCard.tsx diff --git a/assets/components/tracking/watchlist/CalendarWatchlistButton.tsx b/assets/components/tracking/watchlist/CalendarWatchlistButton.tsx new file mode 100644 index 0000000..9e541bf --- /dev/null +++ b/assets/components/tracking/watchlist/CalendarWatchlistButton.tsx @@ -0,0 +1,22 @@ +import {CalendarFilled} from "@ant-design/icons"; +import {t} from "ttag"; +import {Popover, QRCode, Typography} from "antd"; +import React from "react"; +import {Watchlist} from "../../../pages/tracking/WatchlistPage"; + +export function CalendarWatchlistButton({watchlist}: { watchlist: Watchlist }) { + + const icsResourceLink = `${window.location.origin}/api/watchlists/${watchlist.token}/calendar` + + return + }> + + + +} \ No newline at end of file diff --git a/assets/components/tracking/watchlist/WatchlistCard.tsx b/assets/components/tracking/watchlist/WatchlistCard.tsx new file mode 100644 index 0000000..2ef9827 --- /dev/null +++ b/assets/components/tracking/watchlist/WatchlistCard.tsx @@ -0,0 +1,87 @@ +import {Card, Divider, Flex, Space, Table, Tag, Typography} from "antd"; +import {DisconnectOutlined, LinkOutlined} from "@ant-design/icons"; +import {t} from "ttag"; +import {ViewDiagramWatchlistButton} from "./diagram/ViewDiagramWatchlistButton"; +import {UpdateWatchlistButton} from "./UpdateWatchlistButton"; +import {DeleteWatchlistButton} from "./DeleteWatchlistButton"; +import punycode from "punycode/punycode"; +import {actionToColor, domainEvent} from "../../search/EventTimeline"; +import React, {useState} from "react"; +import {Watchlist} from "../../../pages/tracking/WatchlistPage"; +import {Connector} from "../../../utils/api/connectors"; +import useBreakpoint from "../../../hooks/useBreakpoint"; +import {CalendarWatchlistButton} from "./CalendarWatchlistButton"; + +export function WatchlistCard({watchlist, onUpdateWatchlist, connectors, onDelete}: { + watchlist: Watchlist, + onUpdateWatchlist: (values: { domains: string[], triggers: string[], token: string }) => Promise, + connectors: (Connector & { id: string })[], + onDelete: () => void +}) { + const sm = useBreakpoint('sm') + const domainEventTranslated = domainEvent() + + const columns = [ + { + title: t`Domain names`, + dataIndex: 'domains' + }, + { + title: t`Tracked events`, + dataIndex: 'events' + } + ] + + return <> + + { + watchlist.connector ? + } color="lime-inverse" title={watchlist.connector.id}/> : + } color="default" + title={t`This Watchlist is not linked to a Connector.`}/> + } + + {t`Watchlist` + (watchlist.name ? ` (${watchlist.name})` : '')} + + + } + size='small' + style={{width: '100%'}} + extra={ + + + + + + + + + + } + > + + {punycode.toUnicode(d.ldhName)}), + events: watchlist.triggers?.filter(t => t.action === 'email') + .map(t => + {domainEventTranslated[t.event as keyof typeof domainEventTranslated]} + + ) + }]} + {...(sm ? {scroll: {y: 'max-content'}} : {scroll: {y: 240}})} + /> + + + +} \ No newline at end of file diff --git a/assets/components/tracking/watchlist/WatchlistsList.tsx b/assets/components/tracking/watchlist/WatchlistsList.tsx index aed54fd..2cb01f1 100644 --- a/assets/components/tracking/watchlist/WatchlistsList.tsx +++ b/assets/components/tracking/watchlist/WatchlistsList.tsx @@ -1,15 +1,7 @@ -import {Card, Divider, Space, Table, Tag, Typography} from "antd"; -import {t} from "ttag"; -import {CalendarFilled, DisconnectOutlined, LinkOutlined} from "@ant-design/icons"; import React from "react"; -import useBreakpoint from "../../../hooks/useBreakpoint"; -import {actionToColor, domainEvent} from "../../search/EventTimeline"; import {Watchlist} from "../../../pages/tracking/WatchlistPage"; -import punycode from "punycode/punycode"; import {Connector} from "../../../utils/api/connectors"; -import {UpdateWatchlistButton} from "./UpdateWatchlistButton"; -import {DeleteWatchlistButton} from "./DeleteWatchlistButton"; -import {ViewDiagramWatchlistButton} from "./diagram/ViewDiagramWatchlistButton"; +import {WatchlistCard} from "./WatchlistCard"; export function WatchlistsList({watchlists, onDelete, onUpdateWatchlist, connectors}: { watchlists: Watchlist[], @@ -17,79 +9,15 @@ export function WatchlistsList({watchlists, onDelete, onUpdateWatchlist, connect onUpdateWatchlist: (values: { domains: string[], triggers: string[], token: string }) => Promise, connectors: (Connector & { id: string })[] }) { - const sm = useBreakpoint('sm') - const domainEventTranslated = domainEvent() - - - const columns = [ - { - title: t`Domain names`, - dataIndex: 'domains' - }, - { - title: t`Tracked events`, - dataIndex: 'events' - } - ] return <> {watchlists.map(watchlist => - <> - - { - watchlist.connector ? - } color="lime-inverse" title={watchlist.connector.id}/> : - } color="default" - title={t`This Watchlist is not linked to a Connector.`}/> - } - - {t`Watchlist` + (watchlist.name ? ` (${watchlist.name})` : '')} - - - } - size='small' - style={{width: '100%'}} - extra={ - - - - - - - - - - - - } - > - -
{punycode.toUnicode(d.ldhName)}), - events: watchlist.triggers?.filter(t => t.action === 'email') - .map(t => - {domainEventTranslated[t.event as keyof typeof domainEventTranslated]} - - ) - }]} - {...(sm ? {scroll: {y: 'max-content'}} : {scroll: {y: 240}})} - /> - - - - )} + + ) + } } \ No newline at end of file diff --git a/assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx b/assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx index 75875b4..fbc4395 100644 --- a/assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx +++ b/assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx @@ -10,12 +10,6 @@ import {getLayoutedElements} from "./getLayoutedElements"; import {watchlistToNodes} from "./watchlistToNodes"; import {watchlistToEdges} from "./watchlistToEdges"; -export type DiagramConfig = { - tld?: boolean - nameserver?: boolean - entities?: boolean -} - export function ViewDiagramWatchlistButton({token}: { token: string }) { const [open, setOpen] = useState(false) @@ -24,9 +18,11 @@ export function ViewDiagramWatchlistButton({token}: { token: string }) { const [edges, setEdges, onEdgesChange] = useEdgesState([]) useEffect(() => { - setNodes([]) setEdges([]) + setNodes([]) + }, []) + useEffect(() => { if (!open) return setLoading(true) getWatchlist(token).then(w => { @@ -58,19 +54,22 @@ export function ViewDiagramWatchlistButton({token}: { token: string }) { } onOk={() => setOpen(false)} onCancel={() => setOpen(false)} - width='85vw' + width='90vw' + height='100%' > - + diff --git a/assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx b/assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx index 7284258..1111858 100644 --- a/assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx +++ b/assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx @@ -40,9 +40,10 @@ export const tldToEdge = (d: Domain) => ({ label: t`Registry` }) -export function watchlistToEdges(watchlist: Watchlist) { - const entitiesEdges = watchlist.domains.map(d => domainEntitiesToEdges(d)).flat() +export function watchlistToEdges(watchlist: Watchlist, withRegistrar = false, withTld = false) { + const entitiesEdges = watchlist.domains.map(d => domainEntitiesToEdges(d, withRegistrar)).flat() const nameserversEdges = watchlist.domains.map(domainNSToEdges).flat() + const tldEdge = watchlist.domains.map(tldToEdge) - return [...entitiesEdges, ...nameserversEdges] + return [...entitiesEdges, ...nameserversEdges, ...(withTld ? tldEdge : [])] } diff --git a/assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx b/assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx index 30186cb..c2818d8 100644 --- a/assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx +++ b/assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx @@ -20,6 +20,7 @@ export const domainEntitiesToNode = (d: Domain, withRegistrar = false) => d.enti return { id: e.entity.handle, + type: e.roles.includes('registrant') || e.roles.includes('registrar') ? 'input' : 'output', data: {label}, style: { width: 200 @@ -30,6 +31,7 @@ export const domainEntitiesToNode = (d: Domain, withRegistrar = false) => d.enti export const tldToNode = (tld: Tld) => ({ id: tld.tld, data: {label: t`.${tld.tld} Registry`}, + type: 'input', style: { width: 200 } @@ -38,17 +40,18 @@ export const tldToNode = (tld: Tld) => ({ export const nsToNode = (ns: Nameserver) => ({ id: ns.ldhName, data: {label: ns.ldhName}, + type: 'output', style: { width: 200 } }) -export function watchlistToNodes(watchlist: Watchlist) { +export function watchlistToNodes(watchlist: Watchlist, withRegistrar = false, withTld = false) { const domains = watchlist.domains.map(domainToNode) - const entities = [...new Set(watchlist.domains.map(d => domainEntitiesToNode(d)).flat())] + const entities = [...new Set(watchlist.domains.map(d => domainEntitiesToNode(d, withRegistrar)).flat())] const tlds = [...new Set(watchlist.domains.map(d => d.tld))].map(tldToNode) - const nameservers = [...new Set(watchlist.domains.map(d => d.nameservers))].flat().map(nsToNode) + const nameservers = [...new Set(watchlist.domains.map(d => d.nameservers))].flat().map(nsToNode, withRegistrar) - return [...domains, ...entities, ...nameservers] + return [...domains, ...entities, ...nameservers, ...(withTld ? tlds : [])] } \ No newline at end of file diff --git a/translations/translations.pot b/translations/translations.pot index 2a5dca5..32a8269 100644 --- a/translations/translations.pot +++ b/translations/translations.pot @@ -252,8 +252,8 @@ msgstr "" msgid "At least one domain name" msgstr "" +#: assets/components/tracking/watchlist/WatchlistCard.tsx:26 #: assets/components/tracking/watchlist/WatchlistForm.tsx:100 -#: assets/components/tracking/watchlist/WatchlistsList.tsx:26 msgid "Domain names" msgstr "" @@ -265,8 +265,8 @@ msgstr "" msgid "Add a Domain name" msgstr "" +#: assets/components/tracking/watchlist/WatchlistCard.tsx:30 #: assets/components/tracking/watchlist/WatchlistForm.tsx:142 -#: assets/components/tracking/watchlist/WatchlistsList.tsx:30 msgid "Tracked events" msgstr "" @@ -317,11 +317,11 @@ msgstr "" msgid "Cancel" msgstr "" -#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:43 +#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:39 msgid "View the Watchlist Entity Diagram" msgstr "" -#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:48 +#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:44 msgid "Watchlist Entity Diagram" msgstr "" @@ -329,11 +329,19 @@ msgstr "" msgid "Registry" msgstr "" -#: assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx:32 +#: assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx:33 #, javascript-format msgid ".${ tld.tld } Registry" msgstr "" +#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:14 +msgid "QR Code for iCalendar export" +msgstr "" + +#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:17 +msgid "Export events to iCalendar format" +msgstr "" + #: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:12 #: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:19 msgid "Delete the Watchlist" @@ -343,18 +351,14 @@ msgstr "" msgid "Are you sure to delete this Watchlist?" msgstr "" -#: assets/components/tracking/watchlist/WatchlistsList.tsx:46 +#: assets/components/tracking/watchlist/WatchlistCard.tsx:43 msgid "This Watchlist is not linked to a Connector." msgstr "" -#: assets/components/tracking/watchlist/WatchlistsList.tsx:49 +#: assets/components/tracking/watchlist/WatchlistCard.tsx:46 msgid "Watchlist" msgstr "" -#: assets/components/tracking/watchlist/WatchlistsList.tsx:60 -msgid "Export events to iCalendar format" -msgstr "" - #: assets/components/Sider.tsx:29 msgid "Home" msgstr ""