diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/_reset.scss | 48 | ||||
| -rw-r--r-- | src/components/App/App.module.scss | 56 | ||||
| -rw-r--r-- | src/components/App/App.tsx | 118 | ||||
| -rw-r--r-- | src/components/Content/Content.tsx | 74 | ||||
| -rw-r--r-- | src/components/Details/Details.tsx | 254 | ||||
| -rw-r--r-- | src/components/RequestSummary/RequestSummary.tsx | 49 | ||||
| -rw-r--r-- | src/index.scss | 12 | ||||
| -rw-r--r-- | src/index.tsx | 2 |
8 files changed, 369 insertions, 244 deletions
diff --git a/src/_reset.scss b/src/_reset.scss deleted file mode 100644 index ed11813..0000000 --- a/src/_reset.scss +++ /dev/null | |||
| @@ -1,48 +0,0 @@ | |||
| 1 | /* http://meyerweb.com/eric/tools/css/reset/ | ||
| 2 | v2.0 | 20110126 | ||
| 3 | License: none (public domain) | ||
| 4 | */ | ||
| 5 | |||
| 6 | html, body, div, span, applet, object, iframe, | ||
| 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, | ||
| 8 | a, abbr, acronym, address, big, cite, code, | ||
| 9 | del, dfn, em, img, ins, kbd, q, s, samp, | ||
| 10 | small, strike, strong, sub, sup, tt, var, | ||
| 11 | b, u, i, center, | ||
| 12 | dl, dt, dd, ol, ul, li, | ||
| 13 | fieldset, form, label, legend, | ||
| 14 | table, caption, tbody, tfoot, thead, tr, th, td, | ||
| 15 | article, aside, canvas, details, embed, | ||
| 16 | figure, figcaption, footer, header, hgroup, | ||
| 17 | menu, nav, output, ruby, section, summary, | ||
| 18 | time, mark, audio, video { | ||
| 19 | margin: 0; | ||
| 20 | padding: 0; | ||
| 21 | border: 0; | ||
| 22 | font-size: 100%; | ||
| 23 | font: inherit; | ||
| 24 | vertical-align: baseline; | ||
| 25 | } | ||
| 26 | /* HTML5 display-role reset for older browsers */ | ||
| 27 | article, aside, details, figcaption, figure, | ||
| 28 | footer, header, hgroup, menu, nav, section { | ||
| 29 | display: block; | ||
| 30 | } | ||
| 31 | body { | ||
| 32 | line-height: 1; | ||
| 33 | } | ||
| 34 | ol, ul { | ||
| 35 | list-style: none; | ||
| 36 | } | ||
| 37 | blockquote, q { | ||
| 38 | quotes: none; | ||
| 39 | } | ||
| 40 | blockquote:before, blockquote:after, | ||
| 41 | q:before, q:after { | ||
| 42 | content: ''; | ||
| 43 | content: none; | ||
| 44 | } | ||
| 45 | table { | ||
| 46 | border-collapse: collapse; | ||
| 47 | border-spacing: 0; | ||
| 48 | } | ||
diff --git a/src/components/App/App.module.scss b/src/components/App/App.module.scss index 2036eb9..9837cdf 100644 --- a/src/components/App/App.module.scss +++ b/src/components/App/App.module.scss | |||
| @@ -1,55 +1,3 @@ | |||
| 1 | .app { | 1 | .content { |
| 2 | display: flex; | 2 | flex: 1 0 auto; |
| 3 | flex-flow: column nowrap; | ||
| 4 | grid-template-rows: auto 1fr; | ||
| 5 | height: 100vh; | ||
| 6 | overflow: hidden; | ||
| 7 | } | ||
| 8 | |||
| 9 | .main { | ||
| 10 | display: flex; | ||
| 11 | flex-flow: row nowrap; | ||
| 12 | flex-grow: 1; | ||
| 13 | overflow: hidden; | ||
| 14 | } | ||
| 15 | |||
| 16 | .header { | ||
| 17 | font-size: 1.2em; | ||
| 18 | display: flex; | ||
| 19 | flex-flow: row nowrap; | ||
| 20 | align-items: center; | ||
| 21 | justify-content: space-between; | ||
| 22 | background: black; | ||
| 23 | color: white; | ||
| 24 | padding: 1em; | ||
| 25 | |||
| 26 | a { | ||
| 27 | color: white; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | .sidebar { | ||
| 32 | width: calc((6 / 16) * 100%); | ||
| 33 | height: 100%; | ||
| 34 | grid-area: sidebar; | ||
| 35 | border-right: 1px solid black; | ||
| 36 | overflow-y: auto; | ||
| 37 | |||
| 38 | li { | ||
| 39 | border-bottom: 1px solid gray; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | .details { | ||
| 44 | width: calc((10 / 16) * 100%); | ||
| 45 | overflow: hidden; | ||
| 46 | height: 100%; | ||
| 47 | } | ||
| 48 | |||
| 49 | .noRequest, .noRequestSelected { | ||
| 50 | width: 100%; | ||
| 51 | height: 100%; | ||
| 52 | display: flex; | ||
| 53 | justify-content: center; | ||
| 54 | align-items: center; | ||
| 55 | } | 3 | } |
diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index e361aa5..61f88f6 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx | |||
| @@ -6,6 +6,8 @@ import styles from './App.module.scss'; | |||
| 6 | import Details from "../Details/Details"; | 6 | import Details from "../Details/Details"; |
| 7 | import RequestSummary from "../RequestSummary/RequestSummary"; | 7 | import RequestSummary from "../RequestSummary/RequestSummary"; |
| 8 | import {getHost} from "../../utils"; | 8 | import {getHost} from "../../utils"; |
| 9 | import {Container, Navbar, Col, Row, Nav, ListGroup} from "react-bootstrap"; | ||
| 10 | import classNames from "classnames"; | ||
| 9 | 11 | ||
| 10 | interface Config { | 12 | interface Config { |
| 11 | url: string | 13 | url: string |
| @@ -50,29 +52,59 @@ export default function App() { | |||
| 50 | ), [selectedRequestIndex, calls]); | 52 | ), [selectedRequestIndex, calls]); |
| 51 | 53 | ||
| 52 | return config && ( | 54 | return config && ( |
| 53 | <div className={styles.app}> | 55 | <> |
| 54 | <header className={styles.header}> | 56 | <Navbar |
| 55 | {statusMap[readyState]} TTUN | 57 | bg="dark" |
| 56 | <a href={config.url} target="_blank">{config.url}</a> | 58 | variant="dark" |
| 57 | </header> | 59 | expand |
| 58 | <main className={styles.main}> | 60 | > |
| 59 | <ul className={styles.sidebar}> | 61 | <Container fluid> |
| 60 | { | 62 | <Navbar.Brand> |
| 61 | calls.length > 0 | 63 | {statusMap[readyState]} TTUN |
| 62 | ? calls.slice(0).reverse().map((requestResponse, index) => ( | 64 | </Navbar.Brand> |
| 63 | <li onClick={() => setSelectedRequestIndex(calls.length - index - 1)} key={`request-${index}`}> | 65 | <Navbar.Text> |
| 64 | <RequestSummary requestResponse={requestResponse} /> | 66 | <a href={config.url} target="_blank">{config.url}</a> |
| 65 | </li> | 67 | </Navbar.Text> |
| 66 | )) | 68 | </Container> |
| 67 | : ( | 69 | </Navbar> |
| 68 | <div className={styles.noRequest}> | 70 | <Container> |
| 69 | <p>No requests</p> | ||
| 70 | </div> | ||
| 71 | ) | ||
| 72 | } | ||
| 73 | </ul> | ||
| 74 | 71 | ||
| 75 | <div className={styles.details}> | 72 | </Container> |
| 73 | <Row className={classNames(styles.content, 'gx-0', 'overflow-hidden')}> | ||
| 74 | <Col className='border-end overflow-auto' xs={4}> | ||
| 75 | <ListGroup variant='flush'> | ||
| 76 | { | ||
| 77 | calls.length > 0 | ||
| 78 | ? ( | ||
| 79 | calls.slice(0).reverse().map((requestResponse, index) => { | ||
| 80 | const selected = selectedRequestIndex === calls.length - index - 1; | ||
| 81 | return ( | ||
| 82 | <ListGroup.Item | ||
| 83 | onClick={() => setSelectedRequestIndex(calls.length - index - 1)} | ||
| 84 | key={`request-${index}`} | ||
| 85 | className={classNames({ | ||
| 86 | 'bg-primary': selected, | ||
| 87 | 'text-light': selected, | ||
| 88 | 'border-bottom': true | ||
| 89 | })} | ||
| 90 | > | ||
| 91 | <RequestSummary | ||
| 92 | requestResponse={requestResponse} | ||
| 93 | selected={selected} | ||
| 94 | /> | ||
| 95 | </ListGroup.Item> | ||
| 96 | ) | ||
| 97 | }) | ||
| 98 | ) | ||
| 99 | : ( | ||
| 100 | <div className={styles.noRequest}> | ||
| 101 | <p>No requests</p> | ||
| 102 | </div> | ||
| 103 | ) | ||
| 104 | } | ||
| 105 | </ListGroup> | ||
| 106 | </Col> | ||
| 107 | <Col xs={8} className=''> | ||
| 76 | { | 108 | { |
| 77 | selectedRequest !== null | 109 | selectedRequest !== null |
| 78 | ? ( | 110 | ? ( |
| @@ -83,8 +115,44 @@ export default function App() { | |||
| 83 | </div> | 115 | </div> |
| 84 | ) | 116 | ) |
| 85 | } | 117 | } |
| 86 | </div> | 118 | </Col> |
| 87 | </main> | 119 | </Row> |
| 88 | </div> | 120 | </> |
| 121 | // <div className={styles.app}> | ||
| 122 | // <header className={styles.header}> | ||
| 123 | // {statusMap[readyState]} TTUN | ||
| 124 | // <a href={config.url} target="_blank">{config.url}</a> | ||
| 125 | // </header> | ||
| 126 | // <main className={styles.main}> | ||
| 127 | // <ul className={styles.sidebar}> | ||
| 128 | // { | ||
| 129 | // calls.length > 0 | ||
| 130 | // ? calls.slice(0).reverse().map((requestResponse, index) => ( | ||
| 131 | // <li onClick={() => setSelectedRequestIndex(calls.length - index - 1)} key={`request-${index}`}> | ||
| 132 | // <RequestSummary requestResponse={requestResponse} /> | ||
| 133 | // </li> | ||
| 134 | // )) | ||
| 135 | // : ( | ||
| 136 | // <div className={styles.noRequest}> | ||
| 137 | // <p>No requests</p> | ||
| 138 | // </div> | ||
| 139 | // ) | ||
| 140 | // } | ||
| 141 | // </ul> | ||
| 142 | // | ||
| 143 | // <div className={styles.details}> | ||
| 144 | // { | ||
| 145 | // selectedRequest !== null | ||
| 146 | // ? ( | ||
| 147 | // <Details requestResponse={selectedRequest} /> | ||
| 148 | // ) : ( | ||
| 149 | // <div className={styles.noRequestSelected}> | ||
| 150 | // <p>Select a request to inspect it</p> | ||
| 151 | // </div> | ||
| 152 | // ) | ||
| 153 | // } | ||
| 154 | // </div> | ||
| 155 | // </main> | ||
| 156 | // </div> | ||
| 89 | ); | 157 | ); |
| 90 | } | 158 | } |
diff --git a/src/components/Content/Content.tsx b/src/components/Content/Content.tsx index a7b5949..cb5e6bc 100644 --- a/src/components/Content/Content.tsx +++ b/src/components/Content/Content.tsx | |||
| @@ -13,6 +13,21 @@ import { | |||
| 13 | } from "react"; | 13 | } from "react"; |
| 14 | import ReactJson from 'react-json-view'; | 14 | import ReactJson from 'react-json-view'; |
| 15 | import styles from './Content.module.scss'; | 15 | import styles from './Content.module.scss'; |
| 16 | import {Col, Container, Row} from "react-bootstrap"; | ||
| 17 | |||
| 18 | function getHeader(headers: { [key: string]: string }, key: string, unit?: string): string | null { | ||
| 19 | console.log(headers, key) | ||
| 20 | try { | ||
| 21 | const [_, value] = Object | ||
| 22 | .entries(headers) | ||
| 23 | .find(([headerKey]) => headerKey.toLowerCase() === key.toLowerCase()) | ||
| 24 | return unit !== undefined | ||
| 25 | ? `${value}${unit}` | ||
| 26 | : value | ||
| 27 | } catch { | ||
| 28 | return null; | ||
| 29 | } | ||
| 30 | } | ||
| 16 | 31 | ||
| 17 | interface ContentProps { | 32 | interface ContentProps { |
| 18 | data: RequestPayload | ResponsePayload | 33 | data: RequestPayload | ResponsePayload |
| @@ -20,17 +35,29 @@ interface ContentProps { | |||
| 20 | raw?: boolean | 35 | raw?: boolean |
| 21 | } | 36 | } |
| 22 | 37 | ||
| 23 | export default function Content({ raw, setRaw, ...props }: ContentProps): JSX.Element { | 38 | export default function Content({ raw, setRaw, data }: ContentProps): JSX.Element { |
| 24 | return ( | 39 | return ( |
| 25 | <div className={styles.content}> | 40 | <> |
| 26 | <div className={styles.header}> | 41 | <Container fluid className="border-bottom"> |
| 27 | <input id='raw' type='checkbox' checked={raw} onChange={() => setRaw(!raw)}/> | 42 | <Row className="py-3"> |
| 28 | <label htmlFor='raw'>Raw</label> | 43 | <Col> |
| 29 | </div> | 44 | <input id='raw' type='checkbox' checked={raw} onChange={() => setRaw(!raw)}/> |
| 30 | <div className={styles.body}> | 45 | <label htmlFor='raw' className="ps-1">Raw</label> |
| 46 | </Col> | ||
| 47 | <Col xs="auto"> | ||
| 48 | { | ||
| 49 | [ | ||
| 50 | getHeader(data.headers, 'content-length', 'bytes'), | ||
| 51 | getHeader(data.headers, 'content-type'), | ||
| 52 | ].filter(x => x !== null).join('; ') | ||
| 53 | } | ||
| 54 | </Col> | ||
| 55 | </Row> | ||
| 56 | </Container> | ||
| 57 | <Row> | ||
| 31 | {(() => { | 58 | {(() => { |
| 32 | try { | 59 | try { |
| 33 | return ContentBody({ ...props, raw }) | 60 | return ContentBody({ data, raw }) |
| 34 | } catch { | 61 | } catch { |
| 35 | return ( | 62 | return ( |
| 36 | <div className={styles.renderError}> | 63 | <div className={styles.renderError}> |
| @@ -40,8 +67,28 @@ export default function Content({ raw, setRaw, ...props }: ContentProps): JSX.El | |||
| 40 | ) | 67 | ) |
| 41 | } | 68 | } |
| 42 | })()} | 69 | })()} |
| 43 | </div> | 70 | </Row> |
| 44 | </div> | 71 | </> |
| 72 | // <div className={styles.content}> | ||
| 73 | // <div className={styles.header}> | ||
| 74 | // <input id='raw' type='checkbox' checked={raw} onChange={() => setRaw(!raw)}/> | ||
| 75 | // <label htmlFor='raw'>Raw</label> | ||
| 76 | // </div> | ||
| 77 | // <div className={styles.body}> | ||
| 78 | // {(() => { | ||
| 79 | // try { | ||
| 80 | // return ContentBody({ ...props, raw }) | ||
| 81 | // } catch { | ||
| 82 | // return ( | ||
| 83 | // <div className={styles.renderError}> | ||
| 84 | // <p>Body could not be rendered</p> | ||
| 85 | // <a onClick={() => setRaw(true)}>View raw</a> | ||
| 86 | // </div> | ||
| 87 | // ) | ||
| 88 | // } | ||
| 89 | // })()} | ||
| 90 | // </div> | ||
| 91 | // </div> | ||
| 45 | ) | 92 | ) |
| 46 | }; | 93 | }; |
| 47 | 94 | ||
| @@ -51,12 +98,7 @@ function ContentBody({ data, raw = false }: Omit<ContentProps, 'setRaw'>) { | |||
| 51 | return ''; | 98 | return ''; |
| 52 | } | 99 | } |
| 53 | 100 | ||
| 54 | const [_, type] = ( | 101 | const type = getHeader(data.headers, 'content-type'); |
| 55 | Object | ||
| 56 | .entries(data.headers) | ||
| 57 | .find(([key]) => key.toLowerCase() === 'content-type') | ||
| 58 | ); | ||
| 59 | |||
| 60 | return type.toLowerCase().split(';')[0]; | 102 | return type.toLowerCase().split(';')[0]; |
| 61 | }, [data, raw]); | 103 | }, [data, raw]); |
| 62 | 104 | ||
diff --git a/src/components/Details/Details.tsx b/src/components/Details/Details.tsx index e91c98c..c96aa7b 100644 --- a/src/components/Details/Details.tsx +++ b/src/components/Details/Details.tsx | |||
| @@ -10,6 +10,16 @@ import RequestSummary from "../RequestSummary/RequestSummary"; | |||
| 10 | import classNames from 'classnames'; | 10 | import classNames from 'classnames'; |
| 11 | import Content from "../Content/Content"; | 11 | import Content from "../Content/Content"; |
| 12 | import {getHost} from "../../utils"; | 12 | import {getHost} from "../../utils"; |
| 13 | import { | ||
| 14 | Button, | ||
| 15 | Card, | ||
| 16 | Col, | ||
| 17 | Container, | ||
| 18 | Nav, | ||
| 19 | NavItem, | ||
| 20 | Row, | ||
| 21 | Table | ||
| 22 | } from "react-bootstrap"; | ||
| 13 | 23 | ||
| 14 | 24 | ||
| 15 | interface TimingProps { | 25 | interface TimingProps { |
| @@ -39,17 +49,31 @@ interface HeadersProps { | |||
| 39 | 49 | ||
| 40 | function Headers({ title, headers }: HeadersProps) { | 50 | function Headers({ title, headers }: HeadersProps) { |
| 41 | return ( | 51 | return ( |
| 42 | <div className={styles.headers}> | 52 | <Card className="m-3"> |
| 43 | <h2>{ title }</h2> | 53 | <Table |
| 44 | { | 54 | striped |
| 45 | Object.entries(headers).map(([key, value]) => ( | 55 | responsive |
| 46 | <> | 56 | borderless |
| 47 | <div>{key}</div> | 57 | hover |
| 48 | <div>{value}</div> | 58 | className='mb-0' |
| 49 | </> | 59 | > |
| 50 | )) | 60 | <thead> |
| 51 | } | 61 | <tr> |
| 52 | </div> | 62 | <th colSpan={2} className="bg-dark text-white rounded-top">{ title }</th> |
| 63 | </tr> | ||
| 64 | </thead> | ||
| 65 | <tbody> | ||
| 66 | { | ||
| 67 | Object.entries(headers).map(([key, value]) => ( | ||
| 68 | <tr> | ||
| 69 | <td>{key}</td> | ||
| 70 | <td>{value}</td> | ||
| 71 | </tr> | ||
| 72 | )) | ||
| 73 | } | ||
| 74 | </tbody> | ||
| 75 | </Table> | ||
| 76 | </Card> | ||
| 53 | ) | 77 | ) |
| 54 | } | 78 | } |
| 55 | 79 | ||
| @@ -75,73 +99,147 @@ export default function Details({ requestResponse }: DetailsProps) { | |||
| 75 | ), [requestResponse]); | 99 | ), [requestResponse]); |
| 76 | 100 | ||
| 77 | return ( | 101 | return ( |
| 78 | <div className={styles.details}> | 102 | <> |
| 79 | <div className={styles.header}> | 103 | <Row> |
| 80 | <RequestSummary requestResponse={requestResponse} className={styles.summary}/> | 104 | <Col> |
| 81 | 105 | <Container fluid style={{ fontSize: '1.5em' }} className="py-3"> | |
| 82 | <div className={styles.tabs}> | 106 | <RequestSummary requestResponse={requestResponse} className={styles.summary}/> |
| 83 | <button | 107 | </Container> |
| 84 | onClick={() => selectTab('headers')} | 108 | </Col> |
| 85 | className={classNames(styles.tab, { | 109 | </Row> |
| 86 | [styles.selected]: tab === 'headers' | 110 | <Row className="gx-0 d-flex"> |
| 87 | })} | 111 | <Col> |
| 88 | > | 112 | <Nav |
| 89 | Headers | 113 | variant="tabs" |
| 90 | </button> | 114 | activeKey={tab} |
| 91 | <button | 115 | onSelect={(tab) => selectTab(tab as Tab)} |
| 92 | onClick={() => selectTab('request')} | ||
| 93 | className={classNames(styles.tab, { | ||
| 94 | [styles.selected]: tab === 'request' | ||
| 95 | })} | ||
| 96 | > | 116 | > |
| 97 | Request | 117 | <Nav.Item> |
| 98 | </button> | 118 | <Nav.Link eventKey="headers"> |
| 99 | <button | 119 | Headers |
| 100 | onClick={() => selectTab('response')} | 120 | </Nav.Link > |
| 101 | className={classNames(styles.tab, { | 121 | </Nav.Item> |
| 102 | [styles.selected]: tab === 'response' | 122 | <Nav.Item> |
| 103 | })} | 123 | <Nav.Link eventKey="request"> |
| 104 | disabled={requestResponse.response === undefined} | 124 | Request |
| 105 | > | 125 | </Nav.Link> |
| 106 | Response | 126 | </Nav.Item> |
| 107 | </button> | 127 | <Nav.Item> |
| 108 | 128 | <Nav.Link | |
| 109 | <div className={styles.emptySpace}> | 129 | eventKey="response" |
| 110 | <Timing timing={requestResponse.response?.timing ?? NaN} /> | 130 | disabled={requestResponse.response === undefined} |
| 111 | <button className={styles.resend} onClick={() => resend()}>Resend</button> | 131 | > |
| 112 | </div> | 132 | Response |
| 113 | </div> | 133 | </Nav.Link> |
| 114 | </div> | 134 | </Nav.Item> |
| 115 | <div className={styles.content}> | 135 | </Nav> |
| 116 | { | 136 | </Col> |
| 117 | tab === 'headers' && ( | 137 | <Col className="border-bottom px-3 " xs="auto"> |
| 118 | <> | 138 | <Timing timing={requestResponse.response?.timing ?? NaN} /> |
| 119 | <Headers | 139 | <Button variant="outline-primary" onClick={() => resend()} className="ms-3"> |
| 120 | title="Request Headers" | 140 | Resend |
| 121 | headers={requestResponse.request.headers} | 141 | </Button> |
| 122 | /> | 142 | </Col> |
| 123 | { | 143 | </Row> |
| 124 | requestResponse.response && ( | 144 | <Row> |
| 125 | <Headers | 145 | <Col> |
| 126 | title="Response Headers" | 146 | { |
| 127 | headers={requestResponse.response.headers} | 147 | tab === 'headers' && ( |
| 128 | /> | 148 | <> |
| 129 | ) | 149 | <Headers |
| 130 | } | 150 | title="Request Headers" |
| 131 | </> | 151 | headers={requestResponse.request.headers} |
| 132 | ) | 152 | /> |
| 133 | } | 153 | { |
| 134 | { | 154 | requestResponse.response && ( |
| 135 | tab === 'request' && ( | 155 | <Headers |
| 136 | <Content data={requestResponse.request} raw={raw} setRaw={setRaw}/> | 156 | title="Response Headers" |
| 137 | ) | 157 | headers={requestResponse.response.headers} |
| 138 | } | 158 | /> |
| 139 | { | 159 | ) |
| 140 | tab === 'response' && requestResponse.response !== undefined && ( | 160 | } |
| 141 | <Content data={requestResponse.response} raw={raw} setRaw={setRaw}/> | 161 | </> |
| 142 | ) | 162 | ) |
| 143 | } | 163 | } |
| 144 | </div> | 164 | { |
| 145 | </div> | 165 | tab === 'request' && ( |
| 166 | <Content data={requestResponse.request} raw={raw} setRaw={setRaw}/> | ||
| 167 | ) | ||
| 168 | } | ||
| 169 | { | ||
| 170 | tab === 'response' && requestResponse.response !== undefined && ( | ||
| 171 | <Content data={requestResponse.response} raw={raw} setRaw={setRaw}/> | ||
| 172 | ) | ||
| 173 | } | ||
| 174 | </Col> | ||
| 175 | </Row> | ||
| 176 | </> | ||
| 177 | // <div className={styles.details}> | ||
| 178 | // <div className={styles.header}> | ||
| 179 | // | ||
| 180 | // <div className={styles.tabs}> | ||
| 181 | // <button | ||
| 182 | // onClick={() => selectTab('headers')} | ||
| 183 | // className={classNames(styles.tab, { | ||
| 184 | // [styles.selected]: tab === 'headers' | ||
| 185 | // })} | ||
| 186 | // > | ||
| 187 | // Headers | ||
| 188 | // </button> | ||
| 189 | // <button | ||
| 190 | // onClick={() => selectTab('request')} | ||
| 191 | // className={classNames(styles.tab, { | ||
| 192 | // [styles.selected]: tab === 'request' | ||
| 193 | // })} | ||
| 194 | // > | ||
| 195 | // Request | ||
| 196 | // </button> | ||
| 197 | // <button | ||
| 198 | // onClick={() => selectTab('response')} | ||
| 199 | // className={classNames(styles.tab, { | ||
| 200 | // [styles.selected]: tab === 'response' | ||
| 201 | // })} | ||
| 202 | // disabled={requestResponse.response === undefined} | ||
| 203 | // > | ||
| 204 | // Response | ||
| 205 | // </button> | ||
| 206 | // | ||
| 207 | // <div className={styles.emptySpace}> | ||
| 208 | // <Timing timing={requestResponse.response?.timing ?? NaN} /> | ||
| 209 | // <button className={styles.resend} onClick={() => resend()}>Resend</button> | ||
| 210 | // </div> | ||
| 211 | // </div> | ||
| 212 | // </div> | ||
| 213 | // <div className={styles.content}> | ||
| 214 | // { | ||
| 215 | // tab === 'headers' && ( | ||
| 216 | // <> | ||
| 217 | // <Headers | ||
| 218 | // title="Request Headers" | ||
| 219 | // headers={requestResponse.request.headers} | ||
| 220 | // /> | ||
| 221 | // { | ||
| 222 | // requestResponse.response && ( | ||
| 223 | // <Headers | ||
| 224 | // title="Response Headers" | ||
| 225 | // headers={requestResponse.response.headers} | ||
| 226 | // /> | ||
| 227 | // ) | ||
| 228 | // } | ||
| 229 | // </> | ||
| 230 | // ) | ||
| 231 | // } | ||
| 232 | // { | ||
| 233 | // tab === 'request' && ( | ||
| 234 | // <Content data={requestResponse.request} raw={raw} setRaw={setRaw}/> | ||
| 235 | // ) | ||
| 236 | // } | ||
| 237 | // { | ||
| 238 | // tab === 'response' && requestResponse.response !== undefined && ( | ||
| 239 | // <Content data={requestResponse.response} raw={raw} setRaw={setRaw}/> | ||
| 240 | // ) | ||
| 241 | // } | ||
| 242 | // </div> | ||
| 243 | // </div> | ||
| 146 | ) | 244 | ) |
| 147 | } | 245 | } |
diff --git a/src/components/RequestSummary/RequestSummary.tsx b/src/components/RequestSummary/RequestSummary.tsx index af928c0..ea28f3e 100644 --- a/src/components/RequestSummary/RequestSummary.tsx +++ b/src/components/RequestSummary/RequestSummary.tsx | |||
| @@ -3,31 +3,48 @@ import * as React from "react"; | |||
| 3 | import classNames from "classnames"; | 3 | import classNames from "classnames"; |
| 4 | 4 | ||
| 5 | import styles from './RequestSummary.module.scss'; | 5 | import styles from './RequestSummary.module.scss'; |
| 6 | import {Badge, Card, Col, Row} from "react-bootstrap"; | ||
| 6 | 7 | ||
| 7 | interface RequestSummaryProps { | 8 | interface RequestSummaryProps { |
| 9 | selected?: boolean | ||
| 8 | requestResponse: RequestResponse | 10 | requestResponse: RequestResponse |
| 9 | className?: string | ||
| 10 | } | 11 | } |
| 11 | 12 | ||
| 12 | function isBetween(value: number, min: number, max: number) { | 13 | function isBetween(value: number, min: number, max: number) { |
| 13 | return value >= min && value <= max | 14 | return value >= min && value <= max |
| 14 | } | 15 | } |
| 15 | 16 | ||
| 16 | export default function RequestSummary({ requestResponse: { request, response }, className = ''}: RequestSummaryProps) { | 17 | function calcBadgeVariant(statusCode: number | undefined): string { |
| 17 | const statusCode = response?.status ?? 0 | 18 | if (statusCode === undefined) { |
| 19 | return 'secondary'; | ||
| 20 | } else if (isBetween(statusCode, 100, 199)) { | ||
| 21 | return 'info'; | ||
| 22 | } else if (isBetween(statusCode, 200, 299)) { | ||
| 23 | return 'success'; | ||
| 24 | } else if (isBetween(statusCode, 300, 399)) { | ||
| 25 | return 'primary'; | ||
| 26 | } else if (isBetween(statusCode, 400, 499)) { | ||
| 27 | return 'danger'; | ||
| 28 | } else if (isBetween(statusCode, 500, 599)) { | ||
| 29 | return 'warning'; | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | export default function RequestSummary({ requestResponse: { request, response }, selected = false }: RequestSummaryProps) { | ||
| 18 | return ( | 34 | return ( |
| 19 | <div className={classNames(styles.requestSummary, className)}> | 35 | <Row> |
| 20 | <span className={styles.method}>{ request.method }</span> | 36 | <Col className='flex-grow-0 d-flex align-items-center'>{request.method}</Col> |
| 21 | <p>{ request.path }</p> | 37 | <Col className='flex-grow-1'>{ request.path }</Col> |
| 22 | <span className={classNames(styles.statusCode, { | 38 | <Col className='flex-grow-0 d-flex align-items-center'> |
| 23 | [styles.info]: isBetween(statusCode, 100, 199), | 39 | <Badge |
| 24 | [styles.success]: isBetween(statusCode, 200, 299), | 40 | className={classNames({ |
| 25 | [styles.redirect]: isBetween(statusCode, 300, 399), | 41 | 'border': selected, |
| 26 | [styles.clientError]: isBetween(statusCode, 400, 499), | 42 | })} |
| 27 | [styles.serverError]: isBetween(statusCode, 500, 599) | 43 | bg={calcBadgeVariant(response?.status)} |
| 28 | })}> | 44 | > |
| 29 | { response?.status ?? 'Loading...'} | 45 | { response?.status ?? 'Loading...'} |
| 30 | </span> | 46 | </Badge> |
| 31 | </div> | 47 | </Col> |
| 48 | </Row> | ||
| 32 | ) | 49 | ) |
| 33 | } | 50 | } |
diff --git a/src/index.scss b/src/index.scss index ef8fd7f..3d8dd7a 100644 --- a/src/index.scss +++ b/src/index.scss | |||
| @@ -1,16 +1,14 @@ | |||
| 1 | @import "reset"; | ||
| 2 | |||
| 3 | * { | 1 | * { |
| 4 | box-sizing: border-box; | 2 | box-sizing: border-box; |
| 5 | } | 3 | } |
| 6 | 4 | ||
| 7 | html, body, #root { | 5 | html, body, #root { |
| 8 | margin: 0; | ||
| 9 | padding: 0; | ||
| 10 | |||
| 11 | height: 100vh; | 6 | height: 100vh; |
| 12 | width: 100vw; | 7 | width: 100vw; |
| 13 | 8 | ||
| 14 | font-family: sans-serif; | 9 | } |
| 15 | overflow: hidden; | 10 | |
| 11 | #root { | ||
| 12 | display: flex; | ||
| 13 | flex-flow: column nowrap; | ||
| 16 | } | 14 | } |
diff --git a/src/index.tsx b/src/index.tsx index a6c4952..c381d4a 100644 --- a/src/index.tsx +++ b/src/index.tsx | |||
| @@ -2,6 +2,8 @@ import * as React from "react"; | |||
| 2 | import * as ReactDOM from "react-dom"; | 2 | import * as ReactDOM from "react-dom"; |
| 3 | import App from "./components/App/App"; | 3 | import App from "./components/App/App"; |
| 4 | 4 | ||
| 5 | import 'bootstrap/dist/css/bootstrap.min.css'; | ||
| 6 | |||
| 5 | import './index.scss'; | 7 | import './index.scss'; |
| 6 | 8 | ||
| 7 | ReactDOM.render(<App />, document.getElementById("root")); | 9 | ReactDOM.render(<App />, document.getElementById("root")); |
