mirror of
https://github.com/Xevion/rdap.git
synced 2025-12-06 15:16:07 -06:00
feat: add CopyButton and StatusBadge components with enhanced RDAP card UX
This commit introduces two new reusable components and significantly improves the user experience across all RDAP cards: New Components: - CopyButton: Provides one-click copying functionality for handles, addresses, and other identifiers - StatusBadge: Displays color-coded status badges with proper type safety RDAP Card Enhancements: - Replace deprecated ClipboardCopyIcon with ClipboardIcon - Add copy buttons next to all handles, addresses, and identifiers - Migrate status displays from PropertyList to StatusBadge components with color coding - Replace PropertyList with proper DataList components for roles and public IDs - Improve Events table layout and styling - Wrap all copyable values in Code components for better visual distinction Type Safety Improvements: - Add rdapStatusColors mapping with proper Radix UI badge color types - Update IpNetwork and AutonomousNumber schemas to use typed StatusEnum arrays
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import type { FunctionComponent, ReactNode } from "react";
|
import type { FunctionComponent, ReactNode } from "react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useBoolean } from "usehooks-ts";
|
import { useBoolean } from "usehooks-ts";
|
||||||
import { Link2Icon, CodeIcon, DownloadIcon, ClipboardCopyIcon } from "@radix-ui/react-icons";
|
import { Link2Icon, CodeIcon, DownloadIcon, ClipboardIcon } from "@radix-ui/react-icons";
|
||||||
import { Card, Flex, Box, IconButton, Code, ScrollArea } from "@radix-ui/themes";
|
import { Card, Flex, Box, IconButton, Code, ScrollArea } from "@radix-ui/themes";
|
||||||
|
|
||||||
type AbstractCardProps = {
|
type AbstractCardProps = {
|
||||||
@@ -75,7 +75,7 @@ const AbstractCard: FunctionComponent<AbstractCardProps> = ({
|
|||||||
}}
|
}}
|
||||||
aria-label="Copy JSON to clipboard"
|
aria-label="Copy JSON to clipboard"
|
||||||
>
|
>
|
||||||
<ClipboardCopyIcon width="18" height="18" />
|
<ClipboardIcon width="18" height="18" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|||||||
40
src/components/CopyButton.tsx
Normal file
40
src/components/CopyButton.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import type { FunctionComponent } from "react";
|
||||||
|
import React from "react";
|
||||||
|
import { ClipboardIcon } from "@radix-ui/react-icons";
|
||||||
|
import { IconButton } from "@radix-ui/themes";
|
||||||
|
|
||||||
|
export type CopyButtonProps = {
|
||||||
|
value: string;
|
||||||
|
size?: "1" | "2" | "3";
|
||||||
|
};
|
||||||
|
|
||||||
|
const CopyButton: FunctionComponent<CopyButtonProps> = ({ value, size = "1" }) => {
|
||||||
|
const handleCopy = () => {
|
||||||
|
navigator.clipboard.writeText(value).then(
|
||||||
|
() => {
|
||||||
|
// Successfully copied to clipboard
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
if (err instanceof Error) {
|
||||||
|
console.error(`Failed to copy to clipboard (${err.toString()}).`);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to copy to clipboard.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IconButton
|
||||||
|
size={size}
|
||||||
|
aria-label="Copy value"
|
||||||
|
color="gray"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={handleCopy}
|
||||||
|
>
|
||||||
|
<ClipboardIcon />
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CopyButton;
|
||||||
36
src/components/StatusBadge.tsx
Normal file
36
src/components/StatusBadge.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import type { FunctionComponent } from "react";
|
||||||
|
import React from "react";
|
||||||
|
import type { RdapStatusType } from "@/rdap/schemas";
|
||||||
|
import { rdapStatusColors, rdapStatusInfo } from "@/rdap/constants";
|
||||||
|
import { QuestionMarkIcon } from "@radix-ui/react-icons";
|
||||||
|
import { Badge, HoverCard, Text, Flex } from "@radix-ui/themes";
|
||||||
|
|
||||||
|
export type StatusBadgeProps = {
|
||||||
|
status: RdapStatusType;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StatusBadge: FunctionComponent<StatusBadgeProps> = ({ status }) => {
|
||||||
|
return (
|
||||||
|
<HoverCard.Root>
|
||||||
|
<HoverCard.Trigger>
|
||||||
|
<Badge
|
||||||
|
color={rdapStatusColors[status]}
|
||||||
|
variant="soft"
|
||||||
|
radius="full"
|
||||||
|
size="2"
|
||||||
|
style={{ cursor: "help" }}
|
||||||
|
>
|
||||||
|
<Flex align="center" gap="1">
|
||||||
|
{status}
|
||||||
|
<QuestionMarkIcon width="12" height="12" />
|
||||||
|
</Flex>
|
||||||
|
</Badge>
|
||||||
|
</HoverCard.Trigger>
|
||||||
|
<HoverCard.Content maxWidth="400px">
|
||||||
|
<Text size="2">{rdapStatusInfo[status]}</Text>
|
||||||
|
</HoverCard.Content>
|
||||||
|
</HoverCard.Root>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StatusBadge;
|
||||||
@@ -3,9 +3,10 @@ import React from "react";
|
|||||||
import type { AutonomousNumber } from "@/rdap/schemas";
|
import type { AutonomousNumber } from "@/rdap/schemas";
|
||||||
import Events from "@/rdap/components/Events";
|
import Events from "@/rdap/components/Events";
|
||||||
import Property from "@/components/Property";
|
import Property from "@/components/Property";
|
||||||
import PropertyList from "@/components/PropertyList";
|
import CopyButton from "@/components/CopyButton";
|
||||||
|
import StatusBadge from "@/components/StatusBadge";
|
||||||
import AbstractCard from "@/components/AbstractCard";
|
import AbstractCard from "@/components/AbstractCard";
|
||||||
import { Flex, Text, DataList, Badge } from "@radix-ui/themes";
|
import { Flex, Text, DataList, Badge, Code } from "@radix-ui/themes";
|
||||||
|
|
||||||
export type AutnumCardProps = {
|
export type AutnumCardProps = {
|
||||||
data: AutonomousNumber;
|
data: AutonomousNumber;
|
||||||
@@ -31,24 +32,39 @@ const AutnumCard: FunctionComponent<AutnumCardProps> = ({ data, url }: AutnumCar
|
|||||||
>
|
>
|
||||||
<DataList.Root orientation={{ initial: "vertical", sm: "horizontal" }} size="2">
|
<DataList.Root orientation={{ initial: "vertical", sm: "horizontal" }} size="2">
|
||||||
<Property title="Name">{data.name}</Property>
|
<Property title="Name">{data.name}</Property>
|
||||||
<Property title="Handle">{data.handle}</Property>
|
<DataList.Item>
|
||||||
<Property title="ASN Range">
|
<DataList.Label>Handle</DataList.Label>
|
||||||
{data.startAutnum === data.endAutnum
|
<DataList.Value>
|
||||||
? `AS${data.startAutnum}`
|
<Flex align="center" gap="2">
|
||||||
: `AS${data.startAutnum} - AS${data.endAutnum}`}
|
<Code variant="ghost">{data.handle}</Code>
|
||||||
</Property>
|
<CopyButton value={data.handle} />
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
|
<DataList.Item>
|
||||||
|
<DataList.Label>ASN Range</DataList.Label>
|
||||||
|
<DataList.Value>
|
||||||
|
<Flex align="center" gap="2">
|
||||||
|
<Code variant="ghost">{asnRange}</Code>
|
||||||
|
<CopyButton value={asnRange} />
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
<Property title="Type">{data.type}</Property>
|
<Property title="Type">{data.type}</Property>
|
||||||
<Property title="Country">{data.country.toUpperCase()}</Property>
|
<Property title="Country">{data.country.toUpperCase()}</Property>
|
||||||
<Property title="Events">
|
<Property title="Events">
|
||||||
<Events key={0} data={data.events} />
|
<Events key={0} data={data.events} />
|
||||||
</Property>
|
</Property>
|
||||||
<PropertyList title="Status">
|
<DataList.Item align="center">
|
||||||
{data.status.map((status, index) => (
|
<DataList.Label>Status</DataList.Label>
|
||||||
<PropertyList.Item key={index} title={status}>
|
<DataList.Value>
|
||||||
{status}
|
<Flex gap="2" wrap="wrap">
|
||||||
</PropertyList.Item>
|
{data.status.map((status, index) => (
|
||||||
))}
|
<StatusBadge key={index} status={status} />
|
||||||
</PropertyList>
|
))}
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
</DataList.Root>
|
</DataList.Root>
|
||||||
</AbstractCard>
|
</AbstractCard>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import type { FunctionComponent } from "react";
|
import type { FunctionComponent } from "react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { rdapStatusInfo } from "@/rdap/constants";
|
|
||||||
import type { Domain } from "@/rdap/schemas";
|
import type { Domain } from "@/rdap/schemas";
|
||||||
import Events from "@/rdap/components/Events";
|
import Events from "@/rdap/components/Events";
|
||||||
import Property from "@/components/Property";
|
import Property from "@/components/Property";
|
||||||
import PropertyList from "@/components/PropertyList";
|
import CopyButton from "@/components/CopyButton";
|
||||||
|
import StatusBadge from "@/components/StatusBadge";
|
||||||
import AbstractCard from "@/components/AbstractCard";
|
import AbstractCard from "@/components/AbstractCard";
|
||||||
import { Flex, Text, DataList, Badge } from "@radix-ui/themes";
|
import { Flex, Text, DataList, Badge, Code } from "@radix-ui/themes";
|
||||||
|
|
||||||
export type DomainProps = {
|
export type DomainProps = {
|
||||||
data: Domain;
|
data: Domain;
|
||||||
@@ -27,22 +27,49 @@ const DomainCard: FunctionComponent<DomainProps> = ({ data, url }: DomainProps)
|
|||||||
>
|
>
|
||||||
<DataList.Root orientation={{ initial: "vertical", sm: "horizontal" }} size="2">
|
<DataList.Root orientation={{ initial: "vertical", sm: "horizontal" }} size="2">
|
||||||
{data.unicodeName != undefined ? (
|
{data.unicodeName != undefined ? (
|
||||||
<Property title="Unicode Name">{data.unicodeName}</Property>
|
<DataList.Item>
|
||||||
|
<DataList.Label>Unicode Name</DataList.Label>
|
||||||
|
<DataList.Value>
|
||||||
|
<Flex align="center" gap="2">
|
||||||
|
<Code variant="ghost">{data.unicodeName}</Code>
|
||||||
|
<CopyButton value={data.unicodeName} />
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
) : null}
|
) : null}
|
||||||
<Property title={data.unicodeName != undefined ? "LHD Name" : "Name"}>
|
<DataList.Item>
|
||||||
{data.ldhName}
|
<DataList.Label>
|
||||||
</Property>
|
{data.unicodeName != undefined ? "LDH Name" : "Name"}
|
||||||
<Property title="Handle">{data.handle}</Property>
|
</DataList.Label>
|
||||||
|
<DataList.Value>
|
||||||
|
<Flex align="center" gap="2">
|
||||||
|
<Code variant="ghost">{data.ldhName}</Code>
|
||||||
|
<CopyButton value={data.ldhName} />
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
|
<DataList.Item>
|
||||||
|
<DataList.Label>Handle</DataList.Label>
|
||||||
|
<DataList.Value>
|
||||||
|
<Flex align="center" gap="2">
|
||||||
|
<Code variant="ghost">{data.handle}</Code>
|
||||||
|
<CopyButton value={data.handle} />
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
<Property title="Events">
|
<Property title="Events">
|
||||||
<Events key={0} data={data.events} />
|
<Events key={0} data={data.events} />
|
||||||
</Property>
|
</Property>
|
||||||
<PropertyList title="Status">
|
<DataList.Item align="center">
|
||||||
{data.status.map((statusKey, index) => (
|
<DataList.Label>Status</DataList.Label>
|
||||||
<PropertyList.Item key={index} title={rdapStatusInfo[statusKey]}>
|
<DataList.Value>
|
||||||
{statusKey}
|
<Flex gap="2" wrap="wrap">
|
||||||
</PropertyList.Item>
|
{data.status.map((statusKey, index) => (
|
||||||
))}
|
<StatusBadge key={index} status={statusKey} />
|
||||||
</PropertyList>
|
))}
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
</DataList.Root>
|
</DataList.Root>
|
||||||
</AbstractCard>
|
</AbstractCard>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import type { FunctionComponent } from "react";
|
import type { FunctionComponent } from "react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { Entity } from "@/rdap/schemas";
|
import type { Entity } from "@/rdap/schemas";
|
||||||
import Property from "@/components/Property";
|
import CopyButton from "@/components/CopyButton";
|
||||||
import PropertyList from "@/components/PropertyList";
|
|
||||||
import AbstractCard from "@/components/AbstractCard";
|
import AbstractCard from "@/components/AbstractCard";
|
||||||
import { Flex, DataList, Badge, Text } from "@radix-ui/themes";
|
import { Flex, DataList, Badge, Text, Code } from "@radix-ui/themes";
|
||||||
|
|
||||||
export type EntityCardProps = {
|
export type EntityCardProps = {
|
||||||
data: Entity;
|
data: Entity;
|
||||||
@@ -24,22 +23,45 @@ const EntityCard: FunctionComponent<EntityCardProps> = ({ data, url }: EntityCar
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<DataList.Root orientation={{ initial: "vertical", sm: "horizontal" }} size="2">
|
<DataList.Root orientation={{ initial: "vertical", sm: "horizontal" }} size="2">
|
||||||
{data.handle && <Property title="Handle">{data.handle}</Property>}
|
{data.handle && (
|
||||||
<PropertyList title="Roles">
|
<DataList.Item>
|
||||||
{data.roles.map((role, index) => (
|
<DataList.Label>Handle</DataList.Label>
|
||||||
<PropertyList.Item key={index} title={role}>
|
<DataList.Value>
|
||||||
{role}
|
<Flex align="center" gap="2">
|
||||||
</PropertyList.Item>
|
<Code variant="ghost">{data.handle}</Code>
|
||||||
))}
|
<CopyButton value={data.handle} />
|
||||||
</PropertyList>
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
|
)}
|
||||||
|
<DataList.Item align="center">
|
||||||
|
<DataList.Label>Roles</DataList.Label>
|
||||||
|
<DataList.Value>
|
||||||
|
<Flex gap="2" wrap="wrap">
|
||||||
|
{data.roles.map((role, index) => (
|
||||||
|
<Badge key={index} color="gray" variant="soft" radius="full">
|
||||||
|
{role}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
{data.publicIds && data.publicIds.length > 0 && (
|
{data.publicIds && data.publicIds.length > 0 && (
|
||||||
<PropertyList title="Public IDs">
|
<DataList.Item align="center">
|
||||||
{data.publicIds.map((publicId, index) => (
|
<DataList.Label>Public IDs</DataList.Label>
|
||||||
<PropertyList.Item key={index} title={publicId.type}>
|
<DataList.Value>
|
||||||
{`${publicId.identifier} (${publicId.type})`}
|
<Flex direction="column" gap="2">
|
||||||
</PropertyList.Item>
|
{data.publicIds.map((publicId, index) => (
|
||||||
))}
|
<Flex key={index} align="center" gap="2">
|
||||||
</PropertyList>
|
<Code variant="ghost">
|
||||||
|
{publicId.identifier} ({publicId.type})
|
||||||
|
</Code>
|
||||||
|
<CopyButton value={publicId.identifier} />
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
)}
|
)}
|
||||||
</DataList.Root>
|
</DataList.Root>
|
||||||
</AbstractCard>
|
</AbstractCard>
|
||||||
|
|||||||
@@ -9,29 +9,33 @@ export type EventsProps = {
|
|||||||
|
|
||||||
const Events: FunctionComponent<EventsProps> = ({ data }) => {
|
const Events: FunctionComponent<EventsProps> = ({ data }) => {
|
||||||
return (
|
return (
|
||||||
<Table.Root size="1" variant="surface">
|
<Table.Root size="1" variant="surface" layout="auto">
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.ColumnHeaderCell>Event</Table.ColumnHeaderCell>
|
<Table.ColumnHeaderCell>Event</Table.ColumnHeaderCell>
|
||||||
<Table.ColumnHeaderCell>Date</Table.ColumnHeaderCell>
|
<Table.ColumnHeaderCell>Date</Table.ColumnHeaderCell>
|
||||||
<Table.ColumnHeaderCell>Actor</Table.ColumnHeaderCell>
|
<Table.ColumnHeaderCell align="right">Actor</Table.ColumnHeaderCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
{data.map(({ eventAction, eventDate, eventActor }, index) => (
|
{data.map(({ eventAction, eventDate, eventActor }, index) => (
|
||||||
<Table.Row key={index}>
|
<Table.Row key={index}>
|
||||||
<Table.Cell>
|
<Table.Cell pr="4">
|
||||||
<Text size="2" weight="medium">
|
<Text size="2">{eventAction}</Text>
|
||||||
{eventAction}
|
|
||||||
</Text>
|
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell pr="4">
|
||||||
<DynamicDate value={new Date(eventDate)} />
|
<DynamicDate value={new Date(eventDate)} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell align="right">
|
||||||
<Text size="2" color="gray">
|
{eventActor ? (
|
||||||
{eventActor ?? "—"}
|
<Text size="2" color="gray">
|
||||||
</Text>
|
{eventActor}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<Text size="2" style={{ color: "var(--gray-a6)" }}>
|
||||||
|
—
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ import React from "react";
|
|||||||
import type { IpNetwork } from "@/rdap/schemas";
|
import type { IpNetwork } from "@/rdap/schemas";
|
||||||
import Events from "@/rdap/components/Events";
|
import Events from "@/rdap/components/Events";
|
||||||
import Property from "@/components/Property";
|
import Property from "@/components/Property";
|
||||||
import PropertyList from "@/components/PropertyList";
|
import CopyButton from "@/components/CopyButton";
|
||||||
|
import StatusBadge from "@/components/StatusBadge";
|
||||||
import AbstractCard from "@/components/AbstractCard";
|
import AbstractCard from "@/components/AbstractCard";
|
||||||
import { Flex, Text, DataList, Badge } from "@radix-ui/themes";
|
import { Flex, Text, DataList, Badge, Code } from "@radix-ui/themes";
|
||||||
|
|
||||||
export type IPCardProps = {
|
export type IPCardProps = {
|
||||||
data: IpNetwork;
|
data: IpNetwork;
|
||||||
@@ -28,25 +29,60 @@ const IPCard: FunctionComponent<IPCardProps> = ({ data, url }: IPCardProps) => {
|
|||||||
>
|
>
|
||||||
<DataList.Root orientation={{ initial: "vertical", sm: "horizontal" }} size="2">
|
<DataList.Root orientation={{ initial: "vertical", sm: "horizontal" }} size="2">
|
||||||
<Property title="Name">{data.name}</Property>
|
<Property title="Name">{data.name}</Property>
|
||||||
<Property title="Handle">{data.handle}</Property>
|
<DataList.Item>
|
||||||
|
<DataList.Label>Handle</DataList.Label>
|
||||||
|
<DataList.Value>
|
||||||
|
<Flex align="center" gap="2">
|
||||||
|
<Code variant="ghost">{data.handle}</Code>
|
||||||
|
<CopyButton value={data.handle} />
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
<Property title="IP Version">{data.ipVersion.toUpperCase()}</Property>
|
<Property title="IP Version">{data.ipVersion.toUpperCase()}</Property>
|
||||||
<Property title="Start Address">{data.startAddress}</Property>
|
<DataList.Item>
|
||||||
<Property title="End Address">{data.endAddress}</Property>
|
<DataList.Label>Start Address</DataList.Label>
|
||||||
|
<DataList.Value>
|
||||||
|
<Flex align="center" gap="2">
|
||||||
|
<Code variant="ghost">{data.startAddress}</Code>
|
||||||
|
<CopyButton value={data.startAddress} />
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
|
<DataList.Item>
|
||||||
|
<DataList.Label>End Address</DataList.Label>
|
||||||
|
<DataList.Value>
|
||||||
|
<Flex align="center" gap="2">
|
||||||
|
<Code variant="ghost">{data.endAddress}</Code>
|
||||||
|
<CopyButton value={data.endAddress} />
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
<Property title="Type">{data.type}</Property>
|
<Property title="Type">{data.type}</Property>
|
||||||
{data.country && <Property title="Country">{data.country}</Property>}
|
{data.country && <Property title="Country">{data.country}</Property>}
|
||||||
{data.parentHandle && (
|
{data.parentHandle && (
|
||||||
<Property title="Parent Handle">{data.parentHandle}</Property>
|
<DataList.Item>
|
||||||
|
<DataList.Label>Parent Handle</DataList.Label>
|
||||||
|
<DataList.Value>
|
||||||
|
<Flex align="center" gap="2">
|
||||||
|
<Code variant="ghost">{data.parentHandle}</Code>
|
||||||
|
<CopyButton value={data.parentHandle} />
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
)}
|
)}
|
||||||
<Property title="Events">
|
<Property title="Events">
|
||||||
<Events key={0} data={data.events} />
|
<Events key={0} data={data.events} />
|
||||||
</Property>
|
</Property>
|
||||||
<PropertyList title="Status">
|
<DataList.Item align="center">
|
||||||
{data.status.map((status, index) => (
|
<DataList.Label>Status</DataList.Label>
|
||||||
<PropertyList.Item key={index} title={status}>
|
<DataList.Value>
|
||||||
{status}
|
<Flex gap="2" wrap="wrap">
|
||||||
</PropertyList.Item>
|
{data.status.map((status, index) => (
|
||||||
))}
|
<StatusBadge key={index} status={status} />
|
||||||
</PropertyList>
|
))}
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
</DataList.Root>
|
</DataList.Root>
|
||||||
</AbstractCard>
|
</AbstractCard>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { FunctionComponent } from "react";
|
import type { FunctionComponent } from "react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { Nameserver } from "@/rdap/schemas";
|
import type { Nameserver } from "@/rdap/schemas";
|
||||||
import Property from "@/components/Property";
|
import CopyButton from "@/components/CopyButton";
|
||||||
import AbstractCard from "@/components/AbstractCard";
|
import AbstractCard from "@/components/AbstractCard";
|
||||||
import { Flex, DataList, Badge, Text } from "@radix-ui/themes";
|
import { Flex, DataList, Badge, Text, Code } from "@radix-ui/themes";
|
||||||
|
|
||||||
export type NameserverCardProps = {
|
export type NameserverCardProps = {
|
||||||
data: Nameserver;
|
data: Nameserver;
|
||||||
@@ -26,7 +26,15 @@ const NameserverCard: FunctionComponent<NameserverCardProps> = ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<DataList.Root orientation={{ initial: "vertical", sm: "horizontal" }} size="2">
|
<DataList.Root orientation={{ initial: "vertical", sm: "horizontal" }} size="2">
|
||||||
<Property title="LDH Name">{data.ldhName}</Property>
|
<DataList.Item>
|
||||||
|
<DataList.Label>LDH Name</DataList.Label>
|
||||||
|
<DataList.Value>
|
||||||
|
<Flex align="center" gap="2">
|
||||||
|
<Code variant="ghost">{data.ldhName}</Code>
|
||||||
|
<CopyButton value={data.ldhName} />
|
||||||
|
</Flex>
|
||||||
|
</DataList.Value>
|
||||||
|
</DataList.Item>
|
||||||
</DataList.Root>
|
</DataList.Root>
|
||||||
</AbstractCard>
|
</AbstractCard>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,45 @@
|
|||||||
// see https://www.iana.org/assignments/rdap-json-values
|
// see https://www.iana.org/assignments/rdap-json-values
|
||||||
import type { RdapStatusType, RootRegistryType, SimplifiedTargetType } from "@/rdap/schemas";
|
import type { RdapStatusType, RootRegistryType, SimplifiedTargetType } from "@/rdap/schemas";
|
||||||
|
import type { badgePropDefs } from "@radix-ui/themes/src/components/badge.props";
|
||||||
|
|
||||||
|
type BadgeColor = (typeof badgePropDefs)["color"]["values"][number];
|
||||||
|
|
||||||
|
export const rdapStatusColors: Record<RdapStatusType, BadgeColor> = {
|
||||||
|
active: "jade",
|
||||||
|
inactive: "gray",
|
||||||
|
validated: "blue",
|
||||||
|
locked: "red",
|
||||||
|
"renew prohibited": "red",
|
||||||
|
"update prohibited": "red",
|
||||||
|
"transfer prohibited": "red",
|
||||||
|
"delete prohibited": "red",
|
||||||
|
"client delete prohibited": "red",
|
||||||
|
"client renew prohibited": "red",
|
||||||
|
"client transfer prohibited": "red",
|
||||||
|
"client update prohibited": "red",
|
||||||
|
"server delete prohibited": "red",
|
||||||
|
"server renew prohibited": "red",
|
||||||
|
"server transfer prohibited": "red",
|
||||||
|
"server update prohibited": "red",
|
||||||
|
"client hold": "orange",
|
||||||
|
"server hold": "orange",
|
||||||
|
"pending create": "amber",
|
||||||
|
"pending renew": "amber",
|
||||||
|
"pending transfer": "amber",
|
||||||
|
"pending update": "amber",
|
||||||
|
"pending delete": "amber",
|
||||||
|
"pending restore": "amber",
|
||||||
|
proxy: "violet",
|
||||||
|
private: "violet",
|
||||||
|
removed: "violet",
|
||||||
|
obscured: "violet",
|
||||||
|
associated: "blue",
|
||||||
|
"add period": "blue",
|
||||||
|
"auto renew period": "blue",
|
||||||
|
"redemption period": "orange",
|
||||||
|
"renew period": "blue",
|
||||||
|
"transfer period": "blue",
|
||||||
|
};
|
||||||
|
|
||||||
export const rdapStatusInfo: Record<RdapStatusType, string> = {
|
export const rdapStatusInfo: Record<RdapStatusType, string> = {
|
||||||
validated:
|
validated:
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export const IpNetworkSchema = z.object({
|
|||||||
type: z.string(),
|
type: z.string(),
|
||||||
country: z.string().optional(),
|
country: z.string().optional(),
|
||||||
parentHandle: z.string().optional(),
|
parentHandle: z.string().optional(),
|
||||||
status: z.string().array(),
|
status: z.array(StatusEnum),
|
||||||
entities: z.array(EntitySchema).optional(),
|
entities: z.array(EntitySchema).optional(),
|
||||||
remarks: z.any().optional(),
|
remarks: z.any().optional(),
|
||||||
links: z.any().optional(),
|
links: z.any().optional(),
|
||||||
@@ -125,7 +125,7 @@ export const AutonomousNumberSchema = z.object({
|
|||||||
endAutnum: z.number().positive(), // TODO: 32bit
|
endAutnum: z.number().positive(), // TODO: 32bit
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
status: z.array(z.string()),
|
status: z.array(StatusEnum),
|
||||||
country: z.string().length(2),
|
country: z.string().length(2),
|
||||||
events: z.array(EventSchema),
|
events: z.array(EventSchema),
|
||||||
entities: z.array(EntitySchema),
|
entities: z.array(EntitySchema),
|
||||||
|
|||||||
Reference in New Issue
Block a user