Tidy up admin structure

This commit is contained in:
David Stotijn
2022-02-23 15:20:23 +01:00
parent efc20564c1
commit 11f70282d7
80 changed files with 1525 additions and 1206 deletions

View File

@ -0,0 +1,108 @@
import { useApolloClient } from "@apollo/client";
import AddIcon from "@mui/icons-material/Add";
import { Alert } from "@mui/lab";
import {
Box,
Button,
CircularProgress,
FormControl,
FormControlLabel,
FormLabel,
Radio,
RadioGroup,
TextField,
} from "@mui/material";
import React, { useState } from "react";
import { ScopeDocument, ScopeQuery, ScopeRule, useSetScopeMutation } from "lib/graphql/generated";
function AddRule(): JSX.Element {
const [ruleType, setRuleType] = useState("url");
const [expression, setExpression] = useState("");
const client = useApolloClient();
const [setScope, { error, loading }] = useSetScopeMutation({
onCompleted({ setScope }) {
client.writeQuery({
query: ScopeDocument,
data: { scope: setScope },
});
setExpression("");
},
});
const handleTypeChange = (e: React.ChangeEvent, value: string) => {
setRuleType(value);
};
const handleSubmit = (e: React.SyntheticEvent) => {
e.preventDefault();
let scope: ScopeRule[] = [];
try {
const data = client.readQuery<ScopeQuery>({
query: ScopeDocument,
});
if (data) {
scope = data.scope;
}
} catch (e) {}
setScope({
variables: {
scope: [...scope.map(({ url }) => ({ url })), { url: expression }],
},
});
};
return (
<div>
{error && (
<Box mb={4}>
<Alert severity="error">Error adding rule: {error.message}</Alert>
</Box>
)}
<form onSubmit={handleSubmit} autoComplete="off">
<FormControl fullWidth>
<FormLabel color="primary" component="legend">
Rule Type
</FormLabel>
<RadioGroup row name="ruleType" value={ruleType} onChange={handleTypeChange}>
<FormControlLabel value="url" control={<Radio />} label="URL" />
</RadioGroup>
</FormControl>
<FormControl fullWidth>
<TextField
label="Expression"
placeholder="^https:\/\/(.*)example.com(.*)"
helperText="Regular expression to match on."
color="primary"
variant="outlined"
required
value={expression}
onChange={(e) => setExpression(e.target.value)}
InputProps={{
sx: { fontFamily: "'JetBrains Mono', monospace" },
}}
InputLabelProps={{
shrink: true,
}}
margin="normal"
/>
</FormControl>
<Box my={2}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={loading}
startIcon={loading ? <CircularProgress size={22} /> : <AddIcon />}
>
Add rule
</Button>
</Box>
</form>
</div>
);
}
export default AddRule;

View File

@ -0,0 +1,91 @@
import { useApolloClient } from "@apollo/client";
import CodeIcon from "@mui/icons-material/Code";
import DeleteIcon from "@mui/icons-material/Delete";
import {
Avatar,
Chip,
IconButton,
ListItem,
ListItemAvatar,
ListItemSecondaryAction,
ListItemText,
Tooltip,
} from "@mui/material";
import React from "react";
import { ScopeDocument, ScopeQuery, useSetScopeMutation } from "lib/graphql/generated";
type ScopeRule = ScopeQuery["scope"][number];
type RuleListItemProps = {
scope: ScopeQuery["scope"];
rule: ScopeRule;
index: number;
};
function RuleListItem({ scope, rule, index }: RuleListItemProps): JSX.Element {
const client = useApolloClient();
const [setScope, { loading }] = useSetScopeMutation({
onCompleted({ setScope }) {
client.writeQuery({
query: ScopeDocument,
data: { scope: setScope },
});
},
});
const handleDelete = (index: number) => {
const clone = [...scope];
clone.splice(index, 1);
setScope({
variables: {
scope: clone.map(({ url }) => ({ url })),
},
});
};
return (
<ListItem>
<ListItemAvatar>
<Avatar>
<CodeIcon />
</Avatar>
</ListItemAvatar>
<RuleListItemText rule={rule} />
<ListItemSecondaryAction>
<RuleTypeChip rule={rule} />
<Tooltip title="Delete rule">
<span style={{ marginLeft: 8 }}>
<IconButton onClick={() => handleDelete(index)} disabled={loading}>
<DeleteIcon />
</IconButton>
</span>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
);
}
function RuleListItemText({ rule }: { rule: ScopeRule }): JSX.Element {
let text: JSX.Element = <div></div>;
if (rule.url) {
text = <code>{rule.url}</code>;
}
// TODO: Parse and handle rule.header and rule.body.
return <ListItemText>{text}</ListItemText>;
}
function RuleTypeChip({ rule }: { rule: ScopeRule }): JSX.Element {
let label = "Unknown";
if (rule.url) {
label = "URL";
}
return <Chip label={label} variant="outlined" />;
}
export default RuleListItem;

View File

@ -0,0 +1,31 @@
import { Alert } from "@mui/lab";
import { CircularProgress, List } from "@mui/material";
import React from "react";
import RuleListItem from "./RuleListItem";
import { useScopeQuery } from "lib/graphql/generated";
function Rules(): JSX.Element {
const { loading, error, data } = useScopeQuery();
return (
<div>
{loading && <CircularProgress />}
{error && <Alert severity="error">Error fetching scope: {error.message}</Alert>}
{data && data.scope.length > 0 && (
<List
sx={{
bgcolor: "background.paper",
}}
>
{data.scope.map((rule, index) => (
<RuleListItem key={index} rule={rule} scope={data.scope} index={index} />
))}
</List>
)}
</div>
);
}
export default Rules;