Commit latest design, styling & functionality

This commit is contained in:
2023-05-23 20:00:16 -05:00
parent 8da615a8c1
commit ece0ffc7a0
6 changed files with 147 additions and 142 deletions

View File

@@ -1,37 +1,79 @@
import {useForm} from "react-hook-form"; import { useForm } from "react-hook-form";
import type {FunctionComponent} from "react"; import type { FunctionComponent } from "react";
import {onPromise} from "@/helpers"; import { onPromise, preventDefault } from "@/helpers";
import type {TargetType} from "@/types"; import type { SubmitProps, TargetType } from "@/types";
import { MagnifyingGlassIcon } from "@heroicons/react/20/solid";
type LookupInputProps = { type LookupInputProps = {
isLoading?: boolean isLoading?: boolean;
onRegistry?: (type: TargetType) => Promise<void>; // When a type of registry is detected when a user changes their input, this is called.
} onRegistry?: (type: TargetType) => Promise<any>;
// When a user hits submit, this is called.
onSubmit?: (props: SubmitProps) => Promise<any>;
};
type LookupForm = { const LookupInput: FunctionComponent<LookupInputProps> = ({
target: string; isLoading,
} onSubmit,
}: LookupInputProps) => {
const { register, handleSubmit } = useForm<SubmitProps>({
defaultValues: {
followReferral: false,
requestJSContact: false,
},
});
const LookupInput: FunctionComponent<LookupInputProps> = ({isLoading}: LookupInputProps) => { return (
const {register, handleSubmit} = useForm<LookupForm>(); <form
className="pb-3"
const onSubmit = (data: LookupForm) => { onSubmit={
return; onSubmit != undefined
} ? onPromise(handleSubmit(onSubmit))
: preventDefault
return ( }
<form onSubmit={onPromise(handleSubmit(onSubmit))} className="form-inline"> >
<input <div className="col">
className="form-control bg-zinc-800 focus:bg-zinc-700 focus:border-zinc-600 border-zinc-700 text-zinc-200" <label htmlFor="search" className="sr-only">
{...register("target", {required: true})} Search
placeholder="A domain, an IP address, a TLD, an RDAP URL..." </label>
disabled={isLoading} <div className="relative">
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<MagnifyingGlassIcon
className="h-5 w-5 text-zinc-400"
aria-hidden="true"
/> />
<div className="input-group-append"> </div>
<input id="button" type="button" value="Submit" disabled={isLoading}/> <input
</div> className="lg:py-4.5 block w-full rounded-md border border-transparent bg-zinc-700 py-2 pl-10 pr-3 text-sm placeholder-zinc-400 placeholder:translate-y-2 focus:text-zinc-200 focus:outline-none sm:text-sm md:py-3 md:text-base lg:text-lg"
</form> disabled={isLoading}
) placeholder="A domain, an IP address, a TLD, an RDAP URL..."
} type="search"
{...register("target", { required: true })}
/>
</div>
</div>
<div className="col p-0">
<div className="flex pt-3 pb-1 text-sm">
<input
className="ml-2 mr-1 text-zinc-800 accent-zinc-700"
type="checkbox"
{...register("requestJSContact")}
/>
<label className="" htmlFor="requestJSContact">
Request JSContact
</label>
<input
className="ml-2 mr-1 bg-zinc-500 text-inherit accent-zinc-700"
type="checkbox"
{...register("followReferral")}
/>
<label className="" htmlFor="followReferral">
Follow referral to registrar&apos;s RDAP record
</label>
</div>
</div>
</form>
);
};
export default LookupInput; export default LookupInput;

View File

