feat: add capabilities and tags support

#35
This commit is contained in:
snachx 2021-12-11 21:14:26 +08:00
parent 369d96e50a
commit 8f891747d6
7 changed files with 314 additions and 71 deletions

View file

@ -1,30 +1,26 @@
import { useState, useEffect, useCallback } from "react";
import { useParams } from "react-router-dom";
import {
Accordion,
AccordionSummary,
AccordionDetails,
AccordionSummary,
Checkbox,
Grid,
Typography,
IconButton,
Typography,
} from "@material-ui/core";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import RefreshIcon from "@material-ui/icons/Refresh";
import { useCallback, useEffect, useState } from "react";
import DataTable from "react-data-table-component";
import MemberName from "./components/MemberName";
import ManagedIP from "./components/ManagedIP";
import DeleteMember from "./components/DeleteMember";
import MemberSettings from "./components/MemberSettings";
import AddMember from "./components/AddMember";
import { useParams } from "react-router-dom";
import API from "utils/API";
import { parseValue, replaceValue, setValue } from "utils/ChangeHelper";
import AddMember from "./components/AddMember";
import DeleteMember from "./components/DeleteMember";
import ManagedIP from "./components/ManagedIP";
import MemberName from "./components/MemberName";
import MemberSettings from "./components/MemberSettings";
function NetworkMembers() {
function NetworkMembers({ network }) {
const { nwid } = useParams();
const [members, setMembers] = useState([]);
@ -49,27 +45,23 @@ function NetworkMembers() {
console.log("Action:", req);
};
const handleChange = (
member,
key1,
key2 = null,
mode = "text",
id = null
) => (event) => {
const value = parseValue(event, mode, member, key1, key2, id);
const handleChange =
(member, key1, key2 = null, mode = "text", id = null) =>
(event) => {
const value = parseValue(event, mode, member, key1, key2, id);
const updatedMember = replaceValue({ ...member }, key1, key2, value);
const updatedMember = replaceValue({ ...member }, key1, key2, value);
const index = members.findIndex((item) => {
return item["config"]["id"] === member["config"]["id"];
});
let mutableMembers = [...members];
mutableMembers[index] = updatedMember;
setMembers(mutableMembers);
const index = members.findIndex((item) => {
return item["config"]["id"] === member["config"]["id"];
});
let mutableMembers = [...members];
mutableMembers[index] = updatedMember;
setMembers(mutableMembers);
const data = setValue({}, key1, key2, value);
sendReq(member["config"]["id"], data);
};
const data = setValue({}, key1, key2, value);
sendReq(member["config"]["id"], data);
};
const columns = [
{
@ -130,7 +122,11 @@ function NetworkMembers() {
right: true,
cell: (row) => (
<>
<MemberSettings member={row} handleChange={handleChange} />
<MemberSettings
member={row}
network={network}
handleChange={handleChange}
/>
<DeleteMember nwid={nwid} mid={row.config.id} callback={fetchData} />
</>
),

View file

@ -1,16 +1,19 @@
import { useState } from "react";
import {
Checkbox,
Dialog,
DialogTitle,
DialogContent,
DialogTitle,
FormControlLabel,
Grid,
IconButton,
Paper,
Typography,
} from "@material-ui/core";
import BuildIcon from "@material-ui/icons/Build";
import { useState } from "react";
import Tag from "./components/Tag";
function MemberSettings({ member, handleChange }) {
function MemberSettings({ member, network, handleChange }) {
const [open, setOpen] = useState(false);
const handleClickOpen = () => {
@ -55,6 +58,65 @@ function MemberSettings({ member, handleChange }) {
/>
<span>Do Not Auto-Assign IPs</span>
</Grid>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h5">Capabilities</Typography>
</Grid>
<Grid item xs={12}>
<Paper style={{ padding: 20 }}>
{Object.entries(network["capabilitiesByName"]).length === 0
? "No capabilities defined"
: ""}
{Object.entries(network["capabilitiesByName"]).map(
([capName, capId]) => (
<FormControlLabel
control={
<Checkbox
checked={member["config"]["capabilities"].includes(
capId
)}
color="primary"
onChange={handleChange(
member,
"config",
"capabilities",
"capChange",
capId
)}
/>
}
key={"cap-" + capId}
label={capName}
/>
)
)}
</Paper>
</Grid>
</Grid>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h5">Tags</Typography>
</Grid>
{Object.entries(network["tagsByName"]).length === 0 ? (
<Grid item xs={12}>
<Paper style={{ padding: 20 }}>No tags defined</Paper>
</Grid>
) : (
""
)}
{Object.entries(network["tagsByName"]).map(
([tagName, tagDetail]) => (
<Grid item xs={12} sm={6} key={"tag-" + tagName}>
<Tag
member={member}
tagName={tagName}
tagDetail={tagDetail}
handleChange={handleChange}
/>
</Grid>
)
)}
</Grid>
</DialogContent>
</Dialog>
</>

View file

@ -0,0 +1,165 @@
import {
Checkbox,
FormControlLabel,
Grid,
IconButton,
Input,
Paper,
Select,
Typography,
} from "@material-ui/core";
import DeleteIcon from "@material-ui/icons/Delete";
import { useEffect, useState } from "react";
import { useDebounce } from "react-use";
function Tag({ member, tagName, tagDetail, handleChange }) {
const [tagValue, setTagValue] = useState("");
const [tagChangedByUser, setTagChangedByUser] = useState(false);
useEffect(() => {
let tagIndex = member["config"]["tags"].findIndex((item) => {
return item[0] === tagDetail["id"];
});
let value = "";
if (tagIndex !== -1) {
value = member["config"]["tags"][tagIndex][1];
}
value = value !== false ? value : "";
setTagValue(value);
}, [member, tagDetail]);
useDebounce(
async () => {
if (tagChangedByUser) {
let value = tagValue === "" ? "" : parseInt(tagValue);
let event = { target: { value: value } };
handleChange(
member,
"config",
"tags",
"tagChange",
tagDetail["id"]
)(event);
}
setTagChangedByUser(false);
},
500,
[tagValue]
);
const handleSelectChange = (event) => {
let newValue = event.target.value;
setTagChangedByUser(true);
setTagValue(newValue);
};
const handleFlagChange = (value) => (event) => {
let newValue;
let oldValue;
if (tagValue === "") {
oldValue = 0;
} else {
oldValue = tagValue;
}
if (event.target.checked) {
newValue = oldValue + value;
} else {
newValue = oldValue - value;
}
setTagChangedByUser(true);
setTagValue(newValue);
};
const handleInputChange = (event) => {
let value = event.target.value;
if (/^(|0|[1-9]\d*)$/.test(value)) {
value = value === "" ? value : parseInt(value);
} else {
value = 0;
}
setTagChangedByUser(true);
setTagValue(value);
};
const clearTag = (event) => {
setTagChangedByUser(true);
setTagValue("");
};
return (
<Paper style={{ padding: 20 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography
variant="h5"
color={tagValue === "" ? "error" : "primary"}
>
{tagName}
{tagValue === "" ? (
""
) : (
<IconButton aria-label="delete" onClick={clearTag}>
<DeleteIcon />
</IconButton>
)}
</Typography>
</Grid>
<Grid container>
<Grid item xs={6}>
<Select
native
value={tagValue}
onChange={handleSelectChange}
displayEmpty
style={{ minWidth: 100 }}
>
<option value="">--</option>
{Object.entries(tagDetail["enums"]).map(
([enumKey, enumValue]) => (
<option key={enumKey} value={enumValue}>
{enumKey}
</option>
)
)}
{Object.values(tagDetail["enums"]).length === 0 &&
tagValue !== "" ? (
<option value={tagValue}>(no enums)</option>
) : (
""
)}
{Object.values(tagDetail["enums"]).length !== 0 &&
!Object.values(tagDetail["enums"]).includes(tagValue) &&
tagValue !== "" ? (
<option value={tagValue}>(custom)</option>
) : (
""
)}
</Select>
</Grid>
<Grid item xs={6}>
<Input value={tagValue} onChange={handleInputChange} />
</Grid>
</Grid>
<Grid item xs={12}>
{Object.entries(tagDetail["flags"]).map(([flagKey, flagValue]) => (
<FormControlLabel
control={
<Checkbox
checked={(tagValue & flagValue) === flagValue}
onChange={handleFlagChange(flagValue)}
color="primary"
/>
}
key={"flag-" + flagKey}
label={flagKey}
/>
))}
</Grid>
</Grid>
</Paper>
);
}
export default Tag;

View file

@ -0,0 +1 @@
export { default } from "./Tag";

View file

@ -1,28 +1,23 @@
import { useState } from "react";
import {
Accordion,
AccordionSummary,
AccordionDetails,
AccordionSummary,
Button,
Divider,
Snackbar,
Hidden,
Grid,
Hidden,
Snackbar,
Typography,
} from "@material-ui/core";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import CodeMirror from "@uiw/react-codemirror";
import "codemirror/theme/3024-day.css";
import { compile } from "external/RuleCompiler";
import debounce from "lodash/debounce";
import { useState } from "react";
import API from "utils/API";
function NetworkRules({ network }) {
function NetworkRules({ network, callback }) {
const [editor, setEditor] = useState(null);
const [flowData, setFlowData] = useState({
rules: [...network.config.rules],
@ -48,6 +43,9 @@ function NetworkRules({ network }) {
const timer = setTimeout(() => {
setSnackbarOpen(false);
}, 1500);
callback();
return () => clearTimeout(timer);
}
};