mirror of
https://github.com/dstotijn/hetty.git
synced 2025-07-01 18:47:29 -04:00
Polish admin styles
This commit is contained in:
@ -12,6 +12,7 @@
|
|||||||
"@apollo/client": "^3.2.0",
|
"@apollo/client": "^3.2.0",
|
||||||
"@material-ui/core": "^4.11.0",
|
"@material-ui/core": "^4.11.0",
|
||||||
"@material-ui/icons": "^4.9.1",
|
"@material-ui/icons": "^4.9.1",
|
||||||
|
"@material-ui/lab": "^4.0.0-alpha.56",
|
||||||
"graphql": "^15.3.0",
|
"graphql": "^15.3.0",
|
||||||
"next": "^9.5.3",
|
"next": "^9.5.3",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
|
25
admin/src/components/CenteredPaper.tsx
Normal file
25
admin/src/components/CenteredPaper.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Paper } from "@material-ui/core";
|
||||||
|
|
||||||
|
function CenteredPaper({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Paper
|
||||||
|
elevation={0}
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
padding: 36,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Paper>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CenteredPaper;
|
@ -86,7 +86,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
padding: theme.spacing(2),
|
padding: theme.spacing(3),
|
||||||
},
|
},
|
||||||
listItem: {
|
listItem: {
|
||||||
paddingLeft: 16,
|
paddingLeft: 16,
|
||||||
@ -136,7 +136,7 @@ export function Layout(props: { children: React.ReactNode }): JSX.Element {
|
|||||||
<MenuIcon />
|
<MenuIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h5" noWrap>
|
<Typography variant="h5" noWrap>
|
||||||
Hetty
|
<span style={{ marginRight: 12 }}>🧑🔧</span>Hetty
|
||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { teal, orange, red } from "@material-ui/core/colors";
|
import { green, orange, red } from "@material-ui/core/colors";
|
||||||
import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
|
import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
|
||||||
|
|
||||||
function HttpStatusIcon({ status }: { status: number }): JSX.Element {
|
function HttpStatusIcon({ status }: { status: number }): JSX.Element {
|
||||||
@ -6,7 +6,7 @@ function HttpStatusIcon({ status }: { status: number }): JSX.Element {
|
|||||||
switch (Math.floor(status / 100)) {
|
switch (Math.floor(status / 100)) {
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
return <FiberManualRecordIcon style={{ ...style, color: teal[400] }} />;
|
return <FiberManualRecordIcon style={{ ...style, color: green[600] }} />;
|
||||||
case 4:
|
case 4:
|
||||||
return <FiberManualRecordIcon style={{ ...style, color: orange[400] }} />;
|
return <FiberManualRecordIcon style={{ ...style, color: orange[400] }} />;
|
||||||
case 5:
|
case 5:
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { gql, useQuery } from "@apollo/client";
|
import { gql, useQuery } from "@apollo/client";
|
||||||
import { Box, Grid, Paper } from "@material-ui/core";
|
import { Box, Grid, Paper, CircularProgress } from "@material-ui/core";
|
||||||
|
|
||||||
import ResponseDetail from "./ResponseDetail";
|
import ResponseDetail from "./ResponseDetail";
|
||||||
import RequestDetail from "./RequestDetail";
|
import RequestDetail from "./RequestDetail";
|
||||||
|
import Alert from "@material-ui/lab/Alert";
|
||||||
|
|
||||||
const HTTP_REQUEST_LOG = gql`
|
const HTTP_REQUEST_LOG = gql`
|
||||||
query HttpRequestLog($id: ID!) {
|
query HttpRequestLog($id: ID!) {
|
||||||
@ -31,8 +32,16 @@ function LogDetail({ requestId: id }: Props): JSX.Element {
|
|||||||
variables: { id },
|
variables: { id },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (loading) return <div>"Loading..."</div>;
|
if (loading) {
|
||||||
if (error) return <div>`Error: ${error.message}`</div>;
|
return <CircularProgress />;
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<Alert severity="error">
|
||||||
|
Error fetching logs details: {error.message}
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const { method, url, proto, body, response } = data.httpRequestLog;
|
const { method, url, proto, body, response } = data.httpRequestLog;
|
||||||
|
|
||||||
@ -40,13 +49,13 @@ function LogDetail({ requestId: id }: Props): JSX.Element {
|
|||||||
<div>
|
<div>
|
||||||
<Grid container item spacing={2}>
|
<Grid container item spacing={2}>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<Box component={Paper} maxHeight="60vh" overflow="scroll">
|
<Box component={Paper} maxHeight="62vh" overflow="scroll">
|
||||||
<RequestDetail request={{ method, url, proto, body }} />
|
<RequestDetail request={{ method, url, proto, body }} />
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
{response && (
|
{response && (
|
||||||
<Box component={Paper} maxHeight="65vh" overflow="scroll">
|
<Box component={Paper} maxHeight="62vh" overflow="scroll">
|
||||||
<ResponseDetail response={response} />
|
<ResponseDetail response={response} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
@ -1,37 +1,56 @@
|
|||||||
|
import { gql, useQuery } from "@apollo/client";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Box, Paper, Container, Typography } from "@material-ui/core";
|
import { Box, Typography, CircularProgress } from "@material-ui/core";
|
||||||
|
import Alert from "@material-ui/lab/Alert";
|
||||||
|
|
||||||
import RequestList from "./RequestList";
|
import RequestList from "./RequestList";
|
||||||
import LogDetail from "./LogDetail";
|
import LogDetail from "./LogDetail";
|
||||||
|
import CenteredPaper from "../CenteredPaper";
|
||||||
|
|
||||||
|
const HTTP_REQUEST_LOGS = gql`
|
||||||
|
query HttpRequestLogs {
|
||||||
|
httpRequestLogs {
|
||||||
|
id
|
||||||
|
method
|
||||||
|
url
|
||||||
|
timestamp
|
||||||
|
response {
|
||||||
|
status
|
||||||
|
statusCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
function LogsOverview(): JSX.Element {
|
function LogsOverview(): JSX.Element {
|
||||||
const [detailReqLogId, setDetailReqLogId] = useState<string | null>(null);
|
const { loading, error, data } = useQuery(HTTP_REQUEST_LOGS);
|
||||||
|
|
||||||
|
const [detailReqLogId, setDetailReqLogId] = useState<string | null>(null);
|
||||||
const handleLogClick = (reqId: string) => setDetailReqLogId(reqId);
|
const handleLogClick = (reqId: string) => setDetailReqLogId(reqId);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <CircularProgress />;
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
return <Alert severity="error">Error fetching logs: {error.message}</Alert>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { httpRequestLogs: logs } = data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box style={{ padding: 8 }}>
|
<div>
|
||||||
<Box mb={2}>
|
<Box mb={2}>
|
||||||
<RequestList onLogClick={handleLogClick} />
|
<RequestList logs={logs} onLogClick={handleLogClick} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
{detailReqLogId ? (
|
{detailReqLogId && <LogDetail requestId={detailReqLogId} />}
|
||||||
<LogDetail requestId={detailReqLogId} />
|
{logs.length !== 0 && !detailReqLogId && (
|
||||||
) : (
|
<CenteredPaper>
|
||||||
<Paper
|
|
||||||
elevation={0}
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "60vh",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography>Select a log entry…</Typography>
|
<Typography>Select a log entry…</Typography>
|
||||||
</Paper>
|
</CenteredPaper>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Typography, Box } from "@material-ui/core";
|
import { Typography, Box } from "@material-ui/core";
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||||
import { materialLight } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
import { vscDarkPlus } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
request: {
|
request: {
|
||||||
@ -15,7 +15,6 @@ function RequestDetail({ request }: Props): JSX.Element {
|
|||||||
const { method, url, proto, body } = request;
|
const { method, url, proto, body } = request;
|
||||||
|
|
||||||
const parsedUrl = new URL(url);
|
const parsedUrl = new URL(url);
|
||||||
console.log(parsedUrl);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -24,18 +23,18 @@ function RequestDetail({ request }: Props): JSX.Element {
|
|||||||
variant="h6"
|
variant="h6"
|
||||||
style={{ fontSize: "1rem", whiteSpace: "nowrap" }}
|
style={{ fontSize: "1rem", whiteSpace: "nowrap" }}
|
||||||
>
|
>
|
||||||
{request.method}{" "}
|
{method} {decodeURIComponent(parsedUrl.pathname + parsedUrl.search)}{" "}
|
||||||
{decodeURIComponent(parsedUrl.pathname + parsedUrl.search)} {proto}
|
{proto}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
{request.body && (
|
{body && (
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
language="markup"
|
language="markup"
|
||||||
showLineNumbers={true}
|
showLineNumbers={true}
|
||||||
style={materialLight}
|
style={vscDarkPlus}
|
||||||
>
|
>
|
||||||
{request.body}
|
{body}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { gql, useQuery } from "@apollo/client";
|
|
||||||
import {
|
import {
|
||||||
TableContainer,
|
TableContainer,
|
||||||
Paper,
|
Paper,
|
||||||
@ -7,42 +6,50 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
CircularProgress,
|
||||||
Typography,
|
Typography,
|
||||||
|
Box,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
|
|
||||||
import HttpStatusIcon from "./HttpStatusCode";
|
import HttpStatusIcon from "./HttpStatusCode";
|
||||||
|
import CenteredPaper from "../CenteredPaper";
|
||||||
const HTTP_REQUEST_LOGS = gql`
|
|
||||||
query HttpRequestLogs {
|
|
||||||
httpRequestLogs {
|
|
||||||
id
|
|
||||||
method
|
|
||||||
url
|
|
||||||
timestamp
|
|
||||||
response {
|
|
||||||
status
|
|
||||||
statusCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
logs: Array<any>;
|
||||||
onLogClick(requestId: string): void;
|
onLogClick(requestId: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function RequestList({ onLogClick }: Props): JSX.Element {
|
function RequestList({ logs, onLogClick }: Props): JSX.Element {
|
||||||
const { loading, error, data } = useQuery(HTTP_REQUEST_LOGS);
|
return (
|
||||||
|
<div>
|
||||||
|
<RequestListTable onLogClick={onLogClick} logs={logs} />
|
||||||
|
{logs.length === 0 && (
|
||||||
|
<Box my={1}>
|
||||||
|
<CenteredPaper>
|
||||||
|
<Typography>No logs found.</Typography>
|
||||||
|
</CenteredPaper>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (loading) return <div>"Loading..."</div>;
|
interface RequestListTableProps {
|
||||||
if (error) return <div>`Error: ${error.message}`</div>;
|
logs?: any;
|
||||||
|
onLogClick(requestId: string): void;
|
||||||
const { httpRequestLogs: logs } = data;
|
}
|
||||||
|
|
||||||
|
function RequestListTable({
|
||||||
|
logs,
|
||||||
|
onLogClick,
|
||||||
|
}: RequestListTableProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<TableContainer
|
<TableContainer
|
||||||
component={Paper}
|
component={Paper}
|
||||||
style={{ minHeight: 200, height: "24vh" }}
|
style={{
|
||||||
|
minHeight: logs.length ? 200 : 0,
|
||||||
|
height: logs.length ? "24vh" : "inherit",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Table stickyHeader size="small">
|
<Table stickyHeader size="small">
|
||||||
<TableHead>
|
<TableHead>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Typography, Box } from "@material-ui/core";
|
import { Typography, Box } from "@material-ui/core";
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||||
import { materialLight } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
import { vscDarkPlus } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||||
|
|
||||||
import HttpStatusIcon from "./HttpStatusCode";
|
import HttpStatusIcon from "./HttpStatusCode";
|
||||||
|
|
||||||
@ -26,22 +26,24 @@ function ResponseDetail({ response }: Props): JSX.Element {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<SyntaxHighlighter
|
{response.body && (
|
||||||
language="markup"
|
<SyntaxHighlighter
|
||||||
showLineNumbers={true}
|
language="markup"
|
||||||
showInlineLineNumbers={true}
|
showLineNumbers={true}
|
||||||
style={materialLight}
|
showInlineLineNumbers={true}
|
||||||
lineProps={{
|
style={vscDarkPlus}
|
||||||
style: {
|
lineProps={{
|
||||||
display: "block",
|
style: {
|
||||||
wordBreak: "break-all",
|
display: "block",
|
||||||
whiteSpace: "pre-wrap",
|
wordBreak: "break-all",
|
||||||
},
|
whiteSpace: "pre-wrap",
|
||||||
}}
|
},
|
||||||
wrapLines={true}
|
}}
|
||||||
>
|
wrapLines={true}
|
||||||
{response.body}
|
>
|
||||||
</SyntaxHighlighter>
|
{response.body}
|
||||||
|
</SyntaxHighlighter>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,7 @@ function createApolloClient() {
|
|||||||
return new ApolloClient({
|
return new ApolloClient({
|
||||||
ssrMode: typeof window === "undefined",
|
ssrMode: typeof window === "undefined",
|
||||||
link: new HttpLink({
|
link: new HttpLink({
|
||||||
uri: "http://localhost:3000/api/graphql",
|
uri: "/api/graphql",
|
||||||
}),
|
}),
|
||||||
cache: new InMemoryCache({
|
cache: new InMemoryCache({
|
||||||
typePolicies: {
|
typePolicies: {
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { createMuiTheme } from "@material-ui/core/styles";
|
import { createMuiTheme } from "@material-ui/core/styles";
|
||||||
import teal from "@material-ui/core/colors/teal";
|
import grey from "@material-ui/core/colors/grey";
|
||||||
import green from "@material-ui/core/colors/green";
|
import green from "@material-ui/core/colors/green";
|
||||||
|
|
||||||
const theme = createMuiTheme({
|
const theme = createMuiTheme({
|
||||||
palette: {
|
palette: {
|
||||||
|
type: "dark",
|
||||||
primary: {
|
primary: {
|
||||||
main: teal[500],
|
main: grey[900],
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: green[500],
|
main: green[500],
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import RequestList from "../components/reqlog/RequestList";
|
|
||||||
|
|
||||||
function Index(): JSX.Element {
|
function Index(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Hetty</h1>
|
<h1>Hetty123</h1>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
9
admin/src/pages/proxy/index.tsx
Normal file
9
admin/src/pages/proxy/index.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
function Index(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Proxy123</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Index;
|
@ -1,17 +0,0 @@
|
|||||||
import { useState } from "react";
|
|
||||||
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 {
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<LogsOverview />
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Logs;
|
|
17
admin/src/pages/proxy/logs/index.tsx
Normal file
17
admin/src/pages/proxy/logs/index.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Typography, Box } from "@material-ui/core";
|
||||||
|
|
||||||
|
import LogsOverview from "../../../components/reqlog/LogsOverview";
|
||||||
|
import Layout from "../../../components/Layout";
|
||||||
|
|
||||||
|
function ProxyLogs(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<Box mb={2}>
|
||||||
|
<Typography variant="h5">Proxy logs</Typography>
|
||||||
|
</Box>
|
||||||
|
<LogsOverview />
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProxyLogs;
|
@ -1085,6 +1085,17 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.4.4"
|
"@babel/runtime" "^7.4.4"
|
||||||
|
|
||||||
|
"@material-ui/lab@^4.0.0-alpha.56":
|
||||||
|
version "4.0.0-alpha.56"
|
||||||
|
resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.56.tgz#ff63080949b55b40625e056bbda05e130d216d34"
|
||||||
|
integrity sha512-xPlkK+z/6y/24ka4gVJgwPfoCF4RCh8dXb1BNE7MtF9bXEBLN/lBxNTK8VAa0qm3V2oinA6xtUIdcRh0aeRtVw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.4.4"
|
||||||
|
"@material-ui/utils" "^4.10.2"
|
||||||
|
clsx "^1.0.4"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
react-is "^16.8.0"
|
||||||
|
|
||||||
"@material-ui/styles@^4.10.0":
|
"@material-ui/styles@^4.10.0":
|
||||||
version "4.10.0"
|
version "4.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.10.0.tgz#2406dc23aa358217aa8cc772e6237bd7f0544071"
|
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.10.0.tgz#2406dc23aa358217aa8cc772e6237bd7f0544071"
|
||||||
|
Reference in New Issue
Block a user