@@ -37,3 +37,7 @@ export function truncated(input: string, maxLength: number, ellipsis = "...") {
export function classNames(...classes: (string | null | undefined)[]) { export function classNames(...classes: (string | null | undefined)[]) {
return classes.filter(Boolean).join(" "); return classes.filter(Boolean).join(" ");
} }
export function preventDefault(event: SyntheticEvent | Event) {
event.preventDefault();
}

View File

@@ -177,7 +177,6 @@ const useLookup = (warningHandler?: WarningHandler) => {
props: SubmitProps props: SubmitProps
): Promise<ParsedGeneric | undefined> { ): Promise<ParsedGeneric | undefined> {
try { try {
setTarget(props.target);
const response = await submitInternal(); const response = await submitInternal();
if (response == undefined) if (response == undefined)
throw new Error("Internal submission failed to yield any data."); throw new Error("Internal submission failed to yield any data.");

View File

@@ -1,109 +1,62 @@
import {type NextPage} from "next"; import { type NextPage } from "next";
import Head from "next/head"; import Head from "next/head";
import {useState} from "react"; import { useState } from "react";
import Generic, {type ParsedGeneric} from "@/components/lookup/Generic"; import Generic, { type ParsedGeneric } from "@/components/lookup/Generic";
import useLookup from "@/hooks/useLookup"; import useLookup from "@/hooks/useLookup";
import {onPromise} from "@/helpers"; import { OGP } from "react-ogp";
import {OGP} from "react-ogp";
import LookupInput from "@/components/form/LookupInput"; import LookupInput from "@/components/form/LookupInput";
const Index: NextPage = () => { const Index: NextPage = () => {
const {error, setTarget, submit, currentType} = useLookup(); const { error, setTarget, submit } = useLookup();
const [response, setResponse] = useState<ParsedGeneric | null>(); const [response, setResponse] = useState<ParsedGeneric | null>();
return ( return (
<> <>
<Head> <Head>
<title>rdap.xevion.dev</title> <title>rdap.xevion.dev</title>
<OGP <OGP
url="https://rdap.xevion.dev" url="https://rdap.xevion.dev"
title="RDAP | by Xevion.dev" title="RDAP | by Xevion.dev"
description="A custom, private RDAP lookup client built by Xevion." description="A custom, private RDAP lookup client built by Xevion."
siteName="rdap.xevion.dev" siteName="rdap.xevion.dev"
type="website" type="website"
/> />
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="shortcut icon" href="/shortcut-icon.svg"/> <link rel="shortcut icon" href="/shortcut-icon.svg" />
<meta name="keywords" content="xevion, rdap, whois, rdap, domain name, dns, ip address"/> <meta
</Head> name="keywords"
<> content="xevion, rdap, whois, rdap, domain name, dns, ip address"
<style jsx>{` />
dd { </Head>
margin: 0.5em 0 1em 2em; <nav className="bg-zinc-850 px-5 py-4 shadow-sm">
} <span className="text-white" style={{ fontSize: "larger" }}>
<a className="text-xl font-medium" href="#">
.card { rdap.xevion.dev
margin-bottom: 1em; </a>
} </span>
</nav>
dl { <div className="mx-auto max-w-screen-sm px-5 lg:max-w-screen-md xl:max-w-screen-lg">
margin: 0; <div className="dark container mx-auto w-full py-6 md:py-12 ">
} <LookupInput
onRegistry={async (type) => {
.rdap-status-code, .rdap-event-time { // await setTarget(type);
border-bottom: 1px dashed silver; }}
} onSubmit={async (props) => {
setTarget(props.target);
#object { const response = await submit(props);
text-transform: lowercase; setResponse(response);
} }}
/>
#spinner-msg { {error != null ? (
height: 2em; <div className="my-3 mx-7 rounded border-2 border-red-800/40 bg-zinc-700 p-2 text-zinc-300">
display: inline-block; {error}
margin: -0.25em 0 0 0; </div>
padding: 0.25em 0 0 0; ) : null}
} {response != null ? <Generic data={response} /> : null}
</div>
`}</style> </div>
<nav className="navbar navbar-expand-lg navbar-dark shadow-sm"> </>
<span className="text-white" style={{fontSize: 'larger'}}> );
<a className="navbar-brand" href="#">rdap.xevion.dev</a>
</span>
</nav>
<div className="container py-12 mx-auto max-w-screen-lg">
<LookupInput />
<form onSubmit={onPromise(async function (e) {
e.preventDefault()
const r = await submit();
setResponse(() => r ?? null);
})} className="form-inline">
<div className="col p-0">
<div className="input-group">
<input
onChange={(e) => setTarget(e.currentTarget.value)}
className="form-control bg-zinc-800 focus:bg-zinc-700 focus:border-zinc-600 border-zinc-700 text-zinc-200"
type="text"
/>
</div>
</div>
</form>
<div className="container p-0 italic text-[#aaa]" style={{fontSize: "small"}}>
<div className="col pt-3 pb-1">
Options:&nbsp;
<label htmlFor="request-jscontact">
<input name="request-jscontact" id="request-jscontact" type="checkbox"/>
Request JSContact
</label>
&nbsp;
<label htmlFor="follow-referral">
<input name="follow-referral" id="follow-referral" type="checkbox"/>
Follow referral to registrar&apos;s RDAP record
</label>
</div>
</div>
<div>
{error}
</div>
<div id="output-div">
{response != null ? <Generic data={response}/> : null}
</div>
</div>
</>
</>
);
}; };
export default Index; export default Index;

View File

@@ -26,3 +26,9 @@ export type IpNetwork = z.infer<typeof IpNetworkSchema>;
export type AutonomousNumber = z.infer<typeof AutonomousNumberSchema>; export type AutonomousNumber = z.infer<typeof AutonomousNumberSchema>;
export type Register = z.infer<typeof RegisterSchema>; export type Register = z.infer<typeof RegisterSchema>;
export type Domain = z.infer<typeof DomainSchema>; export type Domain = z.infer<typeof DomainSchema>;
export type SubmitProps = {
target: string;
requestJSContact: boolean;
followReferral: boolean;
};

View File

@@ -1,14 +1,15 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
module.exports = { module.exports = {
content: ["./src/**/*.{js,ts,jsx,tsx}"], content: ["./src/**/*.{js,ts,jsx,tsx}"],
theme: { darkMode: "class",
extend: { theme: {
colors: { extend: {
zinc: { colors: {
850: "#1D1D20", zinc: {
850: "#1D1D20",
},
},
}, },
},
}, },
}, plugins: [],
plugins: [],
}; };