Added app menu

master
Tudor Stanciu 2023-03-03 08:48:10 +02:00
parent f6fbdd94f7
commit d1981e72bf
11 changed files with 393 additions and 87 deletions

22
package-lock.json generated
View File

@ -1327,6 +1327,11 @@
} }
} }
}, },
"@flare/js-utils": {
"version": "1.0.3",
"resolved": "https://lab.code-rove.com/public-node-registry/@flare/js-utils/-/js-utils-1.0.3.tgz",
"integrity": "sha512-VgXQHoQEVZ/71B6YQHQP8/Yd/w1smGD+kCCiNvJKZ1xMD3nkN9mjoHxIqbOJMZ2q5PZlV6gXYT7eVol8Wm+D0A=="
},
"@flare/tuitio-client": { "@flare/tuitio-client": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://lab.code-rove.com/public-node-registry/@flare/tuitio-client/-/tuitio-client-1.0.4.tgz", "resolved": "https://lab.code-rove.com/public-node-registry/@flare/tuitio-client/-/tuitio-client-1.0.4.tgz",
@ -1336,15 +1341,10 @@
"axios": "^1.3.2" "axios": "^1.3.2"
}, },
"dependencies": { "dependencies": {
"@flare/js-utils": {
"version": "1.0.3",
"resolved": "https://lab.code-rove.com/public-node-registry/@flare/js-utils/-/js-utils-1.0.3.tgz",
"integrity": "sha512-VgXQHoQEVZ/71B6YQHQP8/Yd/w1smGD+kCCiNvJKZ1xMD3nkN9mjoHxIqbOJMZ2q5PZlV6gXYT7eVol8Wm+D0A=="
},
"axios": { "axios": {
"version": "1.3.3", "version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.3.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-eYq77dYIFS77AQlhzEL937yUBSepBfPIe8FcgEDN35vMNZKMrs81pgnyrQpwfy4NF4b4XWX1Zgx7yX+25w8QJA==", "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"requires": { "requires": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
@ -1354,9 +1354,9 @@
} }
}, },
"@flare/tuitio-client-react": { "@flare/tuitio-client-react": {
"version": "1.0.0", "version": "1.0.1",
"resolved": "https://lab.code-rove.com/public-node-registry/@flare/tuitio-client-react/-/tuitio-client-react-1.0.0.tgz", "resolved": "https://lab.code-rove.com/public-node-registry/@flare/tuitio-client-react/-/tuitio-client-react-1.0.1.tgz",
"integrity": "sha512-iIsyteZaCLXQCAFAnifYq6Stw7Jg72id7Ky/b3hB8ZGaNkrTDKDsmLTW7a2bHWljcFbCbYuRXrmHgpt8YJ6Hzg==", "integrity": "sha512-WA+1UzfBgZ4gGdcT0AT6lQ9zOQ3mxc5UxQvxoM1SBR5i5tbIuQQCY6M8dudm1l02fNfwNQqJIcrtuOTTXZqMOA==",
"requires": { "requires": {
"@flare/tuitio-client": "^1.0.4" "@flare/tuitio-client": "^1.0.4"
} }

View File

@ -13,7 +13,7 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@flare/tuitio-client-react": "^1.0.0", "@flare/tuitio-client-react": "^1.0.1",
"@material-ui/core": "^4.11.2", "@material-ui/core": "^4.11.2",
"@material-ui/icons": "^4.11.2", "@material-ui/icons": "^4.11.2",
"@testing-library/jest-dom": "^5.11.6", "@testing-library/jest-dom": "^5.11.6",

View File

@ -1,46 +1,62 @@
import React, { useReducer, useMemo } from "react"; import React from "react";
import Main from "./layout/Main"; import Layout from "./Layout";
import { initialState } from "../state/initialState"; import { BrowserRouter, Switch, Redirect, Route } from "react-router-dom";
import { import { useTuitioToken } from "@flare/tuitio-client-react";
reducer, import LoginContainer from "../features/login/components/LoginContainer";
dispatchActions as reducerDispatchActions
} from "../state/reducer";
import {
ApplicationStateContext,
ApplicationDispatchContext
} from "../state/ApplicationContexts";
import { ToastContainer, Slide } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
const App = () => { const PrivateRoute = ({ component, ...rest }) => {
const [state, dispatch] = useReducer(reducer, initialState); const { valid } = useTuitioToken();
const dispatchActions = useMemo(
() => reducerDispatchActions(dispatch),
[dispatch]
);
return ( return (
<> <Route
<ApplicationStateContext.Provider value={state}> {...rest}
<ApplicationDispatchContext.Provider value={dispatchActions}> render={props =>
<Main /> valid ? (
</ApplicationDispatchContext.Provider> React.createElement(component, props)
</ApplicationStateContext.Provider> ) : (
<ToastContainer <Redirect
position="bottom-right" to={{
transition={Slide} pathname: "/login",
autoClose={3000} state: {
hideProgressBar={false} from: props.location
newestOnTop={false} }
closeOnClick }}
rtl={false} />
pauseOnFocusLoss )
draggable }
pauseOnHover />
limit={5}
/>
</>
); );
}; };
export default App; const PublicRoute = ({ component, ...rest }) => {
const { valid } = useTuitioToken();
return (
<Route
{...rest}
render={props =>
valid ? (
<Redirect
to={{
pathname: "/"
}}
/>
) : (
React.createElement(component, props)
)
}
/>
);
};
const AppWrapper = () => {
return (
<BrowserRouter basename={process.env.PUBLIC_URL || ""}>
<Switch>
<PublicRoute path="/login" component={LoginContainer} />
<PrivateRoute path="/" component={Layout} />
</Switch>
</BrowserRouter>
);
};
export default AppWrapper;

253
src/components/Layout.js Normal file
View File

@ -0,0 +1,253 @@
import React from "react";
import clsx from "clsx";
import { makeStyles, useTheme } from "@material-ui/core/styles";
import Drawer from "@material-ui/core/Drawer";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import List from "@material-ui/core/List";
import Typography from "@material-ui/core/Typography";
import Divider from "@material-ui/core/Divider";
import IconButton from "@material-ui/core/IconButton";
import MenuIcon from "@material-ui/icons/Menu";
import ChevronLeftIcon from "@material-ui/icons/ChevronLeft";
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import InboxIcon from "@material-ui/icons/MoveToInbox";
import MailIcon from "@material-ui/icons/Mail";
import DnsIcon from "@material-ui/icons/Dns";
import MenuItem from "@material-ui/core/MenuItem";
import Menu from "@material-ui/core/Menu";
import AccountCircle from "@material-ui/icons/AccountCircle";
import SettingsIcon from "@material-ui/icons/Settings";
import Main from "./layout/Main";
import { useHistory } from "react-router-dom";
import { useTuitioClient } from "@flare/tuitio-client-react";
const drawerWidth = 240;
const useStyles = makeStyles(theme => ({
root: {
display: "flex"
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(["width", "margin"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
appBarShift: {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(["width", "margin"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen
})
},
menuButton: {
marginRight: 36
},
hide: {
display: "none"
},
drawer: {
width: drawerWidth,
flexShrink: 0,
whiteSpace: "nowrap"
},
drawerOpen: {
width: drawerWidth,
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen
})
},
drawerClose: {
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
overflowX: "hidden",
width: theme.spacing(7) + 1,
[theme.breakpoints.up("sm")]: {
width: theme.spacing(9) + 1
}
},
toolbar: {
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar
},
title: {
flexGrow: 1
},
content: {
flexGrow: 1,
padding: theme.spacing(2)
}
}));
export default function MiniDrawer() {
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const history = useHistory();
const { logout } = useTuitioClient();
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
const [userAnchorEl, setUserAnchorEl] = React.useState(null);
const openUser = Boolean(userAnchorEl);
const handleMenu = event => {
setUserAnchorEl(event.currentTarget);
};
const handleClose = () => {
setUserAnchorEl(null);
};
return (
<div className={classes.root}>
<AppBar
position="fixed"
className={clsx(classes.appBar, {
[classes.appBarShift]: open
})}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
className={clsx(classes.menuButton, {
[classes.hide]: open
})}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap className={classes.title}>
Network resurrector
</Typography>
<div>
<IconButton
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
onClick={handleMenu}
color="inherit"
>
<AccountCircle />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={userAnchorEl}
anchorOrigin={{
vertical: "top",
horizontal: "right"
}}
keepMounted
transformOrigin={{
vertical: "top",
horizontal: "right"
}}
open={openUser}
onClose={handleClose}
>
<MenuItem
onClick={() => {
history.push("/user-profile");
handleClose();
}}
>
Profile
</MenuItem>
<MenuItem onClick={() => logout()}>Logout</MenuItem>
</Menu>
</div>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
className={clsx(classes.drawer, {
[classes.drawerOpen]: open,
[classes.drawerClose]: !open
})}
classes={{
paper: clsx({
[classes.drawerOpen]: open,
[classes.drawerClose]: !open
})
}}
>
<div className={classes.toolbar}>
<IconButton onClick={handleDrawerClose}>
{theme.direction === "rtl" ? (
<ChevronRightIcon />
) : (
<ChevronLeftIcon />
)}
</IconButton>
</div>
<Divider />
<List>
{["Inbox", "Starred", "Send email", "Drafts"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
<ListItem
button
key="machines"
onClick={() => history.push("/machines")}
>
<ListItemIcon>
<DnsIcon />
</ListItemIcon>
<ListItemText primary={"Machines"} />
</ListItem>
<ListItem
button
key="settings"
onClick={() => history.push("/settings")}
>
<ListItemIcon>
<SettingsIcon />
</ListItemIcon>
<ListItemText primary={"Settings"} />
</ListItem>
{["All mail", "Trash", "Spam"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
<main className={classes.content}>
<div className={classes.toolbar} />
<Main />
</main>
</div>
);
}

View File

@ -1,36 +1,13 @@
import React from "react"; import React from "react";
import ApplicationStepper from "./stepper/ApplicationStepper";
import Switcher from "./Switcher"; import Switcher from "./Switcher";
import { makeStyles } from "@material-ui/core/styles";
import { useTuitioToken } from "@flare/tuitio-client-react"; import { useTuitioToken } from "@flare/tuitio-client-react";
import LoginContainer from "../../features/login/components/LoginContainer"; import LoginContainer from "../../features/login/components/LoginContainer";
const useStyles = makeStyles(() => ({
app: {
backgroundColor: "#282c34",
minHeight: "100vh",
display: "flex",
flexDirection: "column"
}
}));
const Main = () => { const Main = () => {
const classes = useStyles();
const { validate: validateToken } = useTuitioToken(); const { validate: validateToken } = useTuitioToken();
const tokenIsValid = validateToken(); const tokenIsValid = validateToken();
return ( return <>{tokenIsValid ? <Switcher /> : <LoginContainer />}</>;
<div className={classes.app}>
{tokenIsValid ? (
<>
<ApplicationStepper />
<Switcher />
</>
) : (
<LoginContainer />
)}
</div>
);
}; };
export default Main; export default Main;

View File

@ -8,8 +8,8 @@ import SettingsContainer from "../../features/settings/components/SettingsContai
const Switcher = () => { const Switcher = () => {
return ( return (
<Switch> <Switch>
<Route exact path="/" component={LoginContainer} /> <Route exact path="/user-profile" component={LoginContainer} />
<Route exact path="/network" component={NetworkContainer} /> <Route exact path="/machines" component={NetworkContainer} />
<Route exact path="/settings" component={SettingsContainer} /> <Route exact path="/settings" component={SettingsContainer} />
<Route component={PageNotFound} /> <Route component={PageNotFound} />
</Switch> </Switch>

View File

@ -11,7 +11,7 @@ const steps = [
{ {
id: 1, id: 1,
title: "Steps.Network", title: "Steps.Network",
route: "/network", route: "/machines",
disabled: false, disabled: false,
icon: <Router /> icon: <Router />
}, },

View File

@ -1,5 +1,6 @@
body { body {
margin: 0; margin: 0;
background-color: #282c34;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif; sans-serif;

View File

@ -2,17 +2,22 @@ import React, { Suspense } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import "./index.css"; import "./index.css";
import "./utils/i18n"; import "./utils/i18n";
import CssBaseline from "@material-ui/core/CssBaseline";
import App from "./components/App"; import App from "./components/App";
import { BrowserRouter as Router } from "react-router-dom";
import { TuitioProvider } from "@flare/tuitio-client-react"; import { TuitioProvider } from "@flare/tuitio-client-react";
import ApplicationStateProvider from "./providers/ApplicationStateProvider";
import ToastProvider from "./providers/ToastProvider";
ReactDOM.render( ReactDOM.render(
<Router basename={process.env.PUBLIC_URL || ""}> <TuitioProvider tuitioUrl={process.env.REACT_APP_TUITIO_URL}>
<Suspense fallback={<div>Loading...</div>}> <ApplicationStateProvider>
<TuitioProvider tuitioUrl={process.env.REACT_APP_TUITIO_URL}> <CssBaseline />
<App /> <Suspense fallback={<div>Loading...</div>}>
</TuitioProvider> <ToastProvider>
</Suspense> <App />
</Router>, </ToastProvider>
</Suspense>
</ApplicationStateProvider>
</TuitioProvider>,
document.getElementById("root") document.getElementById("root")
); );

View File

@ -0,0 +1,28 @@
import React, { useReducer, useMemo } from "react";
import {
ApplicationStateContext,
ApplicationDispatchContext
} from "../state/ApplicationContexts";
import {
reducer,
dispatchActions as reducerDispatchActions
} from "../state/reducer";
import { initialState } from "../state/initialState";
const ApplicationStateProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const dispatchActions = useMemo(
() => reducerDispatchActions(dispatch),
[dispatch]
);
return (
<ApplicationStateContext.Provider value={state}>
<ApplicationDispatchContext.Provider value={dispatchActions}>
{children}
</ApplicationDispatchContext.Provider>
</ApplicationStateContext.Provider>
);
};
export default ApplicationStateProvider;

View File

@ -0,0 +1,26 @@
import React from "react";
import { ToastContainer, Slide } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
const ToastProvider = ({ children }) => {
return (
<>
{children}
<ToastContainer
position="bottom-right"
transition={Slide}
autoClose={3000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
limit={5}
/>
</>
);
};
export default ToastProvider;