From fe1ddabda3dd8a363151e8ddd0bed50efd36095e Mon Sep 17 00:00:00 2001 From: David Stotijn Date: Mon, 21 Sep 2020 21:46:44 +0200 Subject: [PATCH] Add `Layout` component, syntax highlighting and tidy up --- admin/src/components/Layout.tsx | 196 ++++++++++++++++++ .../src/components/reqlog/HttpStatusCode.tsx | 19 ++ admin/src/components/reqlog/LogDetail.tsx | 10 +- admin/src/components/reqlog/LogsOverview.tsx | 38 ++++ admin/src/components/reqlog/RequestDetail.tsx | 13 +- admin/src/components/reqlog/RequestList.tsx | 56 ++++- .../src/components/reqlog/ResponseDetail.tsx | 36 ++-- admin/src/pages/proxy/logs.tsx | 15 +- 8 files changed, 338 insertions(+), 45 deletions(-) create mode 100644 admin/src/components/Layout.tsx create mode 100644 admin/src/components/reqlog/HttpStatusCode.tsx create mode 100644 admin/src/components/reqlog/LogsOverview.tsx diff --git a/admin/src/components/Layout.tsx b/admin/src/components/Layout.tsx new file mode 100644 index 0000000..fa60702 --- /dev/null +++ b/admin/src/components/Layout.tsx @@ -0,0 +1,196 @@ +import React from "react"; +import { + makeStyles, + Theme, + createStyles, + useTheme, + AppBar, + Toolbar, + IconButton, + Typography, + Drawer, + Divider, + List, + ListItem, + ListItemIcon, + ListItemText, + Box, + Tooltip, +} from "@material-ui/core"; +import MenuIcon from "@material-ui/icons/Menu"; +import HomeIcon from "@material-ui/icons/Home"; +import SettingsEthernetIcon from "@material-ui/icons/SettingsEthernet"; +import ChevronLeftIcon from "@material-ui/icons/ChevronLeft"; +import ChevronRightIcon from "@material-ui/icons/ChevronRight"; +import clsx from "clsx"; + +const drawerWidth = 240; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: "flex", + width: "100%", + }, + appBar: { + zIndex: theme.zIndex.drawer + 1, + transition: theme.transitions.create(["width", "margin"], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + appBarShift: { + marginLeft: drawerWidth, + width: `calc(100% - ${drawerWidth}px)`, + transition: theme.transitions.create(["width", "margin"], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }, + menuButton: { + marginRight: 36, + }, + hide: { + display: "none", + }, + drawer: { + width: drawerWidth, + flexShrink: 0, + whiteSpace: "nowrap", + }, + drawerOpen: { + width: drawerWidth, + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }, + drawerClose: { + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + overflowX: "hidden", + width: theme.spacing(7) + 1, + [theme.breakpoints.up("sm")]: { + width: theme.spacing(7) + 8, + }, + }, + toolbar: { + display: "flex", + alignItems: "center", + justifyContent: "flex-end", + padding: theme.spacing(0, 1), + // necessary for content to be below app bar + ...theme.mixins.toolbar, + }, + content: { + flexGrow: 1, + padding: theme.spacing(2), + }, + listItem: { + paddingLeft: 16, + paddingRight: 16, + [theme.breakpoints.up("sm")]: { + paddingLeft: 20, + paddingRight: 20, + }, + }, + listItemIcon: { + minWidth: 42, + }, + }) +); + +export function Layout(props: { children: React.ReactNode }): JSX.Element { + const classes = useStyles(); + const theme = useTheme(); + const [open, setOpen] = React.useState(false); + + const handleDrawerOpen = () => { + setOpen(true); + }; + + const handleDrawerClose = () => { + setOpen(false); + }; + + return ( +
+ + + + + + + + 🥥 + {" "} + Coco + + + + +
+ + {theme.direction === "rtl" ? ( + + ) : ( + + )} + +
+ + + + + + + + + + + + + + + + + + + +
+
+
+ {props.children} +
+
+ ); +} + +export default Layout; diff --git a/admin/src/components/reqlog/HttpStatusCode.tsx b/admin/src/components/reqlog/HttpStatusCode.tsx new file mode 100644 index 0000000..4155d90 --- /dev/null +++ b/admin/src/components/reqlog/HttpStatusCode.tsx @@ -0,0 +1,19 @@ +import { green, orange, red } from "@material-ui/core/colors"; +import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord"; + +function HttpStatusIcon({ status }: { status: number }): JSX.Element { + const style = { marginTop: "-.25rem", verticalAlign: "middle" }; + switch (Math.floor(status / 100)) { + case 2: + case 3: + return ; + case 4: + return ; + case 5: + return ; + default: + return ; + } +} + +export default HttpStatusIcon; diff --git a/admin/src/components/reqlog/LogDetail.tsx b/admin/src/components/reqlog/LogDetail.tsx index 7abd784..db4dae0 100644 --- a/admin/src/components/reqlog/LogDetail.tsx +++ b/admin/src/components/reqlog/LogDetail.tsx @@ -39,14 +39,16 @@ function LogDetail({ requestId: id }: Props): JSX.Element {
- + - - - + {response && ( + + + + )}
diff --git a/admin/src/components/reqlog/LogsOverview.tsx b/admin/src/components/reqlog/LogsOverview.tsx new file mode 100644 index 0000000..5c53d79 --- /dev/null +++ b/admin/src/components/reqlog/LogsOverview.tsx @@ -0,0 +1,38 @@ +import { useState } from "react"; +import { Box, Paper, Container, Typography } from "@material-ui/core"; + +import RequestList from "./RequestList"; +import LogDetail from "./LogDetail"; + +function LogsOverview(): JSX.Element { + const [detailReqLogId, setDetailReqLogId] = useState(null); + + const handleLogClick = (reqId: string) => setDetailReqLogId(reqId); + + return ( + + + + + + {detailReqLogId ? ( + + ) : ( + + Select a log entry… + + )} + + + ); +} + +export default LogsOverview; diff --git a/admin/src/components/reqlog/RequestDetail.tsx b/admin/src/components/reqlog/RequestDetail.tsx index 057bdc0..3f5334f 100644 --- a/admin/src/components/reqlog/RequestDetail.tsx +++ b/admin/src/components/reqlog/RequestDetail.tsx @@ -11,11 +11,20 @@ interface Props { } function RequestDetail({ request }: Props): JSX.Element { + const { method, url, body } = request; + + const parsedUrl = new URL(url); + console.log(parsedUrl); + return (
- - {request.method} {request.url} + + {request.method}{" "} + {decodeURIComponent(parsedUrl.pathname + parsedUrl.search)} diff --git a/admin/src/components/reqlog/RequestList.tsx b/admin/src/components/reqlog/RequestList.tsx index 1a8f95d..49b8b79 100644 --- a/admin/src/components/reqlog/RequestList.tsx +++ b/admin/src/components/reqlog/RequestList.tsx @@ -7,9 +7,11 @@ import { TableRow, TableCell, TableBody, - makeStyles, + Typography, } from "@material-ui/core"; +import HttpStatusIcon from "./HttpStatusCode"; + const HTTP_REQUEST_LOGS = gql` query HttpRequestLogs { httpRequestLogs { @@ -17,6 +19,10 @@ const HTTP_REQUEST_LOGS = gql` method url timestamp + response { + status + statusCode + } } } `; @@ -34,21 +40,51 @@ function RequestList({ onLogClick }: Props): JSX.Element { const { httpRequestLogs: logs } = data; return ( - - + +
Method - URL + Origin + Path + Status - {logs.map(({ id, method, url }) => ( - onLogClick(id)}> - {method} - {url} - - ))} + {logs.map(({ id, method, url, response }) => { + const { origin, pathname, search, hash } = new URL(url); + + const cellStyle = { + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + } as any; + + return ( + onLogClick(id)}> + + {method} + + + {origin} + + + {decodeURIComponent(pathname + search + hash)} + + + {response && ( +
+ {" "} + {response.status} +
+ )} +
+
+ ); + })}
diff --git a/admin/src/components/reqlog/ResponseDetail.tsx b/admin/src/components/reqlog/ResponseDetail.tsx index b902ec6..1f862c6 100644 --- a/admin/src/components/reqlog/ResponseDetail.tsx +++ b/admin/src/components/reqlog/ResponseDetail.tsx @@ -1,9 +1,9 @@ import { Typography, Box } from "@material-ui/core"; -import { green } from "@material-ui/core/colors"; -import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { materialLight } from "react-syntax-highlighter/dist/cjs/styles/prism"; +import HttpStatusIcon from "./HttpStatusCode"; + interface Props { response: { proto: string; @@ -17,15 +17,28 @@ function ResponseDetail({ response }: Props): JSX.Element { return (
- - {statusIcon(response.statusCode)} {response.proto} {response.status} + + {response.proto}{" "} + {response.status} {response.body} @@ -34,19 +47,4 @@ function ResponseDetail({ response }: Props): JSX.Element { ); } -function statusIcon(status: number): JSX.Element { - const style = { marginTop: ".2rem", verticalAlign: "top" }; - switch (Math.floor(status / 100)) { - case 2: - case 3: - return ; - case 4: - return ; - case 5: - return ; - default: - return ; - } -} - export default ResponseDetail; diff --git a/admin/src/pages/proxy/logs.tsx b/admin/src/pages/proxy/logs.tsx index 0958c48..2fe3de9 100644 --- a/admin/src/pages/proxy/logs.tsx +++ b/admin/src/pages/proxy/logs.tsx @@ -3,19 +3,14 @@ import { Box } from "@material-ui/core"; import RequestList from "../../components/reqlog/RequestList"; import LogDetail from "../../components/reqlog/LogDetail"; +import LogsOverview from "../../components/reqlog/LogsOverview"; +import Layout from "../../components/Layout"; function Logs(): JSX.Element { - const [detailReqLogId, setDetailReqLogId] = useState(); - - const handleLogClick = (reqId: string) => setDetailReqLogId(reqId); - return ( -
- - - - {detailReqLogId && } -
+ + + ); }