mirror of
https://github.com/Xevion/rdap.git
synced 2025-12-07 01:16:08 -06:00
ci: implement comprehensive CI/CD and workflow automation
- Add GitHub Actions workflows for CI, release, and deployment - Configure Renovate for automated dependency updates - Set up Husky pre-commit hooks with lint-staged - Add commitlint for enforcing Conventional Commits - Configure semantic-release for automated versioning - Add Prettier configuration for consistent formatting - Reformat codebase with new formatting rules
This commit is contained in:
@@ -2,104 +2,107 @@ import type { FunctionComponent, ReactNode } from "react";
|
||||
import React from "react";
|
||||
import { useBoolean } from "usehooks-ts";
|
||||
import {
|
||||
LinkIcon,
|
||||
CodeBracketIcon,
|
||||
DocumentArrowDownIcon,
|
||||
ClipboardDocumentIcon,
|
||||
LinkIcon,
|
||||
CodeBracketIcon,
|
||||
DocumentArrowDownIcon,
|
||||
ClipboardDocumentIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
|
||||
type AbstractCardProps = {
|
||||
children?: ReactNode;
|
||||
header?: ReactNode;
|
||||
footer?: ReactNode;
|
||||
data?: object;
|
||||
url?: string;
|
||||
children?: ReactNode;
|
||||
header?: ReactNode;
|
||||
footer?: ReactNode;
|
||||
data?: object;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
const AbstractCard: FunctionComponent<AbstractCardProps> = ({
|
||||
url,
|
||||
children,
|
||||
header,
|
||||
footer,
|
||||
data,
|
||||
url,
|
||||
children,
|
||||
header,
|
||||
footer,
|
||||
data,
|
||||
}) => {
|
||||
const { value: showRaw, toggle: toggleRaw } = useBoolean(false);
|
||||
const { value: showRaw, toggle: toggleRaw } = useBoolean(false);
|
||||
|
||||
return (
|
||||
<div className="mb-4 overflow-clip rounded bg-zinc-800 shadow">
|
||||
{header != undefined || data != undefined ? (
|
||||
<div className="flex bg-zinc-700 p-2 pl-3 md:pl-5">
|
||||
<div className="flex grow gap-2">{header}</div>
|
||||
{url != undefined ? (
|
||||
<div className="pr-2">
|
||||
<a href={url} target="_blank" rel="noreferrer">
|
||||
<LinkIcon className="mt-1 h-5 w-5 cursor-pointer" />
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
{data != undefined ? (
|
||||
<>
|
||||
<div className="pr-2">
|
||||
<ClipboardDocumentIcon
|
||||
onClick={() => {
|
||||
// stringify the JSON object, then begin the async clipboard write
|
||||
navigator.clipboard
|
||||
.writeText(JSON.stringify(data, null, 4))
|
||||
.then(
|
||||
() => {
|
||||
console.log("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.");
|
||||
}
|
||||
);
|
||||
}}
|
||||
className="h-6 w-6 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
<div className="pr-2">
|
||||
<DocumentArrowDownIcon
|
||||
onClick={() => {
|
||||
const file = new Blob([JSON.stringify(data, null, 4)], {
|
||||
type: "application/json",
|
||||
});
|
||||
return (
|
||||
<div className="mb-4 overflow-clip rounded bg-zinc-800 shadow">
|
||||
{header != undefined || data != undefined ? (
|
||||
<div className="flex bg-zinc-700 p-2 pl-3 md:pl-5">
|
||||
<div className="flex grow gap-2">{header}</div>
|
||||
{url != undefined ? (
|
||||
<div className="pr-2">
|
||||
<a href={url} target="_blank" rel="noreferrer">
|
||||
<LinkIcon className="mt-1 h-5 w-5 cursor-pointer" />
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
{data != undefined ? (
|
||||
<>
|
||||
<div className="pr-2">
|
||||
<ClipboardDocumentIcon
|
||||
onClick={() => {
|
||||
// stringify the JSON object, then begin the async clipboard write
|
||||
navigator.clipboard
|
||||
.writeText(JSON.stringify(data, null, 4))
|
||||
.then(
|
||||
() => {
|
||||
console.log("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."
|
||||
);
|
||||
}
|
||||
);
|
||||
}}
|
||||
className="h-6 w-6 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
<div className="pr-2">
|
||||
<DocumentArrowDownIcon
|
||||
onClick={() => {
|
||||
const file = new Blob([JSON.stringify(data, null, 4)], {
|
||||
type: "application/json",
|
||||
});
|
||||
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = URL.createObjectURL(file);
|
||||
anchor.download = "response.json";
|
||||
anchor.click();
|
||||
}}
|
||||
className="h-6 w-6 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
<div className="pr-1">
|
||||
<CodeBracketIcon
|
||||
onClick={toggleRaw}
|
||||
className="h-6 w-6 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="max-w-full overflow-x-auto p-2 px-4">
|
||||
{showRaw ? (
|
||||
<pre className="scrollbar-thin m-2 max-h-[40rem] max-w-full overflow-y-auto whitespace-pre-wrap rounded">
|
||||
{JSON.stringify(data, null, 4)}
|
||||
</pre>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</div>
|
||||
{footer != null ? (
|
||||
<div className="flex gap-2 bg-zinc-700 p-2 pl-5">{footer}</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = URL.createObjectURL(file);
|
||||
anchor.download = "response.json";
|
||||
anchor.click();
|
||||
}}
|
||||
className="h-6 w-6 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
<div className="pr-1">
|
||||
<CodeBracketIcon
|
||||
onClick={toggleRaw}
|
||||
className="h-6 w-6 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="max-w-full overflow-x-auto p-2 px-4">
|
||||
{showRaw ? (
|
||||
<pre className="scrollbar-thin m-2 max-h-[40rem] max-w-full overflow-y-auto rounded whitespace-pre-wrap">
|
||||
{JSON.stringify(data, null, 4)}
|
||||
</pre>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</div>
|
||||
{footer != null ? (
|
||||
<div className="flex gap-2 bg-zinc-700 p-2 pl-5">{footer}</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AbstractCard;
|
||||
|
||||
@@ -4,8 +4,8 @@ import { format } from "date-fns";
|
||||
import TimeAgo from "react-timeago";
|
||||
|
||||
type DynamicDateProps = {
|
||||
value: Date | number;
|
||||
absoluteFormat?: string;
|
||||
value: Date | number;
|
||||
absoluteFormat?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -13,24 +13,21 @@ type DynamicDateProps = {
|
||||
* @param value The date to be displayed, the Date value, or
|
||||
* @param absoluteFormat Optional - the date-fns format string to use for the absolute date rendering.
|
||||
*/
|
||||
const DynamicDate: FunctionComponent<DynamicDateProps> = ({
|
||||
value,
|
||||
absoluteFormat,
|
||||
}) => {
|
||||
const { value: showAbsolute, toggle: toggleFormat } = useBoolean(true);
|
||||
const DynamicDate: FunctionComponent<DynamicDateProps> = ({ value, absoluteFormat }) => {
|
||||
const { value: showAbsolute, toggle: toggleFormat } = useBoolean(true);
|
||||
|
||||
const date = new Date(value);
|
||||
return (
|
||||
<button onClick={toggleFormat}>
|
||||
<span className="dashed" title={date.toISOString()}>
|
||||
{showAbsolute ? (
|
||||
format(date, absoluteFormat ?? "LLL do, y HH:mm:ss xxx")
|
||||
) : (
|
||||
<TimeAgo date={date} />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
const date = new Date(value);
|
||||
return (
|
||||
<button onClick={toggleFormat}>
|
||||
<span className="dashed" title={date.toISOString()}>
|
||||
{showAbsolute ? (
|
||||
format(date, absoluteFormat ?? "LLL do, y HH:mm:ss xxx")
|
||||
) : (
|
||||
<TimeAgo date={date} />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default DynamicDate;
|
||||
|
||||
@@ -3,49 +3,49 @@ import { XCircleIcon } from "@heroicons/react/20/solid";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export type ErrorCardProps = {
|
||||
title: ReactNode;
|
||||
description?: ReactNode;
|
||||
issues?: ReactNode[];
|
||||
className?: string;
|
||||
title: ReactNode;
|
||||
description?: ReactNode;
|
||||
issues?: ReactNode[];
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const ErrorCard: FunctionComponent<ErrorCardProps> = ({
|
||||
title,
|
||||
description,
|
||||
issues,
|
||||
className,
|
||||
title,
|
||||
description,
|
||||
issues,
|
||||
className,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
className,
|
||||
"rounded-md border border-red-700/30 bg-zinc-800 px-3 pb-1 pt-3"
|
||||
)}
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="shrink-0">
|
||||
<XCircleIcon className="h-5 w-5 text-red-300" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="ml-3 w-full text-sm text-red-300">
|
||||
<h3 className="font-medium text-red-200">{title}</h3>
|
||||
{description != undefined ? (
|
||||
<div className="mt-2 max-h-24 w-full overflow-y-auto whitespace-pre-wrap">
|
||||
{description}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="mt-2">
|
||||
{issues != undefined ? (
|
||||
<ul role="list" className="flex list-disc flex-col gap-1 pl-5">
|
||||
{issues.map((issueText, index) => (
|
||||
<li key={index}>{issueText}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
className,
|
||||
"rounded-md border border-red-700/30 bg-zinc-800 px-3 pt-3 pb-1"
|
||||
)}
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="shrink-0">
|
||||
<XCircleIcon className="h-5 w-5 text-red-300" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="ml-3 w-full text-sm text-red-300">
|
||||
<h3 className="font-medium text-red-200">{title}</h3>
|
||||
{description != undefined ? (
|
||||
<div className="mt-2 max-h-24 w-full overflow-y-auto whitespace-pre-wrap">
|
||||
{description}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="mt-2">
|
||||
{issues != undefined ? (
|
||||
<ul role="list" className="flex list-disc flex-col gap-1 pl-5">
|
||||
{issues.map((issueText, index) => (
|
||||
<li key={index}>{issueText}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorCard;
|
||||
|
||||
@@ -3,24 +3,24 @@ import React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type PropertyProps = {
|
||||
title: string | ReactNode;
|
||||
children: string | ReactNode;
|
||||
titleClass?: string;
|
||||
valueClass?: string;
|
||||
title: string | ReactNode;
|
||||
children: string | ReactNode;
|
||||
titleClass?: string;
|
||||
valueClass?: string;
|
||||
};
|
||||
|
||||
const Property: FunctionComponent<PropertyProps> = ({
|
||||
title,
|
||||
children,
|
||||
titleClass,
|
||||
valueClass,
|
||||
title,
|
||||
children,
|
||||
titleClass,
|
||||
valueClass,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<dt className={cn("font-medium", titleClass)}>{title}:</dt>
|
||||
<dd className={cn("mb-2 ml-6 mt-2", valueClass)}>{children}</dd>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<dt className={cn("font-medium", titleClass)}>{title}:</dt>
|
||||
<dd className={cn("mt-2 mb-2 ml-6", valueClass)}>{children}</dd>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Property;
|
||||
|
||||
@@ -3,33 +3,33 @@ import React from "react";
|
||||
import Property from "@/components/common/Property";
|
||||
|
||||
const PropertyListItem: FunctionComponent<{
|
||||
title: string;
|
||||
children: string;
|
||||
title: string;
|
||||
children: string;
|
||||
}> = ({ title, children }) => {
|
||||
return (
|
||||
<li>
|
||||
<span className="dashed" title={title}>
|
||||
{children}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
return (
|
||||
<li>
|
||||
<span className="dashed" title={title}>
|
||||
{children}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
type PropertyListProps = {
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const PropertyList: FunctionComponent<PropertyListProps> & {
|
||||
Item: typeof PropertyListItem;
|
||||
Item: typeof PropertyListItem;
|
||||
} = ({ title, children }) => {
|
||||
return (
|
||||
<Property title={title}>
|
||||
<ul key={2} className="list-disc">
|
||||
{children}
|
||||
</ul>
|
||||
</Property>
|
||||
);
|
||||
return (
|
||||
<Property title={title}>
|
||||
<ul key={2} className="list-disc">
|
||||
{children}
|
||||
</ul>
|
||||
</Property>
|
||||
);
|
||||
};
|
||||
|
||||
PropertyList.Item = PropertyListItem;
|
||||
|
||||
@@ -4,18 +4,18 @@ import { Fragment, useState } from "react";
|
||||
import { onPromise, preventDefault } from "@/helpers";
|
||||
import type { SimplifiedTargetType, SubmitProps, TargetType } from "@/types";
|
||||
import {
|
||||
CheckIcon,
|
||||
ChevronUpDownIcon,
|
||||
LockClosedIcon,
|
||||
MagnifyingGlassIcon,
|
||||
ArrowPathIcon,
|
||||
CheckIcon,
|
||||
ChevronUpDownIcon,
|
||||
LockClosedIcon,
|
||||
MagnifyingGlassIcon,
|
||||
ArrowPathIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import {
|
||||
Listbox,
|
||||
ListboxButton,
|
||||
ListboxOptions,
|
||||
ListboxOption,
|
||||
Transition,
|
||||
Listbox,
|
||||
ListboxButton,
|
||||
ListboxOptions,
|
||||
ListboxOption,
|
||||
Transition,
|
||||
} from "@headlessui/react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { Maybe } from "true-myth";
|
||||
@@ -25,304 +25,289 @@ import { placeholders } from "@/constants";
|
||||
* Props for the LookupInput component.
|
||||
*/
|
||||
type LookupInputProps = {
|
||||
isLoading?: boolean;
|
||||
/**
|
||||
* Callback function called when a type of registry is detected when a user changes their input.
|
||||
* @param type - The detected type of registry.
|
||||
* @returns A promise.
|
||||
*/
|
||||
onRegistry?: (type: TargetType) => Promise<void>;
|
||||
/**
|
||||
* Callback function called when a user hits submit.
|
||||
* @param props - The submit props.
|
||||
* @returns A promise.
|
||||
*/
|
||||
onSubmit?: (props: SubmitProps) => Promise<void>;
|
||||
/**
|
||||
* Callback function called when a user changes their input (text search) or explicitly changes the type of search.
|
||||
* @param target - The target object containing the search target and target type.
|
||||
* @returns Nothing.
|
||||
*/
|
||||
onChange?: (target: {
|
||||
target: string;
|
||||
targetType: TargetType | null;
|
||||
}) => Promise<void>;
|
||||
detectedType: Maybe<TargetType>;
|
||||
isLoading?: boolean;
|
||||
/**
|
||||
* Callback function called when a type of registry is detected when a user changes their input.
|
||||
* @param type - The detected type of registry.
|
||||
* @returns A promise.
|
||||
*/
|
||||
onRegistry?: (type: TargetType) => Promise<void>;
|
||||
/**
|
||||
* Callback function called when a user hits submit.
|
||||
* @param props - The submit props.
|
||||
* @returns A promise.
|
||||
*/
|
||||
onSubmit?: (props: SubmitProps) => Promise<void>;
|
||||
/**
|
||||
* Callback function called when a user changes their input (text search) or explicitly changes the type of search.
|
||||
* @param target - The target object containing the search target and target type.
|
||||
* @returns Nothing.
|
||||
*/
|
||||
onChange?: (target: { target: string; targetType: TargetType | null }) => Promise<void>;
|
||||
detectedType: Maybe<TargetType>;
|
||||
};
|
||||
|
||||
const LookupInput: FunctionComponent<LookupInputProps> = ({
|
||||
isLoading,
|
||||
onSubmit,
|
||||
onChange,
|
||||
detectedType,
|
||||
isLoading,
|
||||
onSubmit,
|
||||
onChange,
|
||||
detectedType,
|
||||
}: LookupInputProps) => {
|
||||
const { register, handleSubmit, getValues } = useForm<SubmitProps>({
|
||||
defaultValues: {
|
||||
target: "",
|
||||
// Not used at this time.
|
||||
followReferral: false,
|
||||
requestJSContact: false,
|
||||
},
|
||||
});
|
||||
const { register, handleSubmit, getValues } = useForm<SubmitProps>({
|
||||
defaultValues: {
|
||||
target: "",
|
||||
// Not used at this time.
|
||||
followReferral: false,
|
||||
requestJSContact: false,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* A mapping of available (simple) target types to their long-form human-readable names.
|
||||
*/
|
||||
const objectNames: Record<SimplifiedTargetType | "auto", string> = {
|
||||
auto: "Autodetect",
|
||||
domain: "Domain",
|
||||
ip: "IP/CIDR", // IPv4/IPv6 are combined into this option
|
||||
tld: "TLD",
|
||||
autnum: "AS Number",
|
||||
entity: "Entity Handle",
|
||||
registrar: "Registrar",
|
||||
url: "URL",
|
||||
json: "JSON",
|
||||
};
|
||||
/**
|
||||
* A mapping of available (simple) target types to their long-form human-readable names.
|
||||
*/
|
||||
const objectNames: Record<SimplifiedTargetType | "auto", string> = {
|
||||
auto: "Autodetect",
|
||||
domain: "Domain",
|
||||
ip: "IP/CIDR", // IPv4/IPv6 are combined into this option
|
||||
tld: "TLD",
|
||||
autnum: "AS Number",
|
||||
entity: "Entity Handle",
|
||||
registrar: "Registrar",
|
||||
url: "URL",
|
||||
json: "JSON",
|
||||
};
|
||||
|
||||
/**
|
||||
* Mapping of precise target types to their simplified short-form names.
|
||||
*/
|
||||
const targetShortNames: Record<TargetType, string> = {
|
||||
domain: "Domain",
|
||||
tld: "TLD",
|
||||
ip4: "IPv4",
|
||||
ip6: "IPv6",
|
||||
autnum: "ASN",
|
||||
entity: "Entity",
|
||||
registrar: "Registrar",
|
||||
url: "URL",
|
||||
json: "JSON",
|
||||
};
|
||||
/**
|
||||
* Mapping of precise target types to their simplified short-form names.
|
||||
*/
|
||||
const targetShortNames: Record<TargetType, string> = {
|
||||
domain: "Domain",
|
||||
tld: "TLD",
|
||||
ip4: "IPv4",
|
||||
ip6: "IPv6",
|
||||
autnum: "ASN",
|
||||
entity: "Entity",
|
||||
registrar: "Registrar",
|
||||
url: "URL",
|
||||
json: "JSON",
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the selected value in the LookupInput component.
|
||||
*/
|
||||
const [selected, setSelected] = useState<SimplifiedTargetType | "auto">(
|
||||
"auto"
|
||||
);
|
||||
/**
|
||||
* Represents the selected value in the LookupInput component.
|
||||
*/
|
||||
const [selected, setSelected] = useState<SimplifiedTargetType | "auto">("auto");
|
||||
|
||||
/**
|
||||
* Retrieves the target type based on the provided value.
|
||||
* @param value - The value to retrieve the target type for.
|
||||
* @returns The target type as ObjectType or null.
|
||||
*/
|
||||
function retrieveTargetType(value?: string | null): TargetType | null {
|
||||
// If the value is null and the selected value is null, return null.
|
||||
if (value == null) value = selected;
|
||||
/**
|
||||
* Retrieves the target type based on the provided value.
|
||||
* @param value - The value to retrieve the target type for.
|
||||
* @returns The target type as ObjectType or null.
|
||||
*/
|
||||
function retrieveTargetType(value?: string | null): TargetType | null {
|
||||
// If the value is null and the selected value is null, return null.
|
||||
if (value == null) value = selected;
|
||||
|
||||
// 'auto' means 'do whatever' so we return null.
|
||||
if (value == "auto") return null;
|
||||
// 'auto' means 'do whatever' so we return null.
|
||||
if (value == "auto") return null;
|
||||
|
||||
return value as TargetType;
|
||||
}
|
||||
return value as TargetType;
|
||||
}
|
||||
|
||||
const searchIcon = (
|
||||
<>
|
||||
<button
|
||||
type="submit"
|
||||
className={cn({
|
||||
"absolute inset-y-0 left-0 flex items-center pl-3": true,
|
||||
"pointer-events-none": isLoading,
|
||||
})}
|
||||
>
|
||||
{isLoading ? (
|
||||
<ArrowPathIcon
|
||||
className="h-5 w-5 animate-spin text-zinc-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
<MagnifyingGlassIcon
|
||||
className="h-5 w-5 text-zinc-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
const searchIcon = (
|
||||
<>
|
||||
<button
|
||||
type="submit"
|
||||
className={cn({
|
||||
"absolute inset-y-0 left-0 flex items-center pl-3": true,
|
||||
"pointer-events-none": isLoading,
|
||||
})}
|
||||
>
|
||||
{isLoading ? (
|
||||
<ArrowPathIcon
|
||||
className="h-5 w-5 animate-spin text-zinc-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
<MagnifyingGlassIcon className="h-5 w-5 text-zinc-400" aria-hidden="true" />
|
||||
)}
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
|
||||
const searchInput = (
|
||||
<input
|
||||
className={cn(
|
||||
"lg:py-4.5 block w-full rounded-l-md border border-transparent",
|
||||
"bg-zinc-700 py-2 pl-10 pr-1.5 text-sm placeholder-zinc-400 placeholder:translate-y-2 focus:text-zinc-200",
|
||||
" focus:outline-hidden sm:text-sm md:py-3 md:text-base lg:text-lg"
|
||||
)}
|
||||
disabled={isLoading}
|
||||
placeholder={placeholders[selected]}
|
||||
type="search"
|
||||
{...register("target", {
|
||||
required: true,
|
||||
onChange: () => {
|
||||
if (onChange != undefined)
|
||||
void onChange({
|
||||
target: getValues("target"),
|
||||
// dropdown target will be pulled from state anyways, so no need to provide it here
|
||||
targetType: retrieveTargetType(null),
|
||||
});
|
||||
},
|
||||
})}
|
||||
/>
|
||||
);
|
||||
const searchInput = (
|
||||
<input
|
||||
className={cn(
|
||||
"block w-full rounded-l-md border border-transparent lg:py-4.5",
|
||||
"bg-zinc-700 py-2 pr-1.5 pl-10 text-sm placeholder-zinc-400 placeholder:translate-y-2 focus:text-zinc-200",
|
||||
"focus:outline-hidden sm:text-sm md:py-3 md:text-base lg:text-lg"
|
||||
)}
|
||||
disabled={isLoading}
|
||||
placeholder={placeholders[selected]}
|
||||
type="search"
|
||||
{...register("target", {
|
||||
required: true,
|
||||
onChange: () => {
|
||||
if (onChange != undefined)
|
||||
void onChange({
|
||||
target: getValues("target"),
|
||||
// dropdown target will be pulled from state anyways, so no need to provide it here
|
||||
targetType: retrieveTargetType(null),
|
||||
});
|
||||
},
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
const dropdown = (
|
||||
<Listbox
|
||||
value={selected}
|
||||
onChange={(value) => {
|
||||
setSelected(value);
|
||||
const dropdown = (
|
||||
<Listbox
|
||||
value={selected}
|
||||
onChange={(value) => {
|
||||
setSelected(value);
|
||||
|
||||
if (onChange != undefined)
|
||||
void onChange({
|
||||
target: getValues("target"),
|
||||
// we provide the value as the state will not have updated yet for this context
|
||||
targetType: retrieveTargetType(value),
|
||||
});
|
||||
}}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<div className="relative">
|
||||
<ListboxButton
|
||||
className={cn(
|
||||
"relative h-full w-full cursor-default whitespace-nowrap rounded-r-lg bg-zinc-700 py-2 pl-1 pr-10 text-right",
|
||||
"text-xs focus:outline-hidden focus-visible:border-indigo-500 sm:text-sm md:text-base lg:text-lg",
|
||||
"focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 "
|
||||
)}
|
||||
>
|
||||
{/* Fetch special text for 'auto' mode, otherwise just use the options. */}
|
||||
<span className="block">
|
||||
{selected == "auto" ? (
|
||||
// If the detected type was provided, then notate which in parentheses. Compact object naming might be better in the future.
|
||||
detectedType.isJust ? (
|
||||
<>
|
||||
Auto (
|
||||
<span className="animate-pulse">
|
||||
{targetShortNames[detectedType.value]}
|
||||
</span>
|
||||
)
|
||||
</>
|
||||
) : (
|
||||
objectNames["auto"]
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<LockClosedIcon
|
||||
className="mb-1 mr-2.5 inline h-4 w-4 animate-pulse text-zinc-500"
|
||||
aria-hidden
|
||||
/>
|
||||
{objectNames[selected]}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<ChevronUpDownIcon
|
||||
className="h-5 w-5 text-zinc-200"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<ListboxOptions
|
||||
className={cn(
|
||||
"scrollbar-thin absolute right-0 mt-1 max-h-60 min-w-full overflow-auto rounded-md bg-zinc-700 py-1",
|
||||
"text-zinc-200 shadow-lg ring-1 ring-black/5 focus:outline-hidden sm:text-sm"
|
||||
)}
|
||||
>
|
||||
{Object.entries(objectNames).map(([key, value]) => (
|
||||
<ListboxOption
|
||||
key={key}
|
||||
className={({ focus }) =>
|
||||
cn(
|
||||
"relative cursor-default select-none py-2 pl-10 pr-4",
|
||||
focus ? "bg-zinc-800 text-zinc-300" : null
|
||||
)
|
||||
}
|
||||
value={key}
|
||||
>
|
||||
{({ selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={cn(
|
||||
"block whitespace-nowrap text-right text-xs md:text-sm lg:text-base",
|
||||
selected ? "font-medium" : null
|
||||
)}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
{selected ? (
|
||||
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-blue-500">
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
) : (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
console.log("TODO: Show Help Explanation");
|
||||
}}
|
||||
className="absolute inset-y-0 left-0 flex items-center pl-4 text-lg font-bold opacity-20 hover:animate-pulse"
|
||||
>
|
||||
?
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</ListboxOption>
|
||||
))}
|
||||
</ListboxOptions>
|
||||
</Transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
);
|
||||
if (onChange != undefined)
|
||||
void onChange({
|
||||
target: getValues("target"),
|
||||
// we provide the value as the state will not have updated yet for this context
|
||||
targetType: retrieveTargetType(value),
|
||||
});
|
||||
}}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<div className="relative">
|
||||
<ListboxButton
|
||||
className={cn(
|
||||
"relative h-full w-full cursor-default rounded-r-lg bg-zinc-700 py-2 pr-10 pl-1 text-right whitespace-nowrap",
|
||||
"text-xs focus:outline-hidden focus-visible:border-indigo-500 sm:text-sm md:text-base lg:text-lg",
|
||||
"focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300"
|
||||
)}
|
||||
>
|
||||
{/* Fetch special text for 'auto' mode, otherwise just use the options. */}
|
||||
<span className="block">
|
||||
{selected == "auto" ? (
|
||||
// If the detected type was provided, then notate which in parentheses. Compact object naming might be better in the future.
|
||||
detectedType.isJust ? (
|
||||
<>
|
||||
Auto (
|
||||
<span className="animate-pulse">
|
||||
{targetShortNames[detectedType.value]}
|
||||
</span>
|
||||
)
|
||||
</>
|
||||
) : (
|
||||
objectNames["auto"]
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<LockClosedIcon
|
||||
className="mr-2.5 mb-1 inline h-4 w-4 animate-pulse text-zinc-500"
|
||||
aria-hidden
|
||||
/>
|
||||
{objectNames[selected]}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<ChevronUpDownIcon className="h-5 w-5 text-zinc-200" aria-hidden="true" />
|
||||
</span>
|
||||
</ListboxButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<ListboxOptions
|
||||
className={cn(
|
||||
"scrollbar-thin absolute right-0 mt-1 max-h-60 min-w-full overflow-auto rounded-md bg-zinc-700 py-1",
|
||||
"text-zinc-200 shadow-lg ring-1 ring-black/5 focus:outline-hidden sm:text-sm"
|
||||
)}
|
||||
>
|
||||
{Object.entries(objectNames).map(([key, value]) => (
|
||||
<ListboxOption
|
||||
key={key}
|
||||
className={({ focus }) =>
|
||||
cn(
|
||||
"relative cursor-default py-2 pr-4 pl-10 select-none",
|
||||
focus ? "bg-zinc-800 text-zinc-300" : null
|
||||
)
|
||||
}
|
||||
value={key}
|
||||
>
|
||||
{({ selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={cn(
|
||||
"block text-right text-xs whitespace-nowrap md:text-sm lg:text-base",
|
||||
selected ? "font-medium" : null
|
||||
)}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
{selected ? (
|
||||
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-blue-500">
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
) : (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
console.log("TODO: Show Help Explanation");
|
||||
}}
|
||||
className="absolute inset-y-0 left-0 flex items-center pl-4 text-lg font-bold opacity-20 hover:animate-pulse"
|
||||
>
|
||||
?
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</ListboxOption>
|
||||
))}
|
||||
</ListboxOptions>
|
||||
</Transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
);
|
||||
|
||||
return (
|
||||
<form
|
||||
className="pb-3"
|
||||
onSubmit={
|
||||
onSubmit != undefined
|
||||
? onPromise(handleSubmit(onSubmit))
|
||||
: preventDefault
|
||||
}
|
||||
>
|
||||
<div className="col">
|
||||
<label htmlFor="search" className="sr-only">
|
||||
Search
|
||||
</label>
|
||||
<div className="relative flex">
|
||||
{searchIcon}
|
||||
{searchInput}
|
||||
{dropdown}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col">
|
||||
<div className="flex flex-wrap pb-1 pt-3 text-sm">
|
||||
<div className="whitespace-nowrap">
|
||||
<input
|
||||
className="ml-2 mr-1 whitespace-nowrap text-zinc-800 accent-blue-700"
|
||||
type="checkbox"
|
||||
{...register("requestJSContact")}
|
||||
/>
|
||||
<label className="text-zinc-300" htmlFor="requestJSContact">
|
||||
Request JSContact
|
||||
</label>
|
||||
</div>
|
||||
<div className="whitespace-nowrap">
|
||||
<input
|
||||
className="ml-2 mr-1 bg-zinc-500 text-inherit accent-blue-700"
|
||||
type="checkbox"
|
||||
{...register("followReferral")}
|
||||
/>
|
||||
<label className="text-zinc-300" htmlFor="followReferral">
|
||||
Follow referral to registrar's RDAP record
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
return (
|
||||
<form
|
||||
className="pb-3"
|
||||
onSubmit={onSubmit != undefined ? onPromise(handleSubmit(onSubmit)) : preventDefault}
|
||||
>
|
||||
<div className="col">
|
||||
<label htmlFor="search" className="sr-only">
|
||||
Search
|
||||
</label>
|
||||
<div className="relative flex">
|
||||
{searchIcon}
|
||||
{searchInput}
|
||||
{dropdown}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col">
|
||||
<div className="flex flex-wrap pt-3 pb-1 text-sm">
|
||||
<div className="whitespace-nowrap">
|
||||
<input
|
||||
className="mr-1 ml-2 whitespace-nowrap text-zinc-800 accent-blue-700"
|
||||
type="checkbox"
|
||||
{...register("requestJSContact")}
|
||||
/>
|
||||
<label className="text-zinc-300" htmlFor="requestJSContact">
|
||||
Request JSContact
|
||||
</label>
|
||||
</div>
|
||||
<div className="whitespace-nowrap">
|
||||
<input
|
||||
className="mr-1 ml-2 bg-zinc-500 text-inherit accent-blue-700"
|
||||
type="checkbox"
|
||||
{...register("followReferral")}
|
||||
/>
|
||||
<label className="text-zinc-300" htmlFor="followReferral">
|
||||
Follow referral to registrar's RDAP record
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default LookupInput;
|
||||
|
||||
@@ -7,54 +7,51 @@ import PropertyList from "@/components/common/PropertyList";
|
||||
import AbstractCard from "@/components/common/AbstractCard";
|
||||
|
||||
export type AutnumCardProps = {
|
||||
data: AutonomousNumber;
|
||||
url?: string;
|
||||
data: AutonomousNumber;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
const AutnumCard: FunctionComponent<AutnumCardProps> = ({
|
||||
data,
|
||||
url,
|
||||
}: AutnumCardProps) => {
|
||||
const asnRange =
|
||||
data.startAutnum === data.endAutnum
|
||||
? `AS${data.startAutnum}`
|
||||
: `AS${data.startAutnum}-AS${data.endAutnum}`;
|
||||
const AutnumCard: FunctionComponent<AutnumCardProps> = ({ data, url }: AutnumCardProps) => {
|
||||
const asnRange =
|
||||
data.startAutnum === data.endAutnum
|
||||
? `AS${data.startAutnum}`
|
||||
: `AS${data.startAutnum}-AS${data.endAutnum}`;
|
||||
|
||||
return (
|
||||
<AbstractCard
|
||||
data={data}
|
||||
url={url}
|
||||
header={
|
||||
<>
|
||||
<span className="font-mono tracking-tighter">AUTONOMOUS SYSTEM</span>
|
||||
<span className="font-mono tracking-wide">{asnRange}</span>
|
||||
<span className="whitespace-nowrap">({data.handle})</span>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<dl>
|
||||
<Property title="Name">{data.name}</Property>
|
||||
<Property title="Handle">{data.handle}</Property>
|
||||
<Property title="ASN Range">
|
||||
{data.startAutnum === data.endAutnum
|
||||
? `AS${data.startAutnum}`
|
||||
: `AS${data.startAutnum} - AS${data.endAutnum}`}
|
||||
</Property>
|
||||
<Property title="Type">{data.type}</Property>
|
||||
<Property title="Country">{data.country.toUpperCase()}</Property>
|
||||
<Property title="Events">
|
||||
<Events key={0} data={data.events} />
|
||||
</Property>
|
||||
<PropertyList title="Status">
|
||||
{data.status.map((status, index) => (
|
||||
<PropertyList.Item key={index} title={status}>
|
||||
{status}
|
||||
</PropertyList.Item>
|
||||
))}
|
||||
</PropertyList>
|
||||
</dl>
|
||||
</AbstractCard>
|
||||
);
|
||||
return (
|
||||
<AbstractCard
|
||||
data={data}
|
||||
url={url}
|
||||
header={
|
||||
<>
|
||||
<span className="font-mono tracking-tighter">AUTONOMOUS SYSTEM</span>
|
||||
<span className="font-mono tracking-wide">{asnRange}</span>
|
||||
<span className="whitespace-nowrap">({data.handle})</span>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<dl>
|
||||
<Property title="Name">{data.name}</Property>
|
||||
<Property title="Handle">{data.handle}</Property>
|
||||
<Property title="ASN Range">
|
||||
{data.startAutnum === data.endAutnum
|
||||
? `AS${data.startAutnum}`
|
||||
: `AS${data.startAutnum} - AS${data.endAutnum}`}
|
||||
</Property>
|
||||
<Property title="Type">{data.type}</Property>
|
||||
<Property title="Country">{data.country.toUpperCase()}</Property>
|
||||
<Property title="Events">
|
||||
<Events key={0} data={data.events} />
|
||||
</Property>
|
||||
<PropertyList title="Status">
|
||||
{data.status.map((status, index) => (
|
||||
<PropertyList.Item key={index} title={status}>
|
||||
{status}
|
||||
</PropertyList.Item>
|
||||
))}
|
||||
</PropertyList>
|
||||
</dl>
|
||||
</AbstractCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default AutnumCard;
|
||||
|
||||
@@ -8,49 +8,46 @@ import PropertyList from "@/components/common/PropertyList";
|
||||
import AbstractCard from "@/components/common/AbstractCard";
|
||||
|
||||
export type DomainProps = {
|
||||
data: Domain;
|
||||
url?: string;
|
||||
data: Domain;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
const DomainCard: FunctionComponent<DomainProps> = ({
|
||||
data,
|
||||
url,
|
||||
}: DomainProps) => {
|
||||
return (
|
||||
<AbstractCard
|
||||
data={data}
|
||||
url={url}
|
||||
header={
|
||||
<>
|
||||
<span className="font-mono tracking-tighter">DOMAIN</span>
|
||||
<span className="font-mono tracking-wide">
|
||||
{data.ldhName ?? data.unicodeName}
|
||||
</span>
|
||||
<span className="whitespace-nowrap">({data.handle})</span>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<dl>
|
||||
{data.unicodeName != undefined ? (
|
||||
<Property title="Unicode Name">{data.unicodeName}</Property>
|
||||
) : null}
|
||||
<Property title={data.unicodeName != undefined ? "LHD Name" : "Name"}>
|
||||
{data.ldhName}
|
||||
</Property>
|
||||
<Property title="Handle">{data.handle}</Property>
|
||||
<Property title="Events">
|
||||
<Events key={0} data={data.events} />
|
||||
</Property>
|
||||
<PropertyList title="Status">
|
||||
{data.status.map((statusKey, index) => (
|
||||
<PropertyList.Item key={index} title={rdapStatusInfo[statusKey]}>
|
||||
{statusKey}
|
||||
</PropertyList.Item>
|
||||
))}
|
||||
</PropertyList>
|
||||
</dl>
|
||||
</AbstractCard>
|
||||
);
|
||||
const DomainCard: FunctionComponent<DomainProps> = ({ data, url }: DomainProps) => {
|
||||
return (
|
||||
<AbstractCard
|
||||
data={data}
|
||||
url={url}
|
||||
header={
|
||||
<>
|
||||
<span className="font-mono tracking-tighter">DOMAIN</span>
|
||||
<span className="font-mono tracking-wide">
|
||||
{data.ldhName ?? data.unicodeName}
|
||||
</span>
|
||||
<span className="whitespace-nowrap">({data.handle})</span>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<dl>
|
||||
{data.unicodeName != undefined ? (
|
||||
<Property title="Unicode Name">{data.unicodeName}</Property>
|
||||
) : null}
|
||||
<Property title={data.unicodeName != undefined ? "LHD Name" : "Name"}>
|
||||
{data.ldhName}
|
||||
</Property>
|
||||
<Property title="Handle">{data.handle}</Property>
|
||||
<Property title="Events">
|
||||
<Events key={0} data={data.events} />
|
||||
</Property>
|
||||
<PropertyList title="Status">
|
||||
{data.status.map((statusKey, index) => (
|
||||
<PropertyList.Item key={index} title={rdapStatusInfo[statusKey]}>
|
||||
{statusKey}
|
||||
</PropertyList.Item>
|
||||
))}
|
||||
</PropertyList>
|
||||
</dl>
|
||||
</AbstractCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default DomainCard;
|
||||
|
||||
@@ -4,24 +4,24 @@ import { Fragment } from "react";
|
||||
import DynamicDate from "@/components/common/DynamicDate";
|
||||
|
||||
export type EventsProps = {
|
||||
data: Event[];
|
||||
data: Event[];
|
||||
};
|
||||
const Events: FunctionComponent<EventsProps> = ({ data }) => {
|
||||
return (
|
||||
<dl>
|
||||
{data.map(({ eventAction, eventDate, eventActor }, index) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<dt className="font-weight-bolder">{eventAction}:</dt>
|
||||
<dd>
|
||||
<DynamicDate value={new Date(eventDate)} />
|
||||
{eventActor != null ? ` (by ${eventActor})` : null}
|
||||
</dd>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</dl>
|
||||
);
|
||||
return (
|
||||
<dl>
|
||||
{data.map(({ eventAction, eventDate, eventActor }, index) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<dt className="font-weight-bolder">{eventAction}:</dt>
|
||||
<dd>
|
||||
<DynamicDate value={new Date(eventDate)} />
|
||||
{eventActor != null ? ` (by ${eventActor})` : null}
|
||||
</dd>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</dl>
|
||||
);
|
||||
};
|
||||
|
||||
export default Events;
|
||||
|
||||
@@ -2,53 +2,39 @@ import type { FunctionComponent } from "react";
|
||||
import DomainCard from "@/components/lookup/DomainCard";
|
||||
import IPCard from "@/components/lookup/IPCard";
|
||||
import AutnumCard from "@/components/lookup/AutnumCard";
|
||||
import type {
|
||||
Domain,
|
||||
AutonomousNumber,
|
||||
Entity,
|
||||
Nameserver,
|
||||
IpNetwork,
|
||||
} from "@/types";
|
||||
import type { Domain, AutonomousNumber, Entity, Nameserver, IpNetwork } from "@/types";
|
||||
import AbstractCard from "@/components/common/AbstractCard";
|
||||
|
||||
export type ParsedGeneric =
|
||||
| Domain
|
||||
| Nameserver
|
||||
| Entity
|
||||
| AutonomousNumber
|
||||
| IpNetwork;
|
||||
export type ParsedGeneric = Domain | Nameserver | Entity | AutonomousNumber | IpNetwork;
|
||||
|
||||
export type ObjectProps = {
|
||||
data: ParsedGeneric;
|
||||
url?: string;
|
||||
data: ParsedGeneric;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
const Generic: FunctionComponent<ObjectProps> = ({
|
||||
data,
|
||||
url,
|
||||
}: ObjectProps) => {
|
||||
switch (data.objectClassName) {
|
||||
case "domain":
|
||||
return <DomainCard url={url} data={data} />;
|
||||
case "ip network":
|
||||
return <IPCard url={url} data={data} />;
|
||||
case "autnum":
|
||||
return <AutnumCard url={url} data={data} />;
|
||||
case "entity":
|
||||
case "nameserver":
|
||||
default:
|
||||
return (
|
||||
<AbstractCard url={url}>
|
||||
Not implemented. (<pre>{data.objectClassName ?? "null"}</pre>)
|
||||
</AbstractCard>
|
||||
);
|
||||
}
|
||||
const Generic: FunctionComponent<ObjectProps> = ({ data, url }: ObjectProps) => {
|
||||
switch (data.objectClassName) {
|
||||
case "domain":
|
||||
return <DomainCard url={url} data={data} />;
|
||||
case "ip network":
|
||||
return <IPCard url={url} data={data} />;
|
||||
case "autnum":
|
||||
return <AutnumCard url={url} data={data} />;
|
||||
case "entity":
|
||||
case "nameserver":
|
||||
default:
|
||||
return (
|
||||
<AbstractCard url={url}>
|
||||
Not implemented. (<pre>{data.objectClassName ?? "null"}</pre>)
|
||||
</AbstractCard>
|
||||
);
|
||||
}
|
||||
|
||||
// const title: string = (data.unicodeName ?? data.ldhName ?? data.handle)?.toUpperCase() ?? "Response";
|
||||
// return <div className="card">
|
||||
// <div className="card-header">{title}</div>
|
||||
// {objectFragment}
|
||||
// </div>
|
||||
// const title: string = (data.unicodeName ?? data.ldhName ?? data.handle)?.toUpperCase() ?? "Response";
|
||||
// return <div className="card">
|
||||
// <div className="card-header">{title}</div>
|
||||
// {objectFragment}
|
||||
// </div>
|
||||
};
|
||||
|
||||
export default Generic;
|
||||
|
||||
@@ -7,50 +7,50 @@ import PropertyList from "@/components/common/PropertyList";
|
||||
import AbstractCard from "@/components/common/AbstractCard";
|
||||
|
||||
export type IPCardProps = {
|
||||
data: IpNetwork;
|
||||
url?: string;
|
||||
data: IpNetwork;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
const IPCard: FunctionComponent<IPCardProps> = ({ data, url }: IPCardProps) => {
|
||||
return (
|
||||
<AbstractCard
|
||||
data={data}
|
||||
url={url}
|
||||
header={
|
||||
<>
|
||||
<span className="font-mono tracking-tighter">IP NETWORK</span>
|
||||
<span className="font-mono tracking-wide">
|
||||
{data.startAddress}
|
||||
{data.startAddress !== data.endAddress && ` - ${data.endAddress}`}
|
||||
</span>
|
||||
<span className="whitespace-nowrap">({data.handle})</span>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<dl>
|
||||
<Property title="Name">{data.name}</Property>
|
||||
<Property title="Handle">{data.handle}</Property>
|
||||
<Property title="IP Version">{data.ipVersion.toUpperCase()}</Property>
|
||||
<Property title="Start Address">{data.startAddress}</Property>
|
||||
<Property title="End Address">{data.endAddress}</Property>
|
||||
<Property title="Type">{data.type}</Property>
|
||||
{data.country && <Property title="Country">{data.country}</Property>}
|
||||
{data.parentHandle && (
|
||||
<Property title="Parent Handle">{data.parentHandle}</Property>
|
||||
)}
|
||||
<Property title="Events">
|
||||
<Events key={0} data={data.events} />
|
||||
</Property>
|
||||
<PropertyList title="Status">
|
||||
{data.status.map((status, index) => (
|
||||
<PropertyList.Item key={index} title={status}>
|
||||
{status}
|
||||
</PropertyList.Item>
|
||||
))}
|
||||
</PropertyList>
|
||||
</dl>
|
||||
</AbstractCard>
|
||||
);
|
||||
return (
|
||||
<AbstractCard
|
||||
data={data}
|
||||
url={url}
|
||||
header={
|
||||
<>
|
||||
<span className="font-mono tracking-tighter">IP NETWORK</span>
|
||||
<span className="font-mono tracking-wide">
|
||||
{data.startAddress}
|
||||
{data.startAddress !== data.endAddress && ` - ${data.endAddress}`}
|
||||
</span>
|
||||
<span className="whitespace-nowrap">({data.handle})</span>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<dl>
|
||||
<Property title="Name">{data.name}</Property>
|
||||
<Property title="Handle">{data.handle}</Property>
|
||||
<Property title="IP Version">{data.ipVersion.toUpperCase()}</Property>
|
||||
<Property title="Start Address">{data.startAddress}</Property>
|
||||
<Property title="End Address">{data.endAddress}</Property>
|
||||
<Property title="Type">{data.type}</Property>
|
||||
{data.country && <Property title="Country">{data.country}</Property>}
|
||||
{data.parentHandle && (
|
||||
<Property title="Parent Handle">{data.parentHandle}</Property>
|
||||
)}
|
||||
<Property title="Events">
|
||||
<Events key={0} data={data.events} />
|
||||
</Property>
|
||||
<PropertyList title="Status">
|
||||
{data.status.map((status, index) => (
|
||||
<PropertyList.Item key={index} title={status}>
|
||||
{status}
|
||||
</PropertyList.Item>
|
||||
))}
|
||||
</PropertyList>
|
||||
</dl>
|
||||
</AbstractCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default IPCard;
|
||||
|
||||
Reference in New Issue
Block a user