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 && }
-
+
+
+
);
}