Upload files to "frontend/src/components"
parent
9fd149dd15
commit
ac598a5464
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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
|
||||||
|
};
|
Loading…
Reference in New Issue