refactor: introduce unified contact parsing and display system

Extracts contact information parsing logic into reusable parsers for
both JSContact and vCard formats. Introduces ContactTable component
for consistent contact rendering across both formats, reducing code
duplication and improving maintainability.

- Add contact-parser.ts with parseJSContact() and parseVCard()
- Add ContactTable component for unified contact display
- Refactor JSContactDisplay and VCardDisplay to use new parsers
- Add comprehensive test suite for contact parsers (488 test cases)
- Move WHOIS Server field to DataList in EntitiesSection
- Fix DynamicDate tooltip to avoid duplicate time display
- Standardize import paths to use @ alias
- Update tooltip text for RDAP URL button clarity
This commit is contained in:
2025-10-23 15:41:39 -05:00
parent 2c67f49e2f
commit 31706a1623
11 changed files with 982 additions and 567 deletions

View File

@@ -14,7 +14,7 @@ type AbstractCardProps = {
footer?: ReactNode;
/** RDAP response data for download/display. When provided, enables JSON actions. */
data?: ParsedGeneric | object;
/** RDAP query URL. When provided, enables "open in new tab" button. */
/** RDAP query URL. When provided, enables "open RDAP URL" button. */
url?: string;
/** Query execution timestamp for filename generation */
queryTimestamp?: Date;
@@ -85,7 +85,7 @@ const AbstractCard: FunctionComponent<AbstractCardProps> = ({
</Flex>
<Flex gap="2" align="center">
{url != null && (
<Tooltip content="Open in new tab">
<Tooltip content="Open RDAP URL">
<IconButton variant="ghost" size="2" asChild>
<a
href={url}

View File

@@ -40,8 +40,11 @@ const DynamicDate: FunctionComponent<DynamicDateProps> = ({
: format(date, absoluteFormatString);
const isoString = date.toISOString();
// Relative time element (disable time since it's already shown in the tooltip)
const relative = <TimeAgo title="" date={date} />;
// Get display value based on global format
const displayValue = dateFormat === "relative" ? <TimeAgo date={date} /> : absoluteWithTz;
const displayValue = dateFormat === "relative" ? relative : absoluteWithTz;
return (
<Tooltip
@@ -49,7 +52,7 @@ const DynamicDate: FunctionComponent<DynamicDateProps> = ({
<Box style={{ minWidth: "280px" }} as="span">
<Flex align="center" justify="between" mb="2" as="span">
<Text size="1">
<strong>Relative:</strong> <TimeAgo date={date} />
<strong>Relative:</strong> {relative}
</Text>
</Flex>
<Flex align="center" justify="between" mb="2" as="span">

View File

@@ -1,6 +1,6 @@
import type { FunctionComponent } from "react";
import { Link2Icon } from "@radix-ui/react-icons";
import CopyButton, { type CopyButtonProps } from "./CopyButton";
import CopyButton, { type CopyButtonProps } from "@/components/CopyButton";
export type ShareButtonProps = Omit<CopyButtonProps, "value"> & {
/**