From bcb77d979d817e1e609adb4d007bbbcc3f61efbd Mon Sep 17 00:00:00 2001 From: Tom van der Lee Date: Thu, 30 Dec 2021 09:51:00 +0100 Subject: Prepare for github --- src/_reset.scss | 48 +++++++ src/components/App/App.module.scss | 55 ++++++++ src/components/App/App.tsx | 68 ++++++++++ src/components/Content/Content.module.scss | 51 +++++++ src/components/Content/Content.tsx | 100 ++++++++++++++ src/components/Details/Details.module.scss | 84 ++++++++++++ src/components/Details/Details.tsx | 148 +++++++++++++++++++++ .../RequestSummary/RequestSummary.module.scss | 24 ++++ src/components/RequestSummary/RequestSummary.tsx | 33 +++++ src/hooks/useRequests.tsx | 77 +++++++++++ src/index.scss | 16 +++ src/index.tsx | 7 + src/utils.ts | 4 + 13 files changed, 715 insertions(+) create mode 100644 src/_reset.scss create mode 100644 src/components/App/App.module.scss create mode 100644 src/components/App/App.tsx create mode 100644 src/components/Content/Content.module.scss create mode 100644 src/components/Content/Content.tsx create mode 100644 src/components/Details/Details.module.scss create mode 100644 src/components/Details/Details.tsx create mode 100644 src/components/RequestSummary/RequestSummary.module.scss create mode 100644 src/components/RequestSummary/RequestSummary.tsx create mode 100644 src/hooks/useRequests.tsx create mode 100644 src/index.scss create mode 100644 src/index.tsx create mode 100644 src/utils.ts (limited to 'src') diff --git a/src/_reset.scss b/src/_reset.scss new file mode 100644 index 0000000..ed11813 --- /dev/null +++ b/src/_reset.scss @@ -0,0 +1,48 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/src/components/App/App.module.scss b/src/components/App/App.module.scss new file mode 100644 index 0000000..2036eb9 --- /dev/null +++ b/src/components/App/App.module.scss @@ -0,0 +1,55 @@ +.app { + display: flex; + flex-flow: column nowrap; + grid-template-rows: auto 1fr; + height: 100vh; + overflow: hidden; +} + +.main { + display: flex; + flex-flow: row nowrap; + flex-grow: 1; + overflow: hidden; +} + +.header { + font-size: 1.2em; + display: flex; + flex-flow: row nowrap; + align-items: center; + justify-content: space-between; + background: black; + color: white; + padding: 1em; + + a { + color: white; + } +} + +.sidebar { + width: calc((6 / 16) * 100%); + height: 100%; + grid-area: sidebar; + border-right: 1px solid black; + overflow-y: auto; + + li { + border-bottom: 1px solid gray; + } +} + +.details { + width: calc((10 / 16) * 100%); + overflow: hidden; + height: 100%; +} + +.noRequest, .noRequestSelected { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx new file mode 100644 index 0000000..3a7fe9b --- /dev/null +++ b/src/components/App/App.tsx @@ -0,0 +1,68 @@ +import * as React from "react"; +import useRequests, {RequestResponse} from "../../hooks/useRequests"; +import {useEffect, useMemo, useState} from "react"; + +import styles from './App.module.scss'; +import Details from "../Details/Details"; +import RequestSummary from "../RequestSummary/RequestSummary"; +import {getHost} from "../../utils"; + +interface Config { + url: string +} + +export default function App() { + const [config, setConfig]= useState(null) + useEffect(() => { + fetch(`http://${getHost()}/config/`) + .then(response => response.json() as Promise) + .then(setConfig) + }, []) + + const requests = useRequests(); + const [selectedRequestIndex, setSelectedRequestIndex] = useState(null); + const selectedRequest = useMemo(() => ( + selectedRequestIndex === null + ? null + : requests[selectedRequestIndex] + ), [selectedRequestIndex, requests]); + + return config && ( +
+
+ TTUN + {config.url} +
+
+
    + { + requests.length > 0 + ? requests.slice(0).reverse().map((requestResponse, index) => ( +
  • setSelectedRequestIndex(requests.length - index - 1)} key={`request-${index}`}> + +
  • + )) + : ( +
    +

    No requests

    +
    + ) + } +
+ +
+ { + selectedRequest !== null + ? ( +
+ ) : ( +
+

Select a request to inspect it

+
+ ) + } +
+
+
+ ); +} diff --git a/src/components/Content/Content.module.scss b/src/components/Content/Content.module.scss new file mode 100644 index 0000000..8908516 --- /dev/null +++ b/src/components/Content/Content.module.scss @@ -0,0 +1,51 @@ +.content { + width: 100%; + height: 100%; + display: flex; + flex-flow: column nowrap; + overflow: hidden; +} + +.header { + flex-shrink: 0; + flex-grow: 0; + width: 100%; + display: flex; + padding: 0.5em; + background-color: black; + color: white; +} + +.body { + flex-grow: 1; + flex-shrink: 1; + overflow-y: auto; + + pre { + width: 100%; + height: 100%; + padding: 1em; + font-family: monospace; + overflow: auto; + } + + iframe { + height: 100%; + width: 100%; + } +} + +.renderError { + width: 100%; + height: 100%; + display: flex; + flex-flow: column nowrap; + justify-content: center; + align-items: center; + + a { + margin-top: 1em; + text-decoration: underline; + color: blue; + } +} diff --git a/src/components/Content/Content.tsx b/src/components/Content/Content.tsx new file mode 100644 index 0000000..a7b5949 --- /dev/null +++ b/src/components/Content/Content.tsx @@ -0,0 +1,100 @@ +import styles from "~components/Details/Details.module.scss"; +import * as React from "react"; +import classNames from "classnames"; +import {RequestPayload, ResponsePayload} from "~hooks/useRequests"; +import { + Dispatch, + forwardRef, SetStateAction, + useEffect, + useImperativeHandle, + useMemo, + useRef, + useState +} from "react"; +import ReactJson from 'react-json-view'; +import styles from './Content.module.scss'; + +interface ContentProps { + data: RequestPayload | ResponsePayload + setRaw: Dispatch> + raw?: boolean +} + +export default function Content({ raw, setRaw, ...props }: ContentProps): JSX.Element { + return ( +
+
+ setRaw(!raw)}/> + +
+
+ {(() => { + try { + return ContentBody({ ...props, raw }) + } catch { + return ( +
+

Body could not be rendered

+ setRaw(true)}>View raw +
+ ) + } + })()} +
+
+ ) +}; + +function ContentBody({ data, raw = false }: Omit) { + const contentType = useMemo(() => { + if (raw) { + return ''; + } + + const [_, type] = ( + Object + .entries(data.headers) + .find(([key]) => key.toLowerCase() === 'content-type') + ); + + return type.toLowerCase().split(';')[0]; + }, [data, raw]); + + if (raw) { + return
{atob(data.body)}
+ } + + if (['application/pdf', 'text/html'].includes(contentType)) { + return