diff --git a/frontend/src/components/NetworkMembers/NetworkMembers.jsx b/frontend/src/components/NetworkMembers/NetworkMembers.jsx
index 303fb25..3f1d368 100644
--- a/frontend/src/components/NetworkMembers/NetworkMembers.jsx
+++ b/frontend/src/components/NetworkMembers/NetworkMembers.jsx
@@ -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) => (
<>
-
+
>
),
diff --git a/frontend/src/components/NetworkMembers/components/MemberSettings/MemberSettings.jsx b/frontend/src/components/NetworkMembers/components/MemberSettings/MemberSettings.jsx
index f442e41..df6bb64 100644
--- a/frontend/src/components/NetworkMembers/components/MemberSettings/MemberSettings.jsx
+++ b/frontend/src/components/NetworkMembers/components/MemberSettings/MemberSettings.jsx
@@ -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 }) {
/>
Do Not Auto-Assign IPs
+
+
+ Capabilities
+
+
+
+ {Object.entries(network["capabilitiesByName"]).length === 0
+ ? "No capabilities defined"
+ : ""}
+ {Object.entries(network["capabilitiesByName"]).map(
+ ([capName, capId]) => (
+
+ }
+ key={"cap-" + capId}
+ label={capName}
+ />
+ )
+ )}
+
+
+
+
+
+ Tags
+
+ {Object.entries(network["tagsByName"]).length === 0 ? (
+
+ No tags defined
+
+ ) : (
+ ""
+ )}
+ {Object.entries(network["tagsByName"]).map(
+ ([tagName, tagDetail]) => (
+
+
+
+ )
+ )}
+
>
diff --git a/frontend/src/components/NetworkMembers/components/MemberSettings/components/Tag/Tag.jsx b/frontend/src/components/NetworkMembers/components/MemberSettings/components/Tag/Tag.jsx
new file mode 100644
index 0000000..3335f33
--- /dev/null
+++ b/frontend/src/components/NetworkMembers/components/MemberSettings/components/Tag/Tag.jsx
@@ -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 (
+
+
+
+
+ {tagName}
+ {tagValue === "" ? (
+ ""
+ ) : (
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ {Object.entries(tagDetail["flags"]).map(([flagKey, flagValue]) => (
+
+ }
+ key={"flag-" + flagKey}
+ label={flagKey}
+ />
+ ))}
+
+
+
+ );
+}
+
+export default Tag;
diff --git a/frontend/src/components/NetworkMembers/components/MemberSettings/components/Tag/index.jsx b/frontend/src/components/NetworkMembers/components/MemberSettings/components/Tag/index.jsx
new file mode 100644
index 0000000..21bb058
--- /dev/null
+++ b/frontend/src/components/NetworkMembers/components/MemberSettings/components/Tag/index.jsx
@@ -0,0 +1 @@
+export { default } from "./Tag";
diff --git a/frontend/src/components/NetworkRules/NetworkRules.jsx b/frontend/src/components/NetworkRules/NetworkRules.jsx
index dd4fbad..1fc6788 100644
--- a/frontend/src/components/NetworkRules/NetworkRules.jsx
+++ b/frontend/src/components/NetworkRules/NetworkRules.jsx
@@ -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);
}
};
diff --git a/frontend/src/routes/Network/Network.jsx b/frontend/src/routes/Network/Network.jsx
index 7a41669..f1475fb 100644
--- a/frontend/src/routes/Network/Network.jsx
+++ b/frontend/src/routes/Network/Network.jsx
@@ -1,18 +1,15 @@
-import { useState, useEffect } from "react";
-import { Link as RouterLink, useParams, useHistory } from "react-router-dom";
-import { useLocalStorage } from "react-use";
-
-import { Link, Grid, Typography } from "@material-ui/core";
+import { Grid, Link, Typography } from "@material-ui/core";
import ArrowBackIcon from "@material-ui/icons/ArrowBack";
-import useStyles from "./Network.styles";
-
import NetworkHeader from "components/NetworkHeader";
-import NetworkSettings from "components/NetworkSettings";
+import NetworkManagement from "components/NetworkManagement";
import NetworkMembers from "components/NetworkMembers";
import NetworkRules from "components/NetworkRules";
-import NetworkManagement from "components/NetworkManagement";
-
+import NetworkSettings from "components/NetworkSettings";
+import { useCallback, useEffect, useState } from "react";
+import { Link as RouterLink, useHistory, useParams } from "react-router-dom";
+import { useLocalStorage } from "react-use";
import API from "utils/API";
+import useStyles from "./Network.styles";
function Network() {
const { nwid } = useParams();
@@ -22,22 +19,23 @@ function Network() {
const classes = useStyles();
const history = useHistory();
- useEffect(() => {
- async function fetchData() {
- try {
- const network = await API.get("network/" + nwid);
- setNetwork(network.data);
- console.log("Current network:", network.data);
- } catch (err) {
- if (err.response.status === 404) {
- history.push("/404");
- }
- console.error(err);
+ const fetchData = useCallback(async () => {
+ try {
+ const network = await API.get("network/" + nwid);
+ setNetwork(network.data);
+ console.log("Current network:", network.data);
+ } catch (err) {
+ if (err.response.status === 404) {
+ history.push("/404");
}
+ console.error(err);
}
- fetchData();
}, [nwid, history]);
+ useEffect(() => {
+ fetchData();
+ }, [nwid, fetchData]);
+
if (loggedIn) {
return (
<>
@@ -52,8 +50,10 @@ function Network() {
>
)}
-
- {network["config"] && }
+
+ {network["config"] && (
+
+ )}
>
diff --git a/frontend/src/utils/ChangeHelper.js b/frontend/src/utils/ChangeHelper.js
index f425320..2183ffc 100644
--- a/frontend/src/utils/ChangeHelper.js
+++ b/frontend/src/utils/ChangeHelper.js
@@ -1,3 +1,5 @@
+import { pull } from "lodash";
+
export function parseValue(
event,
mode = "text",
@@ -23,6 +25,25 @@ export function parseValue(
}
} else if (mode === "custom") {
value = data;
+ } else if (mode === "capChange") {
+ value = data[key1][key2];
+ if (event.target.checked) {
+ value.push(id);
+ } else {
+ pull(value, id);
+ }
+ } else if (mode === "tagChange") {
+ value = data[key1][key2];
+ let tagValue = event.target.value;
+ let tagIndex = value.findIndex((item) => {
+ return item[0] === id;
+ });
+ if (tagIndex !== -1) {
+ value.splice(tagIndex, 1);
+ }
+ if (tagValue !== "") {
+ value.push([id, tagValue]);
+ }
} else {
value = event.target.value;
}