diff --git a/.goreleaser.yml b/.goreleaser.yml
index f557dad..eaee776 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -1,7 +1,7 @@
before:
hooks:
- make clean
- - make build-admin
+ - sh -c "NEXT_PUBLIC_VERSION={{ .Version}} make build-admin"
- go mod tidy
builds:
diff --git a/admin/.eslintrc.json b/admin/.eslintrc.json
new file mode 100644
index 0000000..dca4964
--- /dev/null
+++ b/admin/.eslintrc.json
@@ -0,0 +1,6 @@
+{
+ "extends": "next/core-web-vitals",
+ "rules": {
+ "@next/next/no-css-tags": "off"
+ }
+}
diff --git a/admin/.prettierignore b/admin/.prettierignore
new file mode 100644
index 0000000..3b67566
--- /dev/null
+++ b/admin/.prettierignore
@@ -0,0 +1,4 @@
+/.next/
+/out/
+/build
+/coverage
diff --git a/admin/.prettierrc.json b/admin/.prettierrc.json
new file mode 100644
index 0000000..9fcfde7
--- /dev/null
+++ b/admin/.prettierrc.json
@@ -0,0 +1,3 @@
+{
+ "printWidth": 120
+}
diff --git a/admin/next-env.d.ts b/admin/next-env.d.ts
index 7b7aa2c..4f11a03 100644
--- a/admin/next-env.d.ts
+++ b/admin/next-env.d.ts
@@ -1,2 +1,5 @@
///
-///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/admin/next.config.js b/admin/next.config.js
index 0898b94..e806394 100644
--- a/admin/next.config.js
+++ b/admin/next.config.js
@@ -1,7 +1,10 @@
-const withCSS = require("@zeit/next-css");
-const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
+// @ts-check
-module.exports = withCSS({
+/**
+ * @type {import('next').NextConfig}
+ **/
+const nextConfig = {
+ reactStrictMode: true,
trailingSlash: true,
async rewrites() {
return [
@@ -11,24 +14,6 @@ module.exports = withCSS({
},
];
},
- webpack: (config) => {
- config.module.rules.push({
- test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
- use: {
- loader: "url-loader",
- options: {
- limit: 100000,
- },
- },
- });
+};
- config.plugins.push(
- new MonacoWebpackPlugin({
- languages: ["html", "json", "javascript"],
- filename: "static/[name].worker.js",
- })
- );
-
- return config;
- },
-});
+module.exports = nextConfig;
diff --git a/admin/package.json b/admin/package.json
index 5c324c9..5b3f2f9 100644
--- a/admin/package.json
+++ b/admin/package.json
@@ -6,31 +6,38 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
+ "lint": "next lint",
"export": "next build && next export -o dist"
},
"dependencies": {
"@apollo/client": "^3.2.0",
- "@material-ui/core": "^4.11.0",
- "@material-ui/icons": "^4.9.1",
- "@material-ui/lab": "^4.0.0-alpha.56",
- "@zeit/next-css": "^1.0.1",
- "graphql": "^15.3.0",
- "monaco-editor": "^0.20.0",
- "monaco-editor-webpack-plugin": "^1.9.0",
- "next": "^9.5.4",
+ "@emotion/react": "^11.7.1",
+ "@emotion/server": "^11.4.0",
+ "@emotion/styled": "^11.6.0",
+ "@monaco-editor/react": "^4.3.1",
+ "@mui/icons-material": "^5.3.1",
+ "@mui/lab": "^5.0.0-alpha.66",
+ "@mui/material": "^5.3.1",
+ "deepmerge": "^4.2.2",
+ "graphql": "^16.2.0",
+ "lodash": "^4.17.21",
+ "monaco-editor": "^0.31.1",
+ "next": "^12.0.8",
"next-fonts": "^1.0.3",
- "react": "^16.13.1",
- "react-dom": "^16.13.1",
- "react-monaco-editor": "^0.34.0",
- "react-syntax-highlighter": "^13.5.3",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2",
"typescript": "^4.0.3"
},
"devDependencies": {
- "@types/node": "^14.11.1",
- "@types/react": "^16.9.49",
- "eslint": "^7.9.0",
- "eslint-config-prettier": "^6.11.0",
- "eslint-plugin-prettier": "^3.1.4",
- "prettier": "^2.1.2"
+ "@babel/core": "^7.0.0",
+ "@types/lodash": "^4.14.178",
+ "@types/node": "^17.0.12",
+ "@types/react": "^17.0.38",
+ "eslint": "^8.7.0",
+ "eslint-config-next": "12.0.8",
+ "eslint-config-prettier": "^8.3.0",
+ "eslint-plugin-prettier": "^4.0.0",
+ "prettier": "^2.1.2",
+ "webpack": "^5.67.0"
}
}
diff --git a/admin/src/components/CenteredPaper.tsx b/admin/src/components/CenteredPaper.tsx
index 3b4c1ea..2fa73b7 100644
--- a/admin/src/components/CenteredPaper.tsx
+++ b/admin/src/components/CenteredPaper.tsx
@@ -1,10 +1,6 @@
-import { Paper } from "@material-ui/core";
+import { Paper } from "@mui/material";
-function CenteredPaper({
- children,
-}: {
- children: React.ReactNode;
-}): JSX.Element {
+function CenteredPaper({ children }: { children: React.ReactNode }): JSX.Element {
return (
- 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: 28,
- },
- 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,
+const openedMixin = (theme: Theme): CSSObject => ({
+ width: drawerWidth,
+ transition: theme.transitions.create("width", {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
+ overflowX: "hidden",
+});
+
+const closedMixin = (theme: Theme): CSSObject => ({
+ transition: theme.transitions.create("width", {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.leavingScreen,
+ }),
+ overflowX: "hidden",
+ width: 56,
+});
+
+const DrawerHeader = styled("div")(({ theme }) => ({
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "flex-start",
+ padding: theme.spacing(0, 1),
+ // necessary for content to be below app bar
+ ...theme.mixins.toolbar,
+}));
+
+interface AppBarProps extends MuiAppBarProps {
+ open?: boolean;
+}
+
+const AppBar = styled(MuiAppBar, {
+ shouldForwardProp: (prop) => prop !== "open",
+})(({ theme, open }) => ({
+ backgroundColor: theme.palette.secondary.dark,
+ zIndex: theme.zIndex.drawer + 1,
+ transition: theme.transitions.create(["width", "margin"], {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.leavingScreen,
+ }),
+ ...(open && {
+ marginLeft: drawerWidth,
+ width: `calc(100% - ${drawerWidth}px)`,
+ transition: theme.transitions.create(["width", "margin"], {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
+ }),
+}));
+
+const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== "open" })(({ theme, open }) => ({
+ width: drawerWidth,
+ flexShrink: 0,
+ whiteSpace: "nowrap",
+ boxSizing: "border-box",
+ ...(open && {
+ ...openedMixin(theme),
+ "& .MuiDrawer-paper": openedMixin(theme),
+ }),
+ ...(!open && {
+ ...closedMixin(theme),
+ "& .MuiDrawer-paper": closedMixin(theme),
+ }),
+}));
+
+const ListItemButton = styled(MuiListItemButton)(({ theme }) => ({
+ [theme.breakpoints.up("sm")]: {
+ px: 1,
+ },
+ "&.MuiListItemButton-root": {
+ "&.Mui-selected": {
+ backgroundColor: theme.palette.primary.main,
+ "& .MuiListItemIcon-root": {
+ color: theme.palette.secondary.dark,
+ },
+ "& .MuiListItemText-root": {
+ color: theme.palette.secondary.dark,
},
},
- 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(3),
- },
- listItem: {
- paddingLeft: 16,
- paddingRight: 16,
- [theme.breakpoints.up("sm")]: {
- paddingLeft: 20,
- paddingRight: 20,
- },
- },
- listItemIcon: {
- minWidth: 42,
- },
- titleHighlight: {
- color: theme.palette.secondary.main,
- marginRight: 4,
- },
- })
-);
+ },
+}));
+
+const ListItemIcon = styled(MuiListItemIcon)(() => ({
+ minWidth: 42,
+}));
interface Props {
children: React.ReactNode;
@@ -126,7 +132,6 @@ interface Props {
}
export function Layout({ title, page, children }: Props): JSX.Element {
- const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = React.useState(false);
@@ -138,145 +143,109 @@ export function Layout({ title, page, children }: Props): JSX.Element {
setOpen(false);
};
+ const SiteTitle = styled("span")({
+ ...(title !== "" && {
+ color: theme.palette.primary.main,
+ marginRight: 4,
+ }),
+ });
+
return (
-
-
+
+
-
-
- Hetty://
-
- {title}
-
+
+
+ Hetty://
+ {title}
+
+ v{process.env.NEXT_PUBLIC_VERSION || "0.0"}
+
-
-
+
+
- {theme.direction === "rtl" ? (
-
- ) : (
-
- )}
+ {theme.direction === "rtl" ? : }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
{children}
-
-
+
+
);
}
diff --git a/admin/src/components/projects/NewProject.tsx b/admin/src/components/projects/NewProject.tsx
index 8c0a793..f61c832 100644
--- a/admin/src/components/projects/NewProject.tsx
+++ b/admin/src/components/projects/NewProject.tsx
@@ -1,29 +1,8 @@
import { gql, useMutation } from "@apollo/client";
-import {
- Box,
- Button,
- CircularProgress,
- createStyles,
- makeStyles,
- TextField,
- Theme,
- Typography,
-} from "@material-ui/core";
-import AddIcon from "@material-ui/icons/Add";
+import { Box, Button, CircularProgress, TextField, Typography } from "@mui/material";
+import AddIcon from "@mui/icons-material/Add";
import React, { useState } from "react";
-const useStyles = makeStyles((theme: Theme) =>
- createStyles({
- projectName: {
- marginTop: -6,
- marginRight: theme.spacing(2),
- },
- button: {
- marginRight: theme.spacing(2),
- },
- })
-);
-
const CREATE_PROJECT = gql`
mutation CreateProject($name: String!) {
createProject(name: $name) {
@@ -44,21 +23,17 @@ const OPEN_PROJECT = gql`
`;
function NewProject(): JSX.Element {
- const classes = useStyles();
- const [input, setInput] = useState(null);
+ const [name, setName] = useState("");
const [createProject, { error: createProjErr, loading: createProjLoading }] = useMutation(CREATE_PROJECT, {
- onError: () => { },
+ onError: () => {},
onCompleted(data) {
- input.value = "";
+ setName("");
openProject({ variables: { id: data.createProject.id } });
},
});
const [openProject, { error: openProjErr, loading: openProjLoading }] = useMutation(OPEN_PROJECT, {
- onError: () => { },
- onCompleted() {
- input.value = "";
- },
+ onError: () => {},
update(cache, { data: { openProject } }) {
cache.modify({
fields: {
@@ -99,7 +74,7 @@ function NewProject(): JSX.Element {
const handleCreateAndOpenProjectForm = (e: React.SyntheticEvent) => {
e.preventDefault();
- createProject({ variables: { name: input.value } });
+ createProject({ variables: { name } });
};
return (
@@ -109,25 +84,26 @@ function NewProject(): JSX.Element {
)}
{projData?.projects.length === 0 && (
-
- There are no projects. Create one to get started.
-
+
There are no projects. Create one to get started.
)}
);
diff --git a/admin/src/components/reqlog/ConfirmationDialog.tsx b/admin/src/components/reqlog/ConfirmationDialog.tsx
index 0c65398..ad31179 100644
--- a/admin/src/components/reqlog/ConfirmationDialog.tsx
+++ b/admin/src/components/reqlog/ConfirmationDialog.tsx
@@ -1,10 +1,10 @@
import React, { useState } from "react";
-import Button from "@material-ui/core/Button";
-import Dialog from "@material-ui/core/Dialog";
-import DialogActions from "@material-ui/core/DialogActions";
-import DialogContent from "@material-ui/core/DialogContent";
-import DialogContentText from "@material-ui/core/DialogContentText";
-import DialogTitle from "@material-ui/core/DialogTitle";
+import Button from "@mui/material/Button";
+import Dialog from "@mui/material/Dialog";
+import DialogActions from "@mui/material/DialogActions";
+import DialogContent from "@mui/material/DialogContent";
+import DialogContentText from "@mui/material/DialogContentText";
+import DialogTitle from "@mui/material/DialogTitle";
export function useConfirmationDialog() {
const [isOpen, setIsOpen] = useState(false);
@@ -38,12 +38,10 @@ export function ConfirmationDialog(props: ConfirmationDialog) {
>
Are you sure?
-
- {children}
-
+ {children}
-
+
diff --git a/admin/src/components/reqlog/Editor.tsx b/admin/src/components/reqlog/Editor.tsx
index b72fefe..fd090e3 100644
--- a/admin/src/components/reqlog/Editor.tsx
+++ b/admin/src/components/reqlog/Editor.tsx
@@ -1,7 +1,7 @@
-import dynamic from "next/dynamic";
-const MonacoEditor = dynamic(import("react-monaco-editor"), { ssr: false });
+import MonacoEditor from "@monaco-editor/react";
+import monaco from "monaco-editor/esm/vs/editor/editor.api";
-const monacoOptions = {
+const monacoOptions: monaco.editor.IEditorOptions = {
readOnly: true,
wordWrap: "on",
minimap: {
@@ -11,20 +11,7 @@ const monacoOptions = {
type language = "html" | "typescript" | "json";
-function editorDidMount() {
- return ((window as any).MonacoEnvironment.getWorkerUrl = (
- moduleId,
- label
- ) => {
- if (label === "json") return "/_next/static/json.worker.js";
- if (label === "html") return "/_next/static/html.worker.js";
- if (label === "javascript") return "/_next/static/ts.worker.js";
-
- return "/_next/static/editor.worker.js";
- });
-}
-
-function languageForContentType(contentType: string): language {
+function languageForContentType(contentType?: string): language | undefined {
switch (contentType) {
case "text/html":
return "html";
@@ -41,7 +28,7 @@ function languageForContentType(contentType: string): language {
interface Props {
content: string;
- contentType: string;
+ contentType?: string;
}
function Editor({ content, contentType }: Props): JSX.Element {
@@ -50,8 +37,7 @@ function Editor({ content, contentType }: Props): JSX.Element {
height={"600px"}
language={languageForContentType(contentType)}
theme="vs-dark"
- editorDidMount={editorDidMount}
- options={monacoOptions as any}
+ options={monacoOptions}
value={content}
/>
);
diff --git a/admin/src/components/reqlog/HttpHeadersTable.tsx b/admin/src/components/reqlog/HttpHeadersTable.tsx
index 314ec29..dc280fa 100644
--- a/admin/src/components/reqlog/HttpHeadersTable.tsx
+++ b/admin/src/components/reqlog/HttpHeadersTable.tsx
@@ -1,83 +1,66 @@
-import {
- makeStyles,
- Theme,
- createStyles,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableRow,
- Snackbar,
-} from "@material-ui/core";
-import { Alert } from "@material-ui/lab";
+import { Table, TableBody, TableCell, TableContainer, TableRow, Snackbar } from "@mui/material";
+import { Alert } from "@mui/lab";
import React, { useState } from "react";
-const useStyles = makeStyles((theme: Theme) => {
- const paddingX = 0;
- const paddingY = theme.spacing(1) / 3;
- const tableCell = {
- paddingLeft: paddingX,
- paddingRight: paddingX,
- paddingTop: paddingY,
- paddingBottom: paddingY,
- verticalAlign: "top",
- border: "none",
- whiteSpace: "nowrap" as any,
- overflow: "hidden",
- textOverflow: "ellipsis",
- "&:hover": {
- color: theme.palette.secondary.main,
- whiteSpace: "inherit" as any,
- overflow: "inherit",
- textOverflow: "inherit",
- cursor: "copy",
- },
- };
- return createStyles({
- root: {},
- table: {
- tableLayout: "fixed",
- width: "100%",
- },
- keyCell: {
- ...tableCell,
- paddingRight: theme.spacing(1),
- width: "40%",
- fontWeight: "bold",
- fontSize: ".75rem",
- },
- valueCell: {
- ...tableCell,
- width: "60%",
- border: "none",
- fontSize: ".75rem",
- },
- });
-});
+const baseCellStyle = {
+ px: 0,
+ py: 0.33,
+ verticalAlign: "top",
+ border: "none",
+ whiteSpace: "nowrap" as any,
+ overflow: "hidden",
+ textOverflow: "ellipsis",
+ "&:hover": {
+ color: "primary.main",
+ whiteSpace: "inherit" as any,
+ overflow: "inherit",
+ textOverflow: "inherit",
+ cursor: "copy",
+ },
+};
+
+const keyCellStyle = {
+ ...baseCellStyle,
+ pr: 1,
+ width: "40%",
+ fontWeight: "bold",
+ fontSize: ".75rem",
+};
+
+const valueCellStyle = {
+ ...baseCellStyle,
+ width: "60%",
+ border: "none",
+ fontSize: ".75rem",
+};
interface Props {
headers: Array<{ key: string; value: string }>;
}
function HttpHeadersTable({ headers }: Props): JSX.Element {
- const classes = useStyles();
-
const [open, setOpen] = useState(false);
const handleClick = (e: React.MouseEvent) => {
e.preventDefault();
+ const windowSel = window.getSelection();
+
+ if (!windowSel || !document) {
+ return;
+ }
+
const r = document.createRange();
r.selectNode(e.currentTarget);
- window.getSelection().removeAllRanges();
- window.getSelection().addRange(r);
+ windowSel.removeAllRanges();
+ windowSel.addRange(r);
document.execCommand("copy");
- window.getSelection().removeAllRanges();
+ windowSel.removeAllRanges();
setOpen(true);
};
- const handleClose = (event?: React.SyntheticEvent, reason?: string) => {
+ const handleClose = (event: Event | React.SyntheticEvent, reason?: string) => {
if (reason === "clickaway") {
return;
}
@@ -92,20 +75,21 @@ function HttpHeadersTable({ headers }: Props): JSX.Element {
Copied to clipboard.
-
-
+
+
{headers.map(({ key, value }, index) => (
-
+
{key}:
-
+
{value}
diff --git a/admin/src/components/reqlog/HttpStatusCode.tsx b/admin/src/components/reqlog/HttpStatusCode.tsx
index 9f708a5..417acac 100644
--- a/admin/src/components/reqlog/HttpStatusCode.tsx
+++ b/admin/src/components/reqlog/HttpStatusCode.tsx
@@ -1,31 +1,25 @@
-import { Theme, withTheme } from "@material-ui/core";
-import { orange, red } from "@material-ui/core/colors";
-import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
+import { SvgIconTypeMap } from "@mui/material";
+import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
interface Props {
status: number;
- theme: Theme;
}
-function HttpStatusIcon({ status, theme }: Props): JSX.Element {
- const style = { marginTop: "-.25rem", verticalAlign: "middle" };
+export default function HttpStatusIcon({ status }: Props): JSX.Element {
+ let color: SvgIconTypeMap["props"]["color"] = "inherit";
+
switch (Math.floor(status / 100)) {
case 2:
case 3:
- return (
-
- );
+ color = "primary";
+ break;
case 4:
- return (
-
- );
+ color = "warning";
+ break;
case 5:
- return ;
- default:
- return ;
+ color = "error";
+ break;
}
-}
-export default withTheme(HttpStatusIcon);
+ return ;
+}
diff --git a/admin/src/components/reqlog/LogDetail.tsx b/admin/src/components/reqlog/LogDetail.tsx
index 2491420..dc43fbe 100644
--- a/admin/src/components/reqlog/LogDetail.tsx
+++ b/admin/src/components/reqlog/LogDetail.tsx
@@ -1,9 +1,9 @@
import { gql, useQuery } from "@apollo/client";
-import { Box, Grid, Paper, CircularProgress } from "@material-ui/core";
+import { Box, Grid, Paper, CircularProgress } from "@mui/material";
import ResponseDetail from "./ResponseDetail";
import RequestDetail from "./RequestDetail";
-import Alert from "@material-ui/lab/Alert";
+import Alert from "@mui/lab/Alert";
const HTTP_REQUEST_LOG = gql`
query HttpRequestLog($id: ID!) {
@@ -44,11 +44,7 @@ function LogDetail({ requestId: id }: Props): JSX.Element {
return ;
}
if (error) {
- return (
-
- Error fetching logs details: {error.message}
-
- );
+ return Error fetching logs details: {error.message};
}
if (!data.httpRequestLog) {
diff --git a/admin/src/components/reqlog/LogsOverview.tsx b/admin/src/components/reqlog/LogsOverview.tsx
index e249732..0e29405 100644
--- a/admin/src/components/reqlog/LogsOverview.tsx
+++ b/admin/src/components/reqlog/LogsOverview.tsx
@@ -1,12 +1,7 @@
import { useRouter } from "next/router";
import Link from "next/link";
-import {
- Box,
- CircularProgress,
- Link as MaterialLink,
- Typography,
-} from "@material-ui/core";
-import Alert from "@material-ui/lab/Alert";
+import { Box, CircularProgress, Link as MaterialLink, Typography } from "@mui/material";
+import Alert from "@mui/lab/Alert";
import RequestList from "./RequestList";
import LogDetail from "./LogDetail";
@@ -33,7 +28,7 @@ function LogsOverview(): JSX.Element {
There is no project active.{" "}
- Create or open
+ Create or open
{" "}
one first.
@@ -47,11 +42,7 @@ function LogsOverview(): JSX.Element {
return (
-
+
{detailReqLogId && }
diff --git a/admin/src/components/reqlog/RequestDetail.tsx b/admin/src/components/reqlog/RequestDetail.tsx
index 792592b..6a8d36d 100644
--- a/admin/src/components/reqlog/RequestDetail.tsx
+++ b/admin/src/components/reqlog/RequestDetail.tsx
@@ -1,42 +1,9 @@
import React from "react";
-import {
- Typography,
- Box,
- createStyles,
- makeStyles,
- Theme,
- Divider,
-} from "@material-ui/core";
+import { Typography, Box, Divider } from "@mui/material";
import HttpHeadersTable from "./HttpHeadersTable";
import Editor from "./Editor";
-const useStyles = makeStyles((theme: Theme) =>
- createStyles({
- requestTitle: {
- width: "calc(100% - 80px)",
- fontSize: "1rem",
- wordBreak: "break-all",
- whiteSpace: "pre-wrap",
- },
- headersTable: {
- tableLayout: "fixed",
- width: "100%",
- },
- headerKeyCell: {
- verticalAlign: "top",
- width: "30%",
- fontWeight: "bold",
- },
- headerValueCell: {
- width: "70%",
- verticalAlign: "top",
- wordBreak: "break-all",
- whiteSpace: "pre-wrap",
- },
- })
-);
-
interface Props {
request: {
method: string;
@@ -49,29 +16,27 @@ interface Props {
function RequestDetail({ request }: Props): JSX.Element {
const { method, url, proto, headers, body } = request;
- const classes = useStyles();
- const contentType = headers.find((header) => header.key === "Content-Type")
- ?.value;
+ const contentType = headers.find((header) => header.key === "Content-Type")?.value;
const parsedUrl = new URL(url);
return (
-
+
Request
-
+
{method} {decodeURIComponent(parsedUrl.pathname + parsedUrl.search)}{" "}
-
+
{proto}
diff --git a/admin/src/components/reqlog/RequestList.tsx b/admin/src/components/reqlog/RequestList.tsx
index 3fcb30b..c72c760 100644
--- a/admin/src/components/reqlog/RequestList.tsx
+++ b/admin/src/components/reqlog/RequestList.tsx
@@ -8,48 +8,23 @@ import {
TableBody,
Typography,
Box,
- createStyles,
- makeStyles,
- Theme,
- withTheme,
-} from "@material-ui/core";
+ useTheme,
+} from "@mui/material";
import HttpStatusIcon from "./HttpStatusCode";
import CenteredPaper from "../CenteredPaper";
-
-const useStyles = makeStyles((theme: Theme) =>
- createStyles({
- row: {
- "&:hover": {
- cursor: "pointer",
- },
- },
- /* Pseudo-class applied to the root element if `hover={true}`. */
- hover: {},
- })
-);
+import { RequestLog } from "../../lib/requestLogs";
interface Props {
- logs: Array;
+ logs: RequestLog[];
selectedReqLogId?: string;
onLogClick(requestId: string): void;
- theme: Theme;
}
-function RequestList({
- logs,
- onLogClick,
- selectedReqLogId,
- theme,
-}: Props): JSX.Element {
+export default function RequestList({ logs, onLogClick, selectedReqLogId }: Props): JSX.Element {
return (
-
+
{logs.length === 0 && (
@@ -62,19 +37,14 @@ function RequestList({
}
interface RequestListTableProps {
- logs?: any;
+ logs: RequestLog[];
selectedReqLogId?: string;
onLogClick(requestId: string): void;
- theme: Theme;
}
-function RequestListTable({
- logs,
- selectedReqLogId,
- onLogClick,
- theme,
-}: RequestListTableProps): JSX.Element {
- const classes = useStyles();
+function RequestListTable({ logs, selectedReqLogId, onLogClick }: RequestListTableProps): JSX.Element {
+ const theme = useTheme();
+
return (
onLogClick(id)}
>
{method}
-
- {origin}
-
-
+ {origin}
+
{decodeURIComponent(pathname + search + hash)}
@@ -142,5 +111,3 @@ function RequestListTable({
);
}
-
-export default withTheme(RequestList);
diff --git a/admin/src/components/reqlog/ResponseDetail.tsx b/admin/src/components/reqlog/ResponseDetail.tsx
index adcff7a..7b7238d 100644
--- a/admin/src/components/reqlog/ResponseDetail.tsx
+++ b/admin/src/components/reqlog/ResponseDetail.tsx
@@ -1,4 +1,4 @@
-import { Typography, Box, Divider } from "@material-ui/core";
+import { Typography, Box, Divider } from "@mui/material";
import HttpStatusIcon from "./HttpStatusCode";
import Editor from "./Editor";
@@ -15,30 +15,17 @@ interface Props {
}
function ResponseDetail({ response }: Props): JSX.Element {
- const contentType = response.headers.find(
- (header) => header.key === "Content-Type"
- )?.value;
+ const contentType = response.headers.find((header) => header.key === "Content-Type")?.value;
return (
-
+
Response
-
+
{" "}
-
+
{response.proto}
{" "}
@@ -52,9 +39,7 @@ function ResponseDetail({ response }: Props): JSX.Element {
- {response.body && (
-
- )}
+ {response.body && }
);
}
diff --git a/admin/src/components/reqlog/Search.tsx b/admin/src/components/reqlog/Search.tsx
index df681a4..c4a0c47 100644
--- a/admin/src/components/reqlog/Search.tsx
+++ b/admin/src/components/reqlog/Search.tsx
@@ -3,29 +3,23 @@ import {
Checkbox,
CircularProgress,
ClickAwayListener,
- createStyles,
FormControlLabel,
InputBase,
- makeStyles,
Paper,
Popper,
- Theme,
Tooltip,
useTheme,
-} from "@material-ui/core";
-import IconButton from "@material-ui/core/IconButton";
-import SearchIcon from "@material-ui/icons/Search";
-import FilterListIcon from "@material-ui/icons/FilterList";
-import DeleteIcon from "@material-ui/icons/Delete";
+} from "@mui/material";
+import IconButton from "@mui/material/IconButton";
+import SearchIcon from "@mui/icons-material/Search";
+import FilterListIcon from "@mui/icons-material/FilterList";
+import DeleteIcon from "@mui/icons-material/Delete";
import React, { useRef, useState } from "react";
import { gql, useMutation, useQuery } from "@apollo/client";
import { withoutTypename } from "../../lib/omitTypename";
-import { Alert } from "@material-ui/lab";
+import { Alert } from "@mui/lab";
import { useClearHTTPRequestLog } from "./hooks/useClearHTTPRequestLog";
-import {
- ConfirmationDialog,
- useConfirmationDialog,
-} from "./ConfirmationDialog";
+import { ConfirmationDialog, useConfirmationDialog } from "./ConfirmationDialog";
const FILTER = gql`
query HttpRequestLogFilter {
@@ -45,79 +39,43 @@ const SET_FILTER = gql`
}
`;
-const useStyles = makeStyles((theme: Theme) =>
- createStyles({
- root: {
- padding: "2px 4px",
- display: "flex",
- alignItems: "center",
- width: 400,
- },
- input: {
- marginLeft: theme.spacing(1),
- flex: 1,
- },
- iconButton: {
- padding: 10,
- },
- filterPopper: {
- width: 400,
- marginTop: 6,
- zIndex: 99,
- },
- filterOptions: {
- padding: theme.spacing(2),
- },
- filterLoading: {
- marginRight: 1,
- color: theme.palette.text.primary,
- },
- })
-);
-
export interface SearchFilter {
onlyInScope: boolean;
searchExpression: string;
}
function Search(): JSX.Element {
- const classes = useStyles();
const theme = useTheme();
const [searchExpr, setSearchExpr] = useState("");
- const { loading: filterLoading, error: filterErr, data: filter } = useQuery(
- FILTER,
- {
- onCompleted: (data) => {
- setSearchExpr(data.httpRequestLogFilter?.searchExpression || "");
- },
- }
- );
+ const {
+ loading: filterLoading,
+ error: filterErr,
+ data: filter,
+ } = useQuery(FILTER, {
+ onCompleted: (data) => {
+ setSearchExpr(data.httpRequestLogFilter?.searchExpression || "");
+ },
+ });
- const [
- setFilterMutate,
- { error: setFilterErr, loading: setFilterLoading },
- ] = useMutation<{
+ const [setFilterMutate, { error: setFilterErr, loading: setFilterLoading }] = useMutation<{
setHttpRequestLogFilter: SearchFilter | null;
}>(SET_FILTER, {
- update(cache, { data: { setHttpRequestLogFilter } }) {
+ update(cache, { data }) {
cache.writeQuery({
query: FILTER,
data: {
- httpRequestLogFilter: setHttpRequestLogFilter,
+ httpRequestLogFilter: data?.setHttpRequestLogFilter,
},
});
},
onError: () => {},
});
- const [
- clearHTTPRequestLog,
- clearHTTPRequestLogResult,
- ] = useClearHTTPRequestLog();
+ const [clearHTTPRequestLog, clearHTTPRequestLogResult] = useClearHTTPRequestLog();
const clearHTTPConfirmationDialog = useConfirmationDialog();
- const filterRef = useRef();
+ const filterRef = useRef(null);
const [filterOpen, setFilterOpen] = useState(false);
const handleSubmit = (e: React.SyntheticEvent) => {
@@ -133,8 +91,8 @@ function Search(): JSX.Element {
e.preventDefault();
};
- const handleClickAway = (event: React.MouseEvent) => {
- if (filterRef.current.contains(event.target as HTMLElement)) {
+ const handleClickAway = (event: MouseEvent | TouchEvent) => {
+ if (filterRef?.current && filterRef.current.contains(event.target as HTMLElement)) {
return;
}
setFilterOpen(false);
@@ -144,63 +102,67 @@ function Search(): JSX.Element {
-
+
setFilterOpen(!filterOpen)}
- style={{
- color: filter?.httpRequestLogFilter?.onlyInScope
- ? theme.palette.secondary.main
- : "inherit",
+ sx={{
+ p: 1,
+ color: filter?.httpRequestLogFilter?.onlyInScope ? "primary.main" : "inherit",
}}
>
{filterLoading || setFilterLoading ? (
-
+
) : (
)}
setSearchExpr(e.target.value)}
onFocus={() => setFilterOpen(true)}
/>
-
+
-
+
setFilterMutate({
diff --git a/admin/src/components/scope/AddRule.tsx b/admin/src/components/scope/AddRule.tsx
index b27e90a..f86b309 100644
--- a/admin/src/components/scope/AddRule.tsx
+++ b/admin/src/components/scope/AddRule.tsx
@@ -3,20 +3,18 @@ import {
Box,
Button,
CircularProgress,
- createStyles,
FormControl,
FormControlLabel,
FormLabel,
- makeStyles,
Radio,
RadioGroup,
TextField,
- Theme,
-} from "@material-ui/core";
-import AddIcon from "@material-ui/icons/Add";
-import { Alert } from "@material-ui/lab";
+} from "@mui/material";
+import AddIcon from "@mui/icons-material/Add";
+import { Alert } from "@mui/lab";
import React from "react";
import { SCOPE } from "./Rules";
+import { ScopeRule } from "../../lib/scope";
const SET_SCOPE = gql`
mutation SetScope($scope: [ScopeRuleInput!]!) {
@@ -26,25 +24,15 @@ const SET_SCOPE = gql`
}
`;
-const useStyles = makeStyles((theme: Theme) =>
- createStyles({
- ruleExpression: {
- fontFamily: "'JetBrains Mono', monospace",
- },
- })
-);
-
function AddRule(): JSX.Element {
- const classes = useStyles();
-
const [ruleType, setRuleType] = React.useState("url");
- const [expression, setExpression] = React.useState(null);
+ const [expression, setExpression] = React.useState("");
const client = useApolloClient();
const [setScope, { error, loading }] = useMutation(SET_SCOPE, {
onError() {},
onCompleted() {
- expression.value = "";
+ setExpression("");
},
update(_, { data: { setScope } }) {
client.writeQuery({
@@ -59,21 +47,20 @@ function AddRule(): JSX.Element {
};
const handleSubmit = (e: React.SyntheticEvent) => {
e.preventDefault();
- let scope = [];
+ let scope: ScopeRule[] = [];
try {
- const data = client.readQuery({
+ const data = client.readQuery<{ scope: ScopeRule[] }>({
query: SCOPE,
});
- scope = data.scope;
+ if (data) {
+ scope = data.scope;
+ }
} catch (e) {}
setScope({
variables: {
- scope: [
- ...scope.map(({ url }) => ({ url })),
- { url: expression.value },
- ],
+ scope: [...scope.map(({ url }) => ({ url })), { url: expression }],
},
});
};
@@ -87,15 +74,10 @@ function AddRule(): JSX.Element {
)}