feat: keyboard shortcuts

This commit is contained in:
Andres 2023-10-17 22:23:48 +02:00
parent 213c9499f2
commit 94f1a8b00b
48 changed files with 398 additions and 90 deletions

View file

@ -1,6 +1,7 @@
{
"name": "frontend",
"private": true,
"type": "module",
"dependencies": {
"@fontsource/roboto": "^4.5.8",
"@material-ui/core": "^4.12.4",
@ -13,6 +14,7 @@
"history": "^5.3.0",
"ipaddr.js": "^2.0.1",
"lodash": "^4.17.21",
"ninja-keys": "^1.2.2",
"react": "^17.0.2",
"react-data-table-component": "^6.11.8",
"react-dom": "^17.0.2",

View file

@ -2,12 +2,13 @@ import "@fontsource/roboto";
import { BrowserRouter, Route, Redirect, Switch } from "react-router-dom";
import Theme from "./components/Theme";
import Bar from "./components/Bar";
import Theme from "./components/Theme/Theme.jsx";
import Bar from "./components/Bar/Bar.jsx";
import Home from "./routes/Home";
import NotFound from "./routes/NotFound";
import Network from "./routes/Network/Network";
import Home from "./routes/Home/Home.jsx";
import NotFound from "./routes/NotFound/NotFound.jsx";
import Network from "./routes/Network/Network.jsx";
import Settings from "./routes/Settings/Settings.jsx";
function App() {
return (

View file

@ -1,8 +1,8 @@
import logo from "./assets/logo.png";
import { useState } from "react";
import { Link as RouterLink, useHistory } from "react-router-dom";
import { useLocalStorage } from "react-use";
import { useEffect, useRef, useState } from "react";
import { Redirect, Link as RouterLink, useHistory } from "react-router-dom";
import { useLocalStorage, useWindowScroll } from "react-use";
import {
AppBar,
@ -15,16 +15,56 @@ import {
MenuItem,
Link,
} from "@material-ui/core";
import MenuIcon from "@material-ui/icons/Menu";
import MenuIcon from "@material-ui/icons/Menu.js";
import LogIn from "components/LogIn";
import LogIn from "components/LogIn/LogIn.jsx";
import "ninja-keys";
import { useTranslation } from "react-i18next";
import API from "utils/API.js";
import { generateNetworkConfig } from "utils/NetworkConfig.js";
function Bar() {
const ninjaKeys = useRef(null);
const history = useHistory();
const { t, i18n } = useTranslation();
const [hotkeys, setHotkeys] = useState([
{
id: "Settings",
title: t("settings"),
hotkey: "cmd+shift+s",
handler: () => {
history.push("/settings");
},
},
{
id: "createNet",
title: t("createNetwork"),
hotkey: "cmd+shift+n",
handler: () => {
createNetwork();
},
},
{
id: "LogOut",
title: t("logOut"),
hotkey: "cmd+shift+g",
handler: () => {
onLogOutClick();
},
},
]);
const [loggedIn, setLoggedIn] = useLocalStorage("loggedIn", false);
const [disabledAuth] = useLocalStorage("disableAuth", false);
const [anchorEl, setAnchorEl] = useState(null);
const history = useHistory();
const createNetwork = async () => {
const network = await API.post("network", generateNetworkConfig());
console.log(network);
history.push("/network/" + network.data["config"]["id"]);
};
const openMenu = (event) => {
setAnchorEl(event.currentTarget);
@ -41,6 +81,12 @@ function Bar() {
history.go(0);
};
useEffect(() => {
if (ninjaKeys.current) {
ninjaKeys.current.data = hotkeys;
}
}, []);
const menuItems = [
// TODO: add settings page
// {
@ -64,6 +110,11 @@ function Bar() {
style={{ background: "#000000" }}
position="static"
>
{loggedIn && (
<>
<ninja-keys ref={ninjaKeys}></ninja-keys>
</>
)}
<Toolbar>
<Box display="flex" flexGrow={1}>
<Typography color="inherit" variant="h6">

View file

@ -1 +1 @@
export { default } from "./Bar";
export { default } from "./Bar.jsx";

View file

@ -2,12 +2,12 @@ import { useState, useEffect } from "react";
import { useHistory } from "react-router-dom";
import { Divider, Button, Grid, Typography, Box } from "@material-ui/core";
import useStyles from "./HomeLoggedIn.styles";
import useStyles from "./HomeLoggedIn.styles.jsx";
import NetworkButton from "./components/NetworkButton";
import NetworkButton from "./components/NetworkButton/NetworkButton.jsx";
import API from "utils/API";
import { generateNetworkConfig } from "utils/NetworkConfig";
import API from "utils/API.js";
import { generateNetworkConfig } from "utils/NetworkConfig.js";
function HomeLoggedIn() {
const [networks, setNetworks] = useState([]);

View file

@ -1 +1 @@
export { default } from "./HomeLoggedIn";
export { default } from "./HomeLoggedIn.jsx";

View file

@ -1 +1 @@
export { default } from "./HomeLoggedOut";
export { default } from "./HomeLoggedOut.jsx";

View file

@ -1,7 +1,7 @@
import { Divider } from "@material-ui/core";
import LogInUser from "./components/LogInUser";
import LogInToken from "./components/LogInToken";
import LogInUser from "./components/LogInUser/LogInUser.jsx";
import LogInToken from "./components/LogInToken/LogInToken.jsx";
function LogIn() {
return (

View file

@ -1 +1 @@
export { default } from "./LogInToken";
export { default } from "./LogInToken.jsx";

View file

@ -1 +1 @@
export { default } from "./LogInUser";
export { default } from "./LogInUser.jsx";

View file

@ -1 +1 @@
export { default } from "./LogIn";
export { default } from "./LogIn.jsx";

View file

@ -1 +1 @@
export { default } from "./NetworkHeader";
export { default } from "./NetworkHeader.jsx";

View file

@ -13,10 +13,10 @@ import {
DialogActions,
Typography,
} from "@material-ui/core";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import DeleteIcon from "@material-ui/icons/Delete";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore.js";
import DeleteIcon from "@material-ui/icons/Delete.js";
import API from "utils/API";
import API from "utils/API.js";
function NetworkManagement() {
const { nwid } = useParams();

View file

@ -1 +1 @@
export { default } from "./NetworkManagement";
export { default } from "./NetworkManagement.jsx";

View file

@ -7,19 +7,19 @@ import {
IconButton,
Typography,
} from "@material-ui/core";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import RefreshIcon from "@material-ui/icons/Refresh";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore.js";
import RefreshIcon from "@material-ui/icons/Refresh.js";
import { useCallback, useEffect, useState } from "react";
import DataTable from "react-data-table-component";
import { useParams } from "react-router-dom";
import API from "utils/API";
import { parseValue, replaceValue, setValue } from "utils/ChangeHelper";
import API from "utils/API.js";
import { parseValue, replaceValue, setValue } from "utils/ChangeHelper.js";
import { formatDistance } from "date-fns";
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";
import AddMember from "./components/AddMember/AddMember.jsx";
import DeleteMember from "./components/DeleteMember/DeleteMember.jsx";
import ManagedIP from "./components/ManagedIP/ManagedIP.jsx";
import MemberName from "./components/MemberName/MemberName.jsx";
import MemberSettings from "./components/MemberSettings/MemberSettings.jsx";
function NetworkMembers({ network }) {
const { nwid } = useParams();

View file

@ -1,9 +1,9 @@
import { useState } from "react";
import { List, Typography, IconButton, TextField } from "@material-ui/core";
import AddIcon from "@material-ui/icons/Add";
import AddIcon from "@material-ui/icons/Add.js";
import API from "utils/API";
import API from "utils/API.js";
function AddMember({ nwid, callback }) {
const [member, setMember] = useState("");

View file

@ -1 +1 @@
export { default } from "./AddMember";
export { default } from "./AddMember.jsx";

View file

@ -9,9 +9,10 @@ import {
DialogActions,
IconButton,
} from "@material-ui/core";
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline";
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline.js";
import API from "utils/API";
import API from "utils/API.js";
import { useTranslation } from "react-i18next";
function DeleteMember({ nwid, mid, callback }) {
const [open, setOpen] = useState(false);

View file

@ -1 +1 @@
export { default } from "./DeleteMember";
export { default } from "./DeleteMember.jsx";

View file

@ -1,10 +1,10 @@
import { useState } from "react";
import { Grid, List, TextField, IconButton } from "@material-ui/core";
import AddIcon from "@material-ui/icons/Add";
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline";
import AddIcon from "@material-ui/icons/Add.js";
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline.js";
import { validateIP, normilizeIP } from "utils/IP";
import { validateIP, normilizeIP } from "utils/IP.js";
function ManagedIP({ member, handleChange }) {
const [ipInput, setIpInput] = useState();

View file

@ -1 +1 @@
export { default } from "./ManagedIP";
export { default } from "./ManagedIP.jsx";

View file

@ -1 +1 @@
export { default } from "./MemberName";
export { default } from "./MemberName.jsx";

View file

@ -9,9 +9,9 @@ import {
Paper,
Typography,
} from "@material-ui/core";
import BuildIcon from "@material-ui/icons/Build";
import BuildIcon from "@material-ui/icons/Build.js";
import { useState } from "react";
import Tag from "./components/Tag";
import Tag from "./components/Tag/Tag.jsx";
function MemberSettings({ member, network, handleChange }) {
const [open, setOpen] = useState(false);

View file

@ -8,7 +8,7 @@ import {
Select,
Typography,
} from "@material-ui/core";
import DeleteIcon from "@material-ui/icons/Delete";
import DeleteIcon from "@material-ui/icons/Delete.js";
import { useEffect, useState } from "react";
import { useDebounce } from "react-use";

View file

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

View file

@ -1 +1 @@
export { default } from "./MemberSettings";
export { default } from "./MemberSettings.jsx";

View file

@ -1 +1 @@
export { default } from "./NetworkMembers";
export { default } from "./NetworkMembers.jsx";

View file

@ -9,13 +9,13 @@ import {
Snackbar,
Typography,
} from "@material-ui/core";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore.js";
import CodeMirror from "@uiw/react-codemirror";
import "codemirror/theme/3024-day.css";
import { compile } from "external/RuleCompiler";
import debounce from "lodash/debounce";
import { compile } from "external/RuleCompiler.js";
import debounce from "lodash/debounce.js";
import { useState } from "react";
import API from "utils/API";
import API from "utils/API.js";
function NetworkRules({ network, callback }) {
const [editor, setEditor] = useState(null);

View file

@ -1 +1 @@
export { default } from "./NetworkRules";
export { default } from "./NetworkRules.jsx";

View file

@ -9,13 +9,13 @@ import {
TextField,
Select,
} from "@material-ui/core";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore.js";
import ManagedRoutes from "./components/ManagedRoutes";
import IPv4AutoAssign from "./components/IPv4AutoAssign";
import ManagedRoutes from "./components/ManagedRoutes/ManagedRoutes.jsx";
import IPv4AutoAssign from "./components/IPv4AutoAssign/IPv4AutoAssign.jsx";
import API from "utils/API";
import { parseValue, replaceValue, setValue } from "utils/ChangeHelper";
import API from "utils/API.js";
import { parseValue, replaceValue, setValue } from "utils/ChangeHelper.js";
function NetworkSettings({ network, setNetwork }) {
const sendReq = async (data) => {

View file

@ -10,13 +10,13 @@ import {
TextField,
IconButton,
} from "@material-ui/core";
import AddIcon from "@material-ui/icons/Add";
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline";
import AddIcon from "@material-ui/icons/Add.js";
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline.js";
import DataTable from "react-data-table-component";
import { addressPool } from "utils/NetworkConfig";
import { getCIDRAddress, validateIP, normilizeIP } from "utils/IP";
import { addressPool } from "utils/NetworkConfig.js";
import { getCIDRAddress, validateIP, normilizeIP } from "utils/IP.js";
function IPv4AutoAssign({ ipAssignmentPools, handleChange }) {
const [start, setStart] = useState("");

View file

@ -1 +1 @@
export { default } from "./IPv4AutoAssign";
export { default } from "./IPv4AutoAssign.jsx";

View file

@ -9,12 +9,12 @@ import {
TextField,
IconButton,
} from "@material-ui/core";
import AddIcon from "@material-ui/icons/Add";
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline";
import AddIcon from "@material-ui/icons/Add.js";
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline.js";
import DataTable from "react-data-table-component";
import { validateIP, normilizeIP, validateCIDR } from "utils/IP";
import { validateIP, normilizeIP, validateCIDR } from "utils/IP.js";
function ManagedRoutes({ routes, handleChange }) {
const [destination, setDestination] = useState("");

View file

@ -1 +1 @@
export { default } from "./ManagedRoutes";
export { default } from "./ManagedRoutes.jsx";

View file

@ -1 +1 @@
export { default } from "./NetworkSettings";
export { default } from "./NetworkSettings.jsx";

View file

@ -0,0 +1,43 @@
import {
Accordion,
AccordionSummary,
AccordionDetails,
Checkbox,
Divider,
Grid,
Typography,
TextField,
Select,
} from "@material-ui/core";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore.js";
import API from "utils/API.js";
import { parseValue, replaceValue, setValue } from "utils/ChangeHelper.js";
import { useTranslation } from "react-i18next";
function SettingsComponent() {
const { t, i18n } = useTranslation();
const handleChange = () => (event) => {
i18n.changeLanguage(event.target.value);
};
return (
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{t("language")}</Typography>
</AccordionSummary>
<AccordionDetails>
<Grid item>
<Select native value={i18n.language} onChange={handleChange()}>
<option value={"en"}>English</option>
<option value={"es-ES"}>Español</option>
</Select>
</Grid>
</AccordionDetails>
</Accordion>
);
}
export default SettingsComponent;

View file

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

View file

@ -1 +1 @@
export { default } from "./Theme";
export { default } from "./Theme.jsx";

34
frontend/src/i18n.js Normal file
View file

@ -0,0 +1,34 @@
import i18n from "i18next";
import languageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import Backend from "i18next-http-backend";
i18n
.use(languageDetector)
.use(initReactI18next)
.use(Backend)
.init({
compatibilityJSON: "v4",
//lng: "en",
fallbackLng: "en",
detection: {
order: ["path", "cookie", "localStorage", "htmlTag"],
caches: ["localStorage", "cookie"], // cache user language on
},
debug: true,
//keySeparator: false, // we use content as keys
interpolation: {
escapeValue: true,
},
react: {
useSuspense: true,
},
supportedLngs: ["en", "es-ES"],
backend: {
loadPath: "/locales/{{lng}}/{{ns}}.json",
},
ns: ["common"],
defaultNS: "common",
});
export default i18n;

View file

@ -3,7 +3,7 @@ import "./index.css";
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import App from "./App.jsx";
ReactDOM.render(
<React.StrictMode>

View file

@ -1,7 +1,7 @@
import { useLocalStorage } from "react-use";
import HomeLoggedIn from "components/HomeLoggedIn";
import HomeLoggedOut from "components/HomeLoggedOut";
import HomeLoggedIn from "components/HomeLoggedIn/HomeLoggedIn.jsx";
import HomeLoggedOut from "components/HomeLoggedOut/HomeLoggedOut.jsx";
function Home() {
const [loggedIn] = useLocalStorage("loggedIn", false);

View file

@ -1 +1 @@
export { default } from "./Home";
export { default } from "./Home.jsx";

View file

@ -1,15 +1,15 @@
import { Grid, Link, Typography } from "@material-ui/core";
import ArrowBackIcon from "@material-ui/icons/ArrowBack";
import NetworkHeader from "components/NetworkHeader";
import NetworkManagement from "components/NetworkManagement";
import NetworkMembers from "components/NetworkMembers";
import NetworkRules from "components/NetworkRules";
import NetworkSettings from "components/NetworkSettings";
import ArrowBackIcon from "@material-ui/icons/ArrowBack.js";
import NetworkHeader from "components/NetworkHeader/NetworkHeader.jsx";
import NetworkManagement from "components/NetworkManagement/NetworkManagement.jsx";
import NetworkMembers from "components/NetworkMembers/NetworkMembers.jsx";
import NetworkRules from "components/NetworkRules/NetworkRules.jsx";
import NetworkSettings from "components/NetworkSettings/NetworkSettings.jsx";
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";
import API from "utils/API.js";
import useStyles from "./Network.styles.jsx";
function Network() {
const { nwid } = useParams();

View file

@ -1 +1 @@
export { default } from "./Network";
export { default } from "./Network.jsx";

View file

@ -1 +1 @@
export { default } from "./NotFound";
export { default } from "./NotFound.jsx";

View file

@ -0,0 +1,57 @@
import { Grid, Link, Typography } from "@material-ui/core";
import ArrowBackIcon from "@material-ui/icons/ArrowBack.js";
import SettingsComponent from "components/SettingsComponent/SettingsComponent.jsx";
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 "./Settings.styles.jsx";
import { useTranslation } from "react-i18next";
function Settings() {
const { t, i18n } = useTranslation();
const [loggedIn] = useLocalStorage("loggedIn", false);
const [network, setNetwork] = useState({});
const classes = useStyles();
const history = useHistory();
if (loggedIn) {
return (
<>
<div className={classes.breadcrumbs}>
<Link color="inherit" component={RouterLink} to="/" underline="none">
<ArrowBackIcon className={classes.backIcon}></ArrowBackIcon>
{t("settings")}
</Link>
</div>
<div className={classes.container}>
<SettingsComponent />
</div>
</>
);
} else {
return (
<Grid
container
spacing={0}
direction="column"
alignItems="center"
justify="center"
style={{
minHeight: "50vh",
}}
>
<Grid item xs={10}>
<Typography variant="h5">
You are not authorized. Please Log In
</Typography>
</Grid>
</Grid>
);
}
}
export default Settings;

View file

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