Upload files to "frontend/src/components"

master
Joseph 2024-10-31 17:00:55 +00:00
parent 9fd149dd15
commit ac598a5464
5 changed files with 196 additions and 0 deletions

View File

@ -0,0 +1,49 @@
import React from "react";
interface ButtonProps {
children?: JSX.Element | string,
iconRight?: JSX.Element,
iconLeft?: JSX.Element,
text?: string,
loading?: boolean,
disabled?: boolean,
onClick?: React.MouseEventHandler,
className: string,
textClassName: string
}
const Button = ({ children, iconRight, iconLeft, text, loading, disabled, onClick, className, textClassName }: ButtonProps) => {
return (
<button disabled={disabled ? disabled : loading} onClick={onClick} className={className + " relative"}>
{children ? (
<div className={`${loading ? "opacity-0" : ""} ${textClassName}`}>
{children}
</div>
) : (
<div className={`flex items-center justify-center ${textClassName} ${loading ? "opacity-0" : ""}`}>
{iconLeft}
{text}
{iconRight}
</div>
)}
{loading && (
<div className="justify-center flex absolute">
<svg className="animate-spin h-5 w-5 text-white" fill="none" viewBox="0 0 24 24">
<circle
className="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962
7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
</div>
)}
</button>
);
};
export default Button;

View File

@ -0,0 +1,29 @@
import React, { useEffect, useRef } from "react";
import hljs from "highlight.js";
interface CodeBlockProps {
code: string,
lang?: string
}
const CodeBlock = ({ code, lang }: CodeBlockProps) => {
const classes = "bg-black/10 rounded-lg p-2 mt-3 text-sm text-white";
const highlightRef = useRef<HTMLPreElement>(null);
useEffect(() => {
if (highlightRef.current) {
const nodes = highlightRef.current.querySelectorAll("pre code");
for (let i = 0; i < nodes.length; i++)
hljs.highlightBlock(nodes[i] as HTMLElement);
}
}, [code, lang]);
return (
<pre ref={highlightRef} className={classes}>
<code className={`${lang ? `language-${lang}` : ""} !bg-transparent`}>{code}</code>
</pre>
);
};
export default CodeBlock;

View File

@ -0,0 +1,27 @@
import React from "react";
import { Link } from "react-router-dom";
interface HeaderProps {
row?: JSX.Element
}
const Header = (props: HeaderProps) => {
const { row } = props;
return (
<>
<Link to="/">
<h1 className="text-4xl font-mono text-white font-bold transition duration-500 hover:text-pink-500">
RequestBin
</h1>
</Link>
<h5 className="font-mono italic text-neutral-400 font-bold">
A platform to inspect HTTP requests quickly and easily.
</h5>
{row ? <div className="mt-4">{row}</div> : null}
</>
);
};
export default Header;

View File

@ -0,0 +1,76 @@
import React from "react";
import { StoredRequest } from "../types";
import dayjs from "dayjs";
import RelativeTime from "dayjs/plugin/relativeTime";
import { RequestBody } from "./index";
interface RequestProps {
request: StoredRequest
}
dayjs.extend(RelativeTime);
const Request = ({ request }: RequestProps) => {
const url = new URL(request.url);
const methodColours: {[key: string]: string} = {
GET: "bg-purple-500",
POST: "bg-blue-500",
DELETE: "bg-red-500",
fallback: "bg-yellow-600"
};
return (
<div className="p-3 rounded-lg bg-black/10 border-2 border-white/25 w-full flex gap-y-4 flex-col">
<div className="flex gap-2 items-center justify-between">
<div className="flex gap-2 items-center">
<span className={`px-2 font-bold rounded-md uppercase text-white ${methodColours[request.method] ?? methodColours.fallback}`}>
{request.method}
</span>
<span className="font-bold font-mono text-white">
{request.ip}
</span>
</div>
<p className="text-white">
<span className="font-bold">{dayjs(request.timestamp).format("MMM DDD YYYY, HH:mm:ss.SSS")}</span>{" "}
({dayjs(request.timestamp).fromNow()})
</p>
</div>
{request.body && request.body.length > 0 && <RequestBody body={request.body} type={request.headers["content-type"]} />}
<div>
<h4 className="text-white text-xl font-bold">URL</h4>
<p className="text-white text-sm font-bold">Host: <span className="text-pink-500 font-mono">{url.host}</span></p>
<p className="text-white text-sm font-bold">Path: <span className="text-pink-500 font-mono">{url.pathname}</span></p>
{url.search && (
<div className="ml-2">
<>
<h5 className="text-white text-lg font-bold">Query String</h5>
{Object.keys(Object.fromEntries(url.searchParams)).map(key => <>
<p className="text-white text-sm font-bold">{key}:{" "}
<span className="text-pink-500 font-mono">
{url.searchParams.get(key)}
</span>
</p>
</>)}
</>
</div>
)}
</div>
<div>
<h4 className="text-white text-xl font-bold">Headers</h4>
<table className="w-full">
<tbody>
{Object.keys(request.headers).map(key =>
<tr key={key} className="border-b last:border-0 border-white/25">
<td className="w-96 p-1 align-top text-white font-bold text-sm" key={key}>{key}</td>
<td className="p-1 align-top text-pink-500 text-sm font-mono">{request.headers[key]}</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
);
};
export default Request;

View File

@ -0,0 +1,15 @@
import Header from "./Header";
import Spinner from "./Spinner";
import Button from "./Button";
import Request from "./Request";
import RequestBody from "./RequestBody";
import CodeBlock from "./CodeBlock";
export {
Header,
Spinner,
Button,
Request,
RequestBody,
CodeBlock
};