From aa8ddf4122421e579f0ff52a38228d462522df1c Mon Sep 17 00:00:00 2001 From: David Stotijn Date: Fri, 28 Jan 2022 20:20:15 +0100 Subject: [PATCH] Update Next.js, Material UI --- .goreleaser.yml | 2 +- admin/.eslintrc.json | 6 + admin/.prettierignore | 4 + admin/.prettierrc.json | 3 + admin/next-env.d.ts | 5 +- admin/next.config.js | 31 +- admin/package.json | 43 +- admin/src/components/CenteredPaper.tsx | 8 +- admin/src/components/Layout.tsx | 333 +- admin/src/components/projects/NewProject.tsx | 60 +- admin/src/components/projects/ProjectList.tsx | 287 +- .../components/reqlog/ConfirmationDialog.tsx | 18 +- admin/src/components/reqlog/Editor.tsx | 26 +- .../components/reqlog/HttpHeadersTable.tsx | 122 +- .../src/components/reqlog/HttpStatusCode.tsx | 32 +- admin/src/components/reqlog/LogDetail.tsx | 10 +- admin/src/components/reqlog/LogsOverview.tsx | 17 +- admin/src/components/reqlog/RequestDetail.tsx | 61 +- admin/src/components/reqlog/RequestList.tsx | 73 +- .../src/components/reqlog/ResponseDetail.tsx | 27 +- admin/src/components/reqlog/Search.tsx | 136 +- admin/src/components/scope/AddRule.tsx | 59 +- admin/src/components/scope/RuleListItem.tsx | 27 +- admin/src/components/scope/Rules.tsx | 41 +- admin/src/lib/Project.ts | 5 + admin/src/lib/createEmotionCache.ts | 7 + admin/src/lib/graphql.ts | 36 +- admin/src/lib/omitTypename.ts | 2 +- admin/src/lib/requestLogs.ts | 24 + admin/src/lib/scope.ts | 3 + admin/src/lib/theme.ts | 68 +- admin/src/pages/_app.tsx | 39 +- admin/src/pages/_document.tsx | 69 +- admin/src/pages/get-started/index.tsx | 14 +- admin/src/pages/index.tsx | 59 +- admin/src/pages/projects/index.tsx | 5 +- admin/src/pages/proxy/index.tsx | 12 +- admin/src/pages/proxy/logs/index.tsx | 2 +- admin/src/pages/scope/index.tsx | 10 +- admin/src/pages/sender/index.tsx | 2 +- admin/tsconfig.json | 5 +- admin/yarn.lock | 7010 +++++------------ 42 files changed, 2777 insertions(+), 6026 deletions(-) create mode 100644 admin/.eslintrc.json create mode 100644 admin/.prettierignore create mode 100644 admin/.prettierrc.json create mode 100644 admin/src/lib/Project.ts create mode 100644 admin/src/lib/createEmotionCache.ts create mode 100644 admin/src/lib/requestLogs.ts create mode 100644 admin/src/lib/scope.ts 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 {
{ - setInput(node); - }, + sx={{ + mr: 2, }} + color="primary" + size="small" label="Project name" placeholder="Project name…" + onChange={(e) => setName(e.target.value)} error={Boolean(createProjErr || openProjErr)} - helperText={createProjErr && createProjErr.message || openProjErr && openProjErr.message} + helperText={(createProjErr && createProjErr.message) || (openProjErr && openProjErr.message)} /> @@ -225,6 +207,7 @@ function ProjectList(): JSX.Element { open={deleteNotifOpen} autoHideDuration={3000} onClose={handleCloseDeleteNotif} + anchorOrigin={{ horizontal: "center", vertical: "bottom" }} > Project {deleteProj?.name} was deleted. @@ -237,82 +220,70 @@ function ProjectList(): JSX.Element { {projLoading && } - {projErr && ( - - Error fetching projects: {projErr.message} - - )} - {openProjErr && ( - - Error opening project: {openProjErr.message} - - )} - {closeProjErr && ( - - Error closing project: {closeProjErr.message} - - )} + {projErr && Error fetching projects: {projErr.message}} + {openProjErr && Error opening project: {openProjErr.message}} + {closeProjErr && Error closing project: {closeProjErr.message}} - {projData?.projects.length > 0 && ( - - {projData.projects.map((project) => ( - - - - - - - - {project.name} {project.isActive && (Active)} - - - {project.isActive && ( - - closeProject()}> - - - - )} - {!project.isActive && ( - + {projData && projData.projects.length > 0 && ( + + + {projData.projects.map((project) => ( + + + + + + + + {project.name} {project.isActive && (Active)} + + + {project.isActive && ( + + closeProject()}> + + + + )} + {!project.isActive && ( + + + + openProject({ + variables: { id: project.id }, + }) + } + > + + + + + )} + - - openProject({ - variables: { id: project.id }, - }) - } - > - + handleDeleteButtonClick(project)} disabled={project.isActive}> + - )} - - - handleDeleteButtonClick(project)} - disabled={project.isActive} - > - - - - - - - ))} - + + + ))} + + )} {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 { )} - + Rule Type - + } label="URL" /> @@ -104,20 +86,17 @@ function AddRule(): JSX.Element { label="Expression" placeholder="^https:\/\/(.*)example.com(.*)" helperText="Regular expression to match on." - color="secondary" + color="primary" variant="outlined" required + value={expression} + onChange={(e) => setExpression(e.target.value)} InputProps={{ - className: classes.ruleExpression, + sx: { fontFamily: "'JetBrains Mono', monospace" }, }} InputLabelProps={{ shrink: true, }} - inputProps={{ - ref: (node) => { - setExpression(node); - }, - }} margin="normal" /> @@ -125,7 +104,7 @@ function AddRule(): JSX.Element